Repository: icsharpcode/ILSpy Branch: master Commit: 5eebf1408a47 Files: 1740 Total size: 10.6 MB Directory structure: gitextract_h8aaa6eu/ ├── .editorconfig ├── .git-blame-ignore-revs ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── wrong_decompilation.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── build-frontends.yml │ ├── build-ilspy.yml │ ├── codeql-analysis.yml │ ├── generate-bom.yml │ ├── lock.yml │ └── scorecard.yml ├── .gitignore ├── .gitmodules ├── .tgitconfig ├── BuildTools/ │ ├── ILSpy.AddIn.VS2022.vsix.filelist │ ├── ILSpy.AddIn.vsix.filelist │ ├── ILSpy.msi.filelist │ ├── bom-classify-encodings.ps1 │ ├── bom-strip.ps1 │ ├── create-filelists.ps1 │ ├── format.bat │ ├── ghactions-install.ps1 │ ├── pre-commit │ ├── sort-resx.ps1 │ └── update-assemblyinfo.ps1 ├── Directory.Build.props ├── Directory.Packages.props ├── ICSharpCode.BamlDecompiler/ │ ├── Baml/ │ │ ├── BamlContext.cs │ │ ├── BamlDocument.cs │ │ ├── BamlNode.cs │ │ ├── BamlReader.cs │ │ ├── BamlRecords.cs │ │ ├── BamlWriter.cs │ │ ├── KnownMembers.cs │ │ ├── KnownThings.cs │ │ ├── KnownThings.g.cs │ │ ├── KnownThings.gen.cs │ │ └── KnownTypes.cs │ ├── BamlConnectionId.cs │ ├── BamlDecompilationResult.cs │ ├── BamlDecompilerSettings.cs │ ├── BamlDecompilerTypeSystem.cs │ ├── BamlElement.cs │ ├── Handlers/ │ │ ├── Blocks/ │ │ │ ├── ConstructorParametersHandler.cs │ │ │ ├── DocumentHandler.cs │ │ │ ├── ElementHandler.cs │ │ │ ├── KeyElementStartHandler.cs │ │ │ ├── PropertyArrayHandler.cs │ │ │ ├── PropertyComplexHandler.cs │ │ │ ├── PropertyDictionaryHandler.cs │ │ │ └── PropertyListHandler.cs │ │ └── Records/ │ │ ├── AssemblyInfoHandler.cs │ │ ├── AttributeInfoHandler.cs │ │ ├── ConnectionIdHandler.cs │ │ ├── ConstructorParameterTypeHandler.cs │ │ ├── ContentPropertyHandler.cs │ │ ├── DefAttributeHandler.cs │ │ ├── DefAttributeKeyStringHandler.cs │ │ ├── DefAttributeKeyTypeHandler.cs │ │ ├── DeferableContentStartHandler.cs │ │ ├── LineNumberAndPositionHandler.cs │ │ ├── LinePositionHandler.cs │ │ ├── LiteralContentHandler.cs │ │ ├── OptimizedStaticResourceHandler.cs │ │ ├── PIMappingHandler.cs │ │ ├── PresentationOptionsAttributeHandler.cs │ │ ├── PropertyCustomHandler.cs │ │ ├── PropertyHandler.cs │ │ ├── PropertyTypeReferenceHandler.cs │ │ ├── PropertyWithConverterHandler.cs │ │ ├── PropertyWithExtensionHandler.cs │ │ ├── PropertyWithStaticResourceIdHandler.cs │ │ ├── StaticResourceIdHandler.cs │ │ ├── StaticResourceStartHandler.cs │ │ ├── TextHandler.cs │ │ ├── TextWithConverterHandler.cs │ │ ├── TypeInfoHandler.cs │ │ └── XmlnsPropertyHandler.cs │ ├── ICSharpCode.BamlDecompiler.csproj │ ├── IHandlers.cs │ ├── IRewritePass.cs │ ├── PackageReadme.md │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Rewrite/ │ │ ├── AttributeRewritePass.cs │ │ ├── ConnectionIdRewritePass.cs │ │ ├── DocumentRewritePass.cs │ │ ├── MarkupExtensionRewritePass.cs │ │ └── XClassRewritePass.cs │ ├── Xaml/ │ │ ├── NamespaceMap.cs │ │ ├── XamlExtension.cs │ │ ├── XamlPathDeserializer.cs │ │ ├── XamlProperty.cs │ │ ├── XamlResourceKey.cs │ │ ├── XamlType.cs │ │ └── XamlUtils.cs │ ├── XamlContext.cs │ ├── XamlDecompiler.cs │ ├── XmlnsDictionary.cs │ └── packages.lock.json ├── ICSharpCode.Decompiler/ │ ├── CSharp/ │ │ ├── Annotations.cs │ │ ├── CSharpDecompiler.cs │ │ ├── CSharpLanguageVersion.cs │ │ ├── CallBuilder.cs │ │ ├── ExpressionBuilder.cs │ │ ├── OutputVisitor/ │ │ │ ├── CSharpAmbience.cs │ │ │ ├── CSharpFormattingOptions.cs │ │ │ ├── CSharpOutputVisitor.cs │ │ │ ├── FormattingOptionsFactory.cs │ │ │ ├── GenericGrammarAmbiguityVisitor.cs │ │ │ ├── ITokenWriter.cs │ │ │ ├── InsertMissingTokensDecorator.cs │ │ │ ├── InsertParenthesesVisitor.cs │ │ │ ├── InsertRequiredSpacesDecorator.cs │ │ │ ├── InsertSpecialsDecorator.cs │ │ │ └── TextWriterTokenWriter.cs │ │ ├── ProjectDecompiler/ │ │ │ ├── IProjectFileWriter.cs │ │ │ ├── IProjectInfoProvider.cs │ │ │ ├── ProjectFileWriterDefault.cs │ │ │ ├── ProjectFileWriterSdkStyle.cs │ │ │ ├── TargetFramework.cs │ │ │ ├── TargetServices.cs │ │ │ └── WholeProjectDecompiler.cs │ │ ├── RecordDecompiler.cs │ │ ├── RequiredNamespaceCollector.cs │ │ ├── Resolver/ │ │ │ ├── AliasNamespaceResolveResult.cs │ │ │ ├── AliasTypeResolveResult.cs │ │ │ ├── AwaitResolveResult.cs │ │ │ ├── CSharpConversions.cs │ │ │ ├── CSharpInvocationResolveResult.cs │ │ │ ├── CSharpOperators.cs │ │ │ ├── CSharpResolver.cs │ │ │ ├── DynamicInvocationResolveResult.cs │ │ │ ├── DynamicMemberResolveResult.cs │ │ │ ├── LambdaResolveResult.cs │ │ │ ├── Log.cs │ │ │ ├── MemberLookup.cs │ │ │ ├── MethodGroupResolveResult.cs │ │ │ ├── NameLookupMode.cs │ │ │ ├── OverloadResolution.cs │ │ │ ├── OverloadResolutionErrors.cs │ │ │ └── TypeInference.cs │ │ ├── SequencePointBuilder.cs │ │ ├── StatementBuilder.cs │ │ ├── Syntax/ │ │ │ ├── AstNode.cs │ │ │ ├── AstNodeCollection.cs │ │ │ ├── AstType.cs │ │ │ ├── CSharpModifierToken.cs │ │ │ ├── CSharpTokenNode.cs │ │ │ ├── ComposedType.cs │ │ │ ├── DepthFirstAstVisitor.cs │ │ │ ├── DocumentationReference.cs │ │ │ ├── Expressions/ │ │ │ │ ├── AnonymousMethodExpression.cs │ │ │ │ ├── AnonymousTypeCreateExpression.cs │ │ │ │ ├── ArrayCreateExpression.cs │ │ │ │ ├── ArrayInitializerExpression.cs │ │ │ │ ├── AsExpression.cs │ │ │ │ ├── AssignmentExpression.cs │ │ │ │ ├── BaseReferenceExpression.cs │ │ │ │ ├── BinaryOperatorExpression.cs │ │ │ │ ├── CastExpression.cs │ │ │ │ ├── CheckedExpression.cs │ │ │ │ ├── ConditionalExpression.cs │ │ │ │ ├── DeclarationExpression.cs │ │ │ │ ├── DefaultValueExpression.cs │ │ │ │ ├── DirectionExpression.cs │ │ │ │ ├── ErrorExpression.cs │ │ │ │ ├── Expression.cs │ │ │ │ ├── IdentifierExpression.cs │ │ │ │ ├── IndexerExpression.cs │ │ │ │ ├── InterpolatedStringExpression.cs │ │ │ │ ├── InvocationExpression.cs │ │ │ │ ├── IsExpression.cs │ │ │ │ ├── LambdaExpression.cs │ │ │ │ ├── MemberReferenceExpression.cs │ │ │ │ ├── NamedArgumentExpression.cs │ │ │ │ ├── NamedExpression.cs │ │ │ │ ├── NullReferenceExpression.cs │ │ │ │ ├── ObjectCreateExpression.cs │ │ │ │ ├── OutVarDeclarationExpression.cs │ │ │ │ ├── ParenthesizedExpression.cs │ │ │ │ ├── PointerReferenceExpression.cs │ │ │ │ ├── PrimitiveExpression.cs │ │ │ │ ├── QueryExpression.cs │ │ │ │ ├── RecursivePatternExpression.cs │ │ │ │ ├── SizeOfExpression.cs │ │ │ │ ├── StackAllocExpression.cs │ │ │ │ ├── SwitchExpression.cs │ │ │ │ ├── ThisReferenceExpression.cs │ │ │ │ ├── ThrowExpression.cs │ │ │ │ ├── TupleExpression.cs │ │ │ │ ├── TypeOfExpression.cs │ │ │ │ ├── TypeReferenceExpression.cs │ │ │ │ ├── UnaryOperatorExpression.cs │ │ │ │ ├── UncheckedExpression.cs │ │ │ │ ├── UndocumentedExpression.cs │ │ │ │ └── WithInitializerExpression.cs │ │ │ ├── FunctionPointerAstType.cs │ │ │ ├── GeneralScope/ │ │ │ │ ├── Attribute.cs │ │ │ │ ├── AttributeSection.cs │ │ │ │ ├── Comment.cs │ │ │ │ ├── Constraint.cs │ │ │ │ ├── DelegateDeclaration.cs │ │ │ │ ├── ExternAliasDeclaration.cs │ │ │ │ ├── NamespaceDeclaration.cs │ │ │ │ ├── PreProcessorDirective.cs │ │ │ │ ├── TypeDeclaration.cs │ │ │ │ ├── TypeParameterDeclaration.cs │ │ │ │ ├── UsingAliasDeclaration.cs │ │ │ │ └── UsingDeclaration.cs │ │ │ ├── IAnnotatable.cs │ │ │ ├── IAstVisitor.cs │ │ │ ├── Identifier.cs │ │ │ ├── IdentifierExpressionBackreference.cs │ │ │ ├── InvocationAstType.cs │ │ │ ├── MemberType.cs │ │ │ ├── Modifiers.cs │ │ │ ├── NodeType.cs │ │ │ ├── PatternMatching/ │ │ │ │ ├── AnyNode.cs │ │ │ │ ├── AnyNodeOrNull.cs │ │ │ │ ├── Backreference.cs │ │ │ │ ├── BacktrackingInfo.cs │ │ │ │ ├── Choice.cs │ │ │ │ ├── INode.cs │ │ │ │ ├── Match.cs │ │ │ │ ├── NamedNode.cs │ │ │ │ ├── OptionalNode.cs │ │ │ │ ├── Pattern.cs │ │ │ │ └── Repeat.cs │ │ │ ├── PrimitiveType.cs │ │ │ ├── Role.cs │ │ │ ├── Roles.cs │ │ │ ├── SimpleType.cs │ │ │ ├── Statements/ │ │ │ │ ├── BlockStatement.cs │ │ │ │ ├── BreakStatement.cs │ │ │ │ ├── CheckedStatement.cs │ │ │ │ ├── ContinueStatement.cs │ │ │ │ ├── DoWhileStatement.cs │ │ │ │ ├── EmptyStatement.cs │ │ │ │ ├── ExpressionStatement.cs │ │ │ │ ├── FixedStatement.cs │ │ │ │ ├── ForStatement.cs │ │ │ │ ├── ForeachStatement.cs │ │ │ │ ├── GotoStatement.cs │ │ │ │ ├── IfElseStatement.cs │ │ │ │ ├── LabelStatement.cs │ │ │ │ ├── LocalFunctionDeclarationStatement.cs │ │ │ │ ├── LockStatement.cs │ │ │ │ ├── ReturnStatement.cs │ │ │ │ ├── Statement.cs │ │ │ │ ├── SwitchStatement.cs │ │ │ │ ├── ThrowStatement.cs │ │ │ │ ├── TryCatchStatement.cs │ │ │ │ ├── UncheckedStatement.cs │ │ │ │ ├── UnsafeStatement.cs │ │ │ │ ├── UsingStatement.cs │ │ │ │ ├── VariableDeclarationStatement.cs │ │ │ │ ├── WhileStatement.cs │ │ │ │ ├── YieldBreakStatement.cs │ │ │ │ └── YieldReturnStatement.cs │ │ │ ├── SyntaxExtensions.cs │ │ │ ├── SyntaxTree.cs │ │ │ ├── TextLocation.cs │ │ │ ├── TokenRole.cs │ │ │ ├── TupleAstType.cs │ │ │ ├── TypeMembers/ │ │ │ │ ├── Accessor.cs │ │ │ │ ├── ConstructorDeclaration.cs │ │ │ │ ├── DestructorDeclaration.cs │ │ │ │ ├── EntityDeclaration.cs │ │ │ │ ├── EnumMemberDeclaration.cs │ │ │ │ ├── EventDeclaration.cs │ │ │ │ ├── ExtensionDeclaration.cs │ │ │ │ ├── FieldDeclaration.cs │ │ │ │ ├── FixedFieldDeclaration.cs │ │ │ │ ├── FixedVariableInitializer.cs │ │ │ │ ├── IndexerDeclaration.cs │ │ │ │ ├── MethodDeclaration.cs │ │ │ │ ├── OperatorDeclaration.cs │ │ │ │ ├── ParameterDeclaration.cs │ │ │ │ ├── PropertyDeclaration.cs │ │ │ │ └── VariableInitializer.cs │ │ │ ├── TypeSystemAstBuilder.cs │ │ │ └── VariableDesignation.cs │ │ ├── Transforms/ │ │ │ ├── AddCheckedBlocks.cs │ │ │ ├── AddXmlDocumentationTransform.cs │ │ │ ├── CombineQueryExpressions.cs │ │ │ ├── ContextTrackingVisitor.cs │ │ │ ├── CustomPatterns.cs │ │ │ ├── DeclareVariables.cs │ │ │ ├── EscapeInvalidIdentifiers.cs │ │ │ ├── FixNameCollisions.cs │ │ │ ├── FlattenSwitchBlocks.cs │ │ │ ├── IAstTransform.cs │ │ │ ├── IntroduceExtensionMethods.cs │ │ │ ├── IntroduceQueryExpressions.cs │ │ │ ├── IntroduceUnsafeModifier.cs │ │ │ ├── IntroduceUsingDeclarations.cs │ │ │ ├── NormalizeBlockStatements.cs │ │ │ ├── PatternStatementTransform.cs │ │ │ ├── PrettifyAssignments.cs │ │ │ ├── RemoveCLSCompliantAttribute.cs │ │ │ ├── ReplaceMethodCallsWithOperators.cs │ │ │ ├── TransformContext.cs │ │ │ └── TransformFieldAndConstructorInitializers.cs │ │ ├── TranslatedExpression.cs │ │ ├── TranslatedStatement.cs │ │ ├── TranslationContext.cs │ │ └── TypeSystem/ │ │ ├── CSharpTypeResolveContext.cs │ │ └── UsingScope.cs │ ├── DebugInfo/ │ │ ├── AsyncDebugInfo.cs │ │ ├── DebugInfoGenerator.cs │ │ ├── IDebugInfoProvider.cs │ │ ├── ImportScopeInfo.cs │ │ ├── KnownGuids.cs │ │ ├── PortablePdbWriter.cs │ │ └── SequencePoint.cs │ ├── DecompilationProgress.cs │ ├── DecompileRun.cs │ ├── DecompilerException.cs │ ├── DecompilerSettings.cs │ ├── Disassembler/ │ │ ├── DisassemblerHelpers.cs │ │ ├── DisassemblerSignatureTypeProvider.cs │ │ ├── IEntityProcessor.cs │ │ ├── ILParser.cs │ │ ├── ILStructure.cs │ │ ├── MethodBodyDisassembler.cs │ │ ├── OpCodeInfo.cs │ │ ├── ReflectionDisassembler.cs │ │ └── SortByNameProcessor.cs │ ├── Documentation/ │ │ ├── GetPotentiallyNestedClassTypeReference.cs │ │ ├── IdStringMemberReference.cs │ │ ├── IdStringProvider.cs │ │ ├── XmlDocLoader.cs │ │ ├── XmlDocumentationElement.cs │ │ └── XmlDocumentationProvider.cs │ ├── FlowAnalysis/ │ │ ├── ControlFlowNode.cs │ │ ├── DataFlowVisitor.cs │ │ ├── DefiniteAssignmentVisitor.cs │ │ ├── Dominance.cs │ │ └── ReachingDefinitionsVisitor.cs │ ├── Humanizer/ │ │ ├── LICENSE │ │ ├── StringHumanizeExtensions.cs │ │ ├── Vocabularies.cs │ │ └── Vocabulary.cs │ ├── ICSharpCode.Decompiler.csproj │ ├── ICSharpCode.Decompiler.snk │ ├── IL/ │ │ ├── BlockBuilder.cs │ │ ├── ControlFlow/ │ │ │ ├── AsyncAwaitDecompiler.cs │ │ │ ├── AwaitInCatchTransform.cs │ │ │ ├── AwaitInFinallyTransform.cs │ │ │ ├── ConditionDetection.cs │ │ │ ├── ControlFlowGraph.cs │ │ │ ├── ControlFlowSimplification.cs │ │ │ ├── DetectPinnedRegions.cs │ │ │ ├── ExitPoints.cs │ │ │ ├── LoopDetection.cs │ │ │ ├── RemoveRedundantReturn.cs │ │ │ ├── StateRangeAnalysis.cs │ │ │ ├── SwitchAnalysis.cs │ │ │ ├── SwitchDetection.cs │ │ │ ├── SymbolicExecution.cs │ │ │ └── YieldReturnDecompiler.cs │ │ ├── ILAmbience.cs │ │ ├── ILAstWritingOptions.cs │ │ ├── ILInstructionExtensions.cs │ │ ├── ILReader.cs │ │ ├── ILTypeExtensions.cs │ │ ├── ILVariable.cs │ │ ├── InstructionFlags.cs │ │ ├── InstructionOutputExtensions.cs │ │ ├── Instructions/ │ │ │ ├── Await.cs │ │ │ ├── BinaryNumericInstruction.cs │ │ │ ├── Block.cs │ │ │ ├── BlockContainer.cs │ │ │ ├── Branch.cs │ │ │ ├── CallIndirect.cs │ │ │ ├── CallInstruction.cs │ │ │ ├── Comp.cs │ │ │ ├── CompoundAssignmentInstruction.cs │ │ │ ├── Conv.cs │ │ │ ├── DeconstructInstruction.cs │ │ │ ├── DeconstructResultInstruction.cs │ │ │ ├── DefaultValue.cs │ │ │ ├── DynamicInstructions.cs │ │ │ ├── ExpressionTreeCast.cs │ │ │ ├── ILFunction.cs │ │ │ ├── ILInstruction.cs │ │ │ ├── ILVariableCollection.cs │ │ │ ├── IfInstruction.cs │ │ │ ├── InstructionCollection.cs │ │ │ ├── IsInst.cs │ │ │ ├── LdFlda.cs │ │ │ ├── LdLen.cs │ │ │ ├── Leave.cs │ │ │ ├── LockInstruction.cs │ │ │ ├── LogicInstructions.cs │ │ │ ├── MatchInstruction.cs │ │ │ ├── MemoryInstructions.cs │ │ │ ├── NullCoalescingInstruction.cs │ │ │ ├── NullableInstructions.cs │ │ │ ├── PatternMatching.cs │ │ │ ├── SimpleInstruction.cs │ │ │ ├── StLoc.cs │ │ │ ├── StringToInt.cs │ │ │ ├── SwitchInstruction.cs │ │ │ ├── TryInstruction.cs │ │ │ ├── UnaryInstruction.cs │ │ │ └── UsingInstruction.cs │ │ ├── Instructions.cs │ │ ├── Instructions.tt │ │ ├── Patterns/ │ │ │ ├── AnyNode.cs │ │ │ ├── ListMatch.cs │ │ │ └── Match.cs │ │ ├── PointerArithmeticOffset.cs │ │ ├── PrimitiveType.cs │ │ ├── SemanticHelper.cs │ │ ├── SlotInfo.cs │ │ ├── StackType.cs │ │ └── Transforms/ │ │ ├── AssignVariableNames.cs │ │ ├── BlockTransform.cs │ │ ├── CachedDelegateInitialization.cs │ │ ├── CombineExitsTransform.cs │ │ ├── CopyPropagation.cs │ │ ├── DeconstructionTransform.cs │ │ ├── DelegateConstruction.cs │ │ ├── DetectCatchWhenConditionBlocks.cs │ │ ├── DynamicCallSiteTransform.cs │ │ ├── DynamicIsEventAssignmentTransform.cs │ │ ├── EarlyExpressionTransforms.cs │ │ ├── ExpressionTransforms.cs │ │ ├── FixRemainingIncrements.cs │ │ ├── HighLevelLoopTransform.cs │ │ ├── IILTransform.cs │ │ ├── ILExtraction.cs │ │ ├── ILInlining.cs │ │ ├── IndexRangeTransform.cs │ │ ├── InlineArrayTransform.cs │ │ ├── InlineReturnTransform.cs │ │ ├── InterpolatedStringTransform.cs │ │ ├── IntroduceDynamicTypeOnLocals.cs │ │ ├── IntroduceNativeIntTypeOnLocals.cs │ │ ├── IntroduceRefReadOnlyModifierOnLocals.cs │ │ ├── LdLocaDupInitObjTransform.cs │ │ ├── LocalFunctionDecompiler.cs │ │ ├── LockTransform.cs │ │ ├── NamedArgumentTransform.cs │ │ ├── NullCoalescingTransform.cs │ │ ├── NullPropagationTransform.cs │ │ ├── NullableLiftingTransform.cs │ │ ├── PatternMatchingTransform.cs │ │ ├── ProxyCallReplacer.cs │ │ ├── ReduceNestingTransform.cs │ │ ├── RemoveDeadVariableInit.cs │ │ ├── RemoveInfeasiblePathTransform.cs │ │ ├── RemoveUnconstrainedGenericReferenceTypeCheck.cs │ │ ├── SplitVariables.cs │ │ ├── StatementTransform.cs │ │ ├── Stepper.cs │ │ ├── SwitchOnNullableTransform.cs │ │ ├── SwitchOnStringTransform.cs │ │ ├── TransformArrayInitializers.cs │ │ ├── TransformAssignment.cs │ │ ├── TransformCollectionAndObjectInitializers.cs │ │ ├── TransformDisplayClassUsage.cs │ │ ├── TransformExpressionTrees.cs │ │ ├── TupleTransform.cs │ │ ├── UserDefinedLogicTransform.cs │ │ └── UsingTransform.cs │ ├── Instrumentation/ │ │ └── DecompilerEventSource.cs │ ├── Metadata/ │ │ ├── AssemblyReferences.cs │ │ ├── CodeMappingInfo.cs │ │ ├── CustomAttributeDecoder.cs │ │ ├── DotNetCorePathFinder.cs │ │ ├── DotNetCorePathFinderExtensions.cs │ │ ├── EnumUnderlyingTypeResolveException.cs │ │ ├── ExportedTypeMetadata.cs │ │ ├── FindTypeDecoder.cs │ │ ├── FullTypeNameSignatureDecoder.cs │ │ ├── ILOpCodes.cs │ │ ├── ILOpCodes.tt │ │ ├── LightJson/ │ │ │ ├── JsonArray.cs │ │ │ ├── JsonObject.cs │ │ │ ├── JsonValue.cs │ │ │ ├── JsonValueType.cs │ │ │ └── Serialization/ │ │ │ ├── JsonParseException.cs │ │ │ ├── JsonReader.cs │ │ │ ├── TextPosition.cs │ │ │ └── TextScanner.cs │ │ ├── MemberReferenceMetadata.cs │ │ ├── MetadataExtensions.cs │ │ ├── MetadataFile.cs │ │ ├── MetadataGenericContext.cs │ │ ├── MetadataTokenHelpers.cs │ │ ├── MethodSemanticsLookup.cs │ │ ├── ModuleReferenceMetadata.cs │ │ ├── OperandType.cs │ │ ├── PEFile.cs │ │ ├── PropertyAndEventBackingFieldLookup.cs │ │ ├── ReferenceLoadInfo.cs │ │ ├── Resource.cs │ │ ├── SignatureBlobComparer.cs │ │ ├── TypeReferenceMetadata.cs │ │ ├── UniversalAssemblyResolver.cs │ │ ├── UnresolvedAssemblyNameReference.cs │ │ └── WebCilFile.cs │ ├── NRExtensions.cs │ ├── NRTAttributes.cs │ ├── Output/ │ │ ├── IAmbience.cs │ │ ├── ITextOutput.cs │ │ ├── PlainTextOutput.cs │ │ ├── TextOutputWriter.cs │ │ └── TextTokenWriter.cs │ ├── PackageReadme.md │ ├── PartialTypeInfo.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── DecompilerVersionInfo.template.cs │ ├── SRMExtensions.cs │ ├── SRMHacks.cs │ ├── Semantics/ │ │ ├── AmbiguousResolveResult.cs │ │ ├── ArrayAccessResolveResult.cs │ │ ├── ArrayCreateResolveResult.cs │ │ ├── ByReferenceResolveResult.cs │ │ ├── ConstantResolveResult.cs │ │ ├── Conversion.cs │ │ ├── ConversionResolveResult.cs │ │ ├── ErrorResolveResult.cs │ │ ├── ForEachResolveResult.cs │ │ ├── InitializedObjectResolveResult.cs │ │ ├── InterpolatedStringResolveResult.cs │ │ ├── InvocationResolveResult.cs │ │ ├── LocalResolveResult.cs │ │ ├── MemberResolveResult.cs │ │ ├── NamedArgumentResolveResult.cs │ │ ├── NamespaceResolveResult.cs │ │ ├── OperatorResolveResult.cs │ │ ├── OutVarResolveResult.cs │ │ ├── ResolveResult.cs │ │ ├── SizeOfResolveResult.cs │ │ ├── ThisResolveResult.cs │ │ ├── ThrowResolveResult.cs │ │ ├── TupleResolveResult.cs │ │ ├── TypeIsResolveResult.cs │ │ ├── TypeOfResolveResult.cs │ │ ├── TypeResolveResult.cs │ │ └── UnknownMemberResolveResult.cs │ ├── SingleFileBundle.cs │ ├── Solution/ │ │ ├── ProjectId.cs │ │ ├── ProjectItem.cs │ │ └── SolutionCreator.cs │ ├── TypeSystem/ │ │ ├── Accessibility.cs │ │ ├── ApplyAttributeTypeVisitor.cs │ │ ├── ArrayType.cs │ │ ├── AssemblyQualifiedTypeName.cs │ │ ├── ByReferenceType.cs │ │ ├── ComHelper.cs │ │ ├── DecompilerTypeSystem.cs │ │ ├── ExtensionInfo.cs │ │ ├── FullTypeName.cs │ │ ├── FunctionPointerType.cs │ │ ├── GenericContext.cs │ │ ├── IAssembly.cs │ │ ├── IAttribute.cs │ │ ├── ICodeContext.cs │ │ ├── ICompilation.cs │ │ ├── IDecompilerTypeSystem.cs │ │ ├── IEntity.cs │ │ ├── IEvent.cs │ │ ├── IField.cs │ │ ├── IFreezable.cs │ │ ├── IInterningProvider.cs │ │ ├── IMember.cs │ │ ├── IMethod.cs │ │ ├── INamedElement.cs │ │ ├── INamespace.cs │ │ ├── IParameter.cs │ │ ├── IParameterizedMember.cs │ │ ├── IProperty.cs │ │ ├── ISupportsInterning.cs │ │ ├── ISymbol.cs │ │ ├── IType.cs │ │ ├── ITypeDefinition.cs │ │ ├── ITypeDefinitionOrUnknown.cs │ │ ├── ITypeParameter.cs │ │ ├── ITypeReference.cs │ │ ├── IVariable.cs │ │ ├── Implementation/ │ │ │ ├── AbstractFreezable.cs │ │ │ ├── AbstractType.cs │ │ │ ├── AbstractTypeParameter.cs │ │ │ ├── AttributeListBuilder.cs │ │ │ ├── BaseTypeCollector.cs │ │ │ ├── CustomAttribute.cs │ │ │ ├── DecimalConstantHelper.cs │ │ │ ├── DecoratedType.cs │ │ │ ├── DefaultAssemblyReference.cs │ │ │ ├── DefaultAttribute.cs │ │ │ ├── DefaultParameter.cs │ │ │ ├── DefaultTypeParameter.cs │ │ │ ├── DefaultVariable.cs │ │ │ ├── DummyTypeParameter.cs │ │ │ ├── FakeMember.cs │ │ │ ├── GetMembersHelper.cs │ │ │ ├── KnownAttributes.cs │ │ │ ├── KnownTypeCache.cs │ │ │ ├── LocalFunctionMethod.cs │ │ │ ├── MergedNamespace.cs │ │ │ ├── MetadataEvent.cs │ │ │ ├── MetadataField.cs │ │ │ ├── MetadataMethod.cs │ │ │ ├── MetadataNamespace.cs │ │ │ ├── MetadataParameter.cs │ │ │ ├── MetadataProperty.cs │ │ │ ├── MetadataTypeDefinition.cs │ │ │ ├── MetadataTypeParameter.cs │ │ │ ├── MinimalCorlib.cs │ │ │ ├── NestedTypeReference.cs │ │ │ ├── NullabilityAnnotatedType.cs │ │ │ ├── PinnedType.cs │ │ │ ├── SimpleCompilation.cs │ │ │ ├── SpecializedEvent.cs │ │ │ ├── SpecializedField.cs │ │ │ ├── SpecializedMember.cs │ │ │ ├── SpecializedMethod.cs │ │ │ ├── SpecializedParameter.cs │ │ │ ├── SpecializedProperty.cs │ │ │ ├── SyntheticRangeIndexer.cs │ │ │ ├── ThreeState.cs │ │ │ ├── TypeParameterReference.cs │ │ │ ├── TypeWithElementType.cs │ │ │ └── UnknownType.cs │ │ ├── InheritanceHelper.cs │ │ ├── IntersectionType.cs │ │ ├── KnownTypeReference.cs │ │ ├── MetadataModule.cs │ │ ├── ModifiedType.cs │ │ ├── NormalizeTypeVisitor.cs │ │ ├── Nullability.cs │ │ ├── NullableType.cs │ │ ├── ParameterListComparer.cs │ │ ├── ParameterizedType.cs │ │ ├── PointerType.cs │ │ ├── ReferenceResolvingException.cs │ │ ├── ReflectionHelper.cs │ │ ├── ReflectionNameParseException.cs │ │ ├── SimpleTypeResolveContext.cs │ │ ├── SpecialType.cs │ │ ├── TaskType.cs │ │ ├── TopLevelTypeName.cs │ │ ├── TupleType.cs │ │ ├── TypeKind.cs │ │ ├── TypeParameterSubstitution.cs │ │ ├── TypeProvider.cs │ │ ├── TypeSystemExtensions.cs │ │ ├── TypeUtils.cs │ │ ├── TypeVisitor.cs │ │ └── VarArgInstanceMethod.cs │ ├── Util/ │ │ ├── BitOperations.cs │ │ ├── BitSet.cs │ │ ├── BusyManager.cs │ │ ├── CSharpPrimitiveCast.cs │ │ ├── CacheManager.cs │ │ ├── CallbackOnDispose.cs │ │ ├── CollectionExtensions.cs │ │ ├── DelegateComparer.cs │ │ ├── EmptyList.cs │ │ ├── ExtensionMethods.cs │ │ ├── FileUtility.cs │ │ ├── GraphTraversal.cs │ │ ├── Index.cs │ │ ├── Interval.cs │ │ ├── KeyComparer.cs │ │ ├── LazyInit.cs │ │ ├── LongDict.cs │ │ ├── LongSet.cs │ │ ├── MultiDictionary.cs │ │ ├── Platform.cs │ │ ├── ProjectedList.cs │ │ ├── ReferenceComparer.cs │ │ ├── ResXResourceWriter.cs │ │ ├── ResourcesFile.cs │ │ ├── TreeTraversal.cs │ │ ├── UnicodeNewline.cs │ │ ├── UnionFind.cs │ │ └── Win32Resources.cs │ └── packages.lock.json ├── ICSharpCode.Decompiler.PowerShell/ │ ├── Demo.ps1 │ ├── ErrorIds.cs │ ├── GetDecompiledProjectCmdlet.cs │ ├── GetDecompiledSourceCmdlet.cs │ ├── GetDecompiledTypesCmdlet.cs │ ├── GetDecompilerCmdlet.cs │ ├── GetDecompilerVersion.cs │ ├── GetTargetFramework.cs │ ├── ICSharpCode.Decompiler.PowerShell.csproj │ ├── NullAttributes.cs │ ├── README.md │ ├── TypesParser.cs │ └── manifest.psd1 ├── ICSharpCode.Decompiler.TestRunner/ │ ├── ICSharpCode.Decompiler.TestRunner.csproj │ └── Program.cs ├── ICSharpCode.Decompiler.Tests/ │ ├── .editorconfig │ ├── CorrectnessTestRunner.cs │ ├── DataFlowTest.cs │ ├── DisassemblerPrettyTestRunner.cs │ ├── Helpers/ │ │ ├── CodeAssert.cs │ │ ├── RemoveCompilerAttribute.cs │ │ ├── RoslynToolset.cs │ │ ├── SdkUtility.cs │ │ ├── Tester.VB.cs │ │ ├── Tester.cs │ │ └── TestsAssemblyOutput.cs │ ├── ICSharpCode.Decompiler.Tests.csproj │ ├── ILPrettyTestRunner.cs │ ├── Output/ │ │ ├── CSharpAmbienceTests.cs │ │ ├── ILAmbienceTests.cs │ │ └── InsertParenthesesVisitorTests.cs │ ├── PdbGenerationTestRunner.cs │ ├── PrettyTestRunner.cs │ ├── ProjectDecompiler/ │ │ ├── TargetFrameworkTests.cs │ │ └── WholeProjectDecompilerTests.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── RoundtripAssembly.cs │ ├── Semantics/ │ │ ├── ConversionTests.cs │ │ ├── ExplicitConversionTest.cs │ │ └── OverloadResolutionTests.cs │ ├── TestAssemblyResolver.cs │ ├── TestCases/ │ │ ├── Correctness/ │ │ │ ├── Async.cs │ │ │ ├── BitNot.il │ │ │ ├── Capturing.cs │ │ │ ├── ComInterop.cs │ │ │ ├── Comparisons.cs │ │ │ ├── CompoundAssignment.cs │ │ │ ├── ConditionalAttr.cs │ │ │ ├── ControlFlow.cs │ │ │ ├── Conversions.cs │ │ │ ├── DecimalFields.cs │ │ │ ├── DeconstructionTests.cs │ │ │ ├── DynamicTests.cs │ │ │ ├── ExpressionTrees.cs │ │ │ ├── FloatingPointArithmetic.cs │ │ │ ├── Generics.cs │ │ │ ├── HelloWorld.cs │ │ │ ├── InitializerTests.cs │ │ │ ├── Jmp.il │ │ │ ├── LINQRaytracer.cs │ │ │ ├── Loops.cs │ │ │ ├── MemberLookup.cs │ │ │ ├── MiniJSON.cs │ │ │ ├── NonGenericConstrainedCallVirt.il │ │ │ ├── NullPropagation.cs │ │ │ ├── NullableTests.cs │ │ │ ├── OverloadResolution.cs │ │ │ ├── PropertiesAndEvents.cs │ │ │ ├── Readme.txt │ │ │ ├── StackTests.il │ │ │ ├── StackTypes.il │ │ │ ├── StringConcat.cs │ │ │ ├── Switch.cs │ │ │ ├── TrickyTypes.cs │ │ │ ├── UndocumentedExpressions.cs │ │ │ ├── Uninit.vb │ │ │ ├── UnsafeCode.cs │ │ │ ├── Using.cs │ │ │ ├── ValueTypeCall.cs │ │ │ └── YieldReturn.cs │ │ ├── Disassembler/ │ │ │ └── Pretty/ │ │ │ ├── .gitignore │ │ │ ├── GenericConstraints.il │ │ │ ├── InterfaceImplAttributes.il │ │ │ ├── SecurityDeclarations.il │ │ │ ├── SortMembers.expected.il │ │ │ └── SortMembers.il │ │ ├── ILPretty/ │ │ │ ├── .gitignore │ │ │ ├── CS1xSwitch_Debug.cs │ │ │ ├── CS1xSwitch_Debug.il │ │ │ ├── CS1xSwitch_Release.cs │ │ │ ├── CS1xSwitch_Release.il │ │ │ ├── CallIndirect.cs │ │ │ ├── CallIndirect.il │ │ │ ├── ConstantBlobs.cs │ │ │ ├── ConstantBlobs.il │ │ │ ├── DirectCallToExplicitInterfaceImpl.cs │ │ │ ├── DirectCallToExplicitInterfaceImpl.il │ │ │ ├── EmptyBodies.cs │ │ │ ├── EmptyBodies.il │ │ │ ├── EvalOrder.cs │ │ │ ├── EvalOrder.il │ │ │ ├── ExtensionEncodingV1.cs │ │ │ ├── ExtensionEncodingV1.il │ │ │ ├── ExtensionEncodingV2.cs │ │ │ ├── ExtensionEncodingV2.il │ │ │ ├── FSharpLoops.fs │ │ │ ├── FSharpLoops_Debug.cs │ │ │ ├── FSharpLoops_Debug.il │ │ │ ├── FSharpLoops_Release.cs │ │ │ ├── FSharpLoops_Release.il │ │ │ ├── FSharpUsing.fs │ │ │ ├── FSharpUsing_Debug.cs │ │ │ ├── FSharpUsing_Debug.il │ │ │ ├── FSharpUsing_Release.cs │ │ │ ├── FSharpUsing_Release.il │ │ │ ├── GuessAccessors.cs │ │ │ ├── GuessAccessors.il │ │ │ ├── Issue1038.cs │ │ │ ├── Issue1038.il │ │ │ ├── Issue1047.cs │ │ │ ├── Issue1047.il │ │ │ ├── Issue1145.cs │ │ │ ├── Issue1145.il │ │ │ ├── Issue1157.cs │ │ │ ├── Issue1157.il │ │ │ ├── Issue1256.cs │ │ │ ├── Issue1256.il │ │ │ ├── Issue1323.cs │ │ │ ├── Issue1323.il │ │ │ ├── Issue1325.cs │ │ │ ├── Issue1325.il │ │ │ ├── Issue1325.vb │ │ │ ├── Issue1389.cs │ │ │ ├── Issue1389.il │ │ │ ├── Issue1454.cs │ │ │ ├── Issue1454.il │ │ │ ├── Issue1681.cs │ │ │ ├── Issue1681.il │ │ │ ├── Issue1918.cs │ │ │ ├── Issue1918.il │ │ │ ├── Issue1922.cs │ │ │ ├── Issue1922.il │ │ │ ├── Issue2104.cs │ │ │ ├── Issue2104.il │ │ │ ├── Issue2260SwitchString.cs │ │ │ ├── Issue2260SwitchString.il │ │ │ ├── Issue2443.cs │ │ │ ├── Issue2443.il │ │ │ ├── Issue3344CkFinite.cs │ │ │ ├── Issue3344CkFinite.il │ │ │ ├── Issue3421.cs │ │ │ ├── Issue3421.il │ │ │ ├── Issue3442.cs │ │ │ ├── Issue3442.il │ │ │ ├── Issue3465.cs │ │ │ ├── Issue3465.il │ │ │ ├── Issue3466.cs │ │ │ ├── Issue3466.il │ │ │ ├── Issue3504.cs │ │ │ ├── Issue3504.il │ │ │ ├── Issue3524.cs │ │ │ ├── Issue3524.il │ │ │ ├── Issue3552.cs │ │ │ ├── Issue3552.il │ │ │ ├── Issue379.cs │ │ │ ├── Issue379.il │ │ │ ├── Issue646.cs │ │ │ ├── Issue646.il │ │ │ ├── Issue684.cs │ │ │ ├── Issue684.il │ │ │ ├── Issue959.cs │ │ │ ├── Issue959.il │ │ │ ├── Issue982.cs │ │ │ ├── Issue982.il │ │ │ ├── MonoFixed.cs │ │ │ ├── MonoFixed.il │ │ │ ├── SequenceOfNestedIfs.cs │ │ │ ├── SequenceOfNestedIfs.il │ │ │ ├── UnknownTypes.cs │ │ │ ├── UnknownTypes.il │ │ │ ├── Unsafe.cs │ │ │ ├── Unsafe.il │ │ │ ├── WeirdEnums.cs │ │ │ └── WeirdEnums.il │ │ ├── PdbGen/ │ │ │ ├── .gitignore │ │ │ ├── CustomPdbId.xml │ │ │ ├── ForLoopTests.xml │ │ │ ├── HelloWorld.xml │ │ │ ├── LambdaCapturing.xml │ │ │ ├── Members.xml │ │ │ └── ProgressReporting.xml │ │ ├── Pretty/ │ │ │ ├── .gitignore │ │ │ ├── AnonymousTypes.cs │ │ │ ├── AssemblyCustomAttributes.cs │ │ │ ├── Async.cs │ │ │ ├── AsyncForeach.cs │ │ │ ├── AsyncMain.cs │ │ │ ├── AsyncStreams.cs │ │ │ ├── AsyncUsing.cs │ │ │ ├── AutoProperties.cs │ │ │ ├── CS72_PrivateProtected.cs │ │ │ ├── CS73_StackAllocInitializers.cs │ │ │ ├── CS9_ExtensionGetEnumerator.cs │ │ │ ├── CheckedUnchecked.cs │ │ │ ├── Comparisons.cs │ │ │ ├── CompoundAssignmentTest.cs │ │ │ ├── ConstantsTests.cs │ │ │ ├── ConstructorInitializers.cs │ │ │ ├── CovariantReturns.cs │ │ │ ├── CustomAttributeConflicts.cs │ │ │ ├── CustomAttributeSamples.cs │ │ │ ├── CustomAttributes.cs │ │ │ ├── CustomAttributes2.cs │ │ │ ├── CustomShortCircuitOperators.cs │ │ │ ├── CustomTaskType.cs │ │ │ ├── DeconstructionTests.cs │ │ │ ├── DelegateConstruction.cs │ │ │ ├── Discards.cs │ │ │ ├── DynamicTests.cs │ │ │ ├── EnumTests.cs │ │ │ ├── ExceptionHandling.cs │ │ │ ├── ExpandParamsArgumentsDisabled.cs │ │ │ ├── ExpressionTrees.cs │ │ │ ├── ExtensionProperties.cs │ │ │ ├── FileScopedNamespaces.cs │ │ │ ├── FixProxyCalls.cs │ │ │ ├── FunctionPointers.cs │ │ │ ├── Generics.cs │ │ │ ├── GloballyQualifiedTypeInStringInterpolation.cs │ │ │ ├── HelloWorld.cs │ │ │ ├── IndexRangeTest.cs │ │ │ ├── InitializerTests.cs │ │ │ ├── InlineArrayTests.cs │ │ │ ├── InlineAssignmentTest.cs │ │ │ ├── InterfaceTests.cs │ │ │ ├── Issue1080.cs │ │ │ ├── Issue3406.cs │ │ │ ├── Issue3439.cs │ │ │ ├── Issue3442.cs │ │ │ ├── Issue3452.cs │ │ │ ├── Issue3483.cs │ │ │ ├── Issue3541.cs │ │ │ ├── Issue3571_A.cs │ │ │ ├── Issue3571_B.cs │ │ │ ├── Issue3571_C.cs │ │ │ ├── Issue3576.cs │ │ │ ├── Issue3584.cs │ │ │ ├── Issue3598.cs │ │ │ ├── Issue3610.cs │ │ │ ├── Issue3611.cs │ │ │ ├── LiftedOperators.cs │ │ │ ├── LocalFunctions.cs │ │ │ ├── Lock.cs │ │ │ ├── Loops.cs │ │ │ ├── MemberTests.cs │ │ │ ├── MetadataAttributes.cs │ │ │ ├── MultidimensionalArray.cs │ │ │ ├── NamedArguments.cs │ │ │ ├── NativeInts.cs │ │ │ ├── NullPropagation.cs │ │ │ ├── NullableRefTypes.cs │ │ │ ├── Operators.cs │ │ │ ├── OptionalArguments.cs │ │ │ ├── OptionalArgumentsDisabled.cs │ │ │ ├── OutVariables.cs │ │ │ ├── PInvoke.cs │ │ │ ├── ParamsCollections.cs │ │ │ ├── PatternMatching.cs │ │ │ ├── PointerArithmetic.cs │ │ │ ├── PropertiesAndEvents.cs │ │ │ ├── QualifierTests.cs │ │ │ ├── QueryExpressions.cs │ │ │ ├── Readme.txt │ │ │ ├── Records.cs │ │ │ ├── ReduceNesting.cs │ │ │ ├── RefFields.cs │ │ │ ├── RefLocalsAndReturns.cs │ │ │ ├── ShortCircuit.cs │ │ │ ├── StaticAbstractInterfaceMembers.cs │ │ │ ├── StringInterpolation.cs │ │ │ ├── Structs.cs │ │ │ ├── Switch.cs │ │ │ ├── SwitchExpressions.cs │ │ │ ├── ThrowExpressions.cs │ │ │ ├── TupleTests.cs │ │ │ ├── TypeAnalysisTests.cs │ │ │ ├── TypeMemberTests.cs │ │ │ ├── UnsafeCode.cs │ │ │ ├── UserDefinedConversions.cs │ │ │ ├── Using.cs │ │ │ ├── UsingVariables.cs │ │ │ ├── ValueTypes.cs │ │ │ ├── VariableNaming.cs │ │ │ ├── VariableNamingWithoutSymbols.cs │ │ │ ├── WellKnownConstants.cs │ │ │ └── YieldReturn.cs │ │ ├── Ugly/ │ │ │ ├── .gitignore │ │ │ ├── AggressiveScalarReplacementOfAggregates.Expected.cs │ │ │ ├── AggressiveScalarReplacementOfAggregates.cs │ │ │ ├── AggressiveScalarReplacementOfAggregates.net40.roslyn.il │ │ │ ├── AggressiveScalarReplacementOfAggregates.opt.net40.roslyn.il │ │ │ ├── AggressiveScalarReplacementOfAggregates.opt.roslyn.il │ │ │ ├── AggressiveScalarReplacementOfAggregates.roslyn.il │ │ │ ├── NoArrayInitializers.Expected.cs │ │ │ ├── NoArrayInitializers.cs │ │ │ ├── NoArrayInitializers.net40.roslyn.il │ │ │ ├── NoArrayInitializers.opt.net40.roslyn.il │ │ │ ├── NoArrayInitializers.opt.roslyn.il │ │ │ ├── NoArrayInitializers.roslyn.il │ │ │ ├── NoDecimalConstants.Expected.cs │ │ │ ├── NoDecimalConstants.cs │ │ │ ├── NoDecimalConstants.net40.roslyn.il │ │ │ ├── NoDecimalConstants.opt.net40.roslyn.il │ │ │ ├── NoDecimalConstants.opt.roslyn.il │ │ │ ├── NoDecimalConstants.roslyn.il │ │ │ ├── NoExtensionMethods.Expected.cs │ │ │ ├── NoExtensionMethods.cs │ │ │ ├── NoExtensionMethods.net40.roslyn.il │ │ │ ├── NoExtensionMethods.opt.net40.roslyn.il │ │ │ ├── NoExtensionMethods.opt.roslyn.il │ │ │ ├── NoExtensionMethods.roslyn.il │ │ │ ├── NoForEachStatement.Expected.cs │ │ │ ├── NoForEachStatement.cs │ │ │ ├── NoForEachStatement.il │ │ │ ├── NoForEachStatement.net40.roslyn.il │ │ │ ├── NoForEachStatement.opt.il │ │ │ ├── NoForEachStatement.opt.net40.roslyn.il │ │ │ ├── NoForEachStatement.opt.roslyn.il │ │ │ ├── NoForEachStatement.roslyn.il │ │ │ ├── NoLocalFunctions.Expected.cs │ │ │ ├── NoLocalFunctions.cs │ │ │ ├── NoLocalFunctions.net40.roslyn.il │ │ │ ├── NoLocalFunctions.opt.net40.roslyn.il │ │ │ ├── NoLocalFunctions.opt.roslyn.il │ │ │ ├── NoLocalFunctions.roslyn.il │ │ │ ├── NoNewOfT.Expected.cs │ │ │ ├── NoNewOfT.cs │ │ │ ├── NoNewOfT.il │ │ │ ├── NoNewOfT.net40.roslyn.il │ │ │ ├── NoNewOfT.opt.il │ │ │ ├── NoNewOfT.opt.net40.roslyn.il │ │ │ ├── NoNewOfT.opt.roslyn.il │ │ │ ├── NoNewOfT.roslyn.il │ │ │ ├── NoPropertiesAndEvents.Expected.cs │ │ │ ├── NoPropertiesAndEvents.cs │ │ │ ├── NoPropertiesAndEvents.il │ │ │ ├── NoPropertiesAndEvents.net40.roslyn.il │ │ │ ├── NoPropertiesAndEvents.opt.il │ │ │ ├── NoPropertiesAndEvents.opt.net40.roslyn.il │ │ │ ├── NoPropertiesAndEvents.opt.roslyn.il │ │ │ └── NoPropertiesAndEvents.roslyn.il │ │ └── VBPretty/ │ │ ├── .gitignore │ │ ├── Async.cs │ │ ├── Async.vb │ │ ├── Issue1906.cs │ │ ├── Issue1906.vb │ │ ├── Issue2192.cs │ │ ├── Issue2192.vb │ │ ├── Select.cs │ │ ├── Select.vb │ │ ├── VBAutomaticEvents.cs │ │ ├── VBAutomaticEvents.vb │ │ ├── VBCompoundAssign.cs │ │ ├── VBCompoundAssign.vb │ │ ├── VBNonGenericForEach.cs │ │ ├── VBNonGenericForEach.vb │ │ ├── VBPropertiesTest.cs │ │ ├── VBPropertiesTest.vb │ │ ├── YieldReturn.cs │ │ └── YieldReturn.vb │ ├── TestTraceListener.cs │ ├── TypeSystem/ │ │ ├── ReflectionHelperTests.cs │ │ ├── TypeSystemLoaderTests.cs │ │ └── TypeSystemTestCase.cs │ ├── UglyTestRunner.cs │ ├── Util/ │ │ ├── BitSetTests.cs │ │ ├── FileUtilityTests.cs │ │ ├── IntervalTests.cs │ │ └── LongSetTests.cs │ └── VBPrettyTestRunner.cs ├── ICSharpCode.ILSpyCmd/ │ ├── AsContainer/ │ │ ├── Dockerfile │ │ ├── DockerfileForAutomation │ │ └── README.md │ ├── DotNetToolUpdateChecker.cs │ ├── ICSharpCode.ILSpyCmd.csproj │ ├── IlspyCmdProgram.cs │ ├── ProgramExitCodes.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── README.md │ ├── TypesParser.cs │ ├── ValidationAttributes.cs │ └── packages.lock.json ├── ICSharpCode.ILSpyX/ │ ├── Abstractions/ │ │ ├── ILanguage.cs │ │ └── ITreeNode.cs │ ├── Analyzers/ │ │ ├── AnalyzerContext.cs │ │ ├── AnalyzerHelpers.cs │ │ ├── AnalyzerScope.cs │ │ ├── Builtin/ │ │ │ ├── AttributeAppliedToAnalyzer.cs │ │ │ ├── EventImplementedByAnalyzer.cs │ │ │ ├── EventOverriddenByAnalyzer.cs │ │ │ ├── FieldAccessAnalyzer.cs │ │ │ ├── FindTypeInAttributeDecoder.cs │ │ │ ├── MemberImplementsInterfaceAnalyzer.cs │ │ │ ├── MethodImplementedByAnalyzer.cs │ │ │ ├── MethodOverriddenByAnalyzer.cs │ │ │ ├── MethodUsedByAnalyzer.cs │ │ │ ├── MethodUsesAnalyzer.cs │ │ │ ├── MethodVirtualUsedByAnalyzer.cs │ │ │ ├── PropertyImplementedByAnalyzer.cs │ │ │ ├── PropertyOverriddenByAnalyzer.cs │ │ │ ├── TypeExposedByAnalyzer.cs │ │ │ ├── TypeExtensionMethodsAnalyzer.cs │ │ │ ├── TypeInstantiatedByAnalyzer.cs │ │ │ └── TypeUsedByAnalyzer.cs │ │ ├── ExportAnalyzerAttribute.cs │ │ └── IAnalyzer.cs │ ├── ApiVisibility.cs │ ├── AssemblyList.cs │ ├── AssemblyListManager.cs │ ├── AssemblyListSnapshot.cs │ ├── Extensions/ │ │ └── CollectionExtensions.cs │ ├── FileLoaders/ │ │ ├── ArchiveFileLoader.cs │ │ ├── BundleFileLoader.cs │ │ ├── FileLoaderRegistry.cs │ │ ├── LoadResult.cs │ │ ├── MetadataFileLoader.cs │ │ ├── PEFileLoader.cs │ │ ├── WebCilFileLoader.cs │ │ └── XamarinCompressedFileLoader.cs │ ├── ICSharpCode.ILSpyX.csproj │ ├── LanguageVersion.cs │ ├── LoadedAssembly.cs │ ├── LoadedAssemblyExtensions.cs │ ├── LoadedPackage.cs │ ├── MermaidDiagrammer/ │ │ ├── ClassDiagrammer.cs │ │ ├── ClassDiagrammerFactory.cs │ │ ├── EmbeddedResource.cs │ │ ├── Extensions/ │ │ │ ├── StringExtensions.cs │ │ │ └── TypeExtensions.cs │ │ ├── Factory.BuildTypes.cs │ │ ├── Factory.FlatMembers.cs │ │ ├── Factory.Relationships.cs │ │ ├── Factory.TypeIds.cs │ │ ├── Factory.TypeNames.cs │ │ ├── GenerateHtmlDiagrammer.cs │ │ ├── Generator.Run.cs │ │ ├── ReadMe.md │ │ ├── XmlDocumentationFormatter.cs │ │ └── html/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── README.txt │ │ ├── gulpfile.js │ │ ├── package.json │ │ ├── script.js │ │ ├── styles.css │ │ ├── styles.less │ │ └── template.html │ ├── PackageReadme.md │ ├── PdbProvider/ │ │ ├── DebugInfoUtils.cs │ │ ├── MonoCecilDebugInfoProvider.cs │ │ └── PortableDebugInfoProvider.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Search/ │ │ ├── AbstractEntitySearchStrategy.cs │ │ ├── AbstractSearchStrategy.cs │ │ ├── AssemblySearchStrategy.cs │ │ ├── CSharpLexer.cs │ │ ├── LiteralSearchStrategy.cs │ │ ├── MemberSearchStrategy.cs │ │ ├── MetadataTokenSearchStrategy.cs │ │ ├── NamespaceSearchStrategy.cs │ │ ├── ResourceSearchStrategy.cs │ │ └── SearchResult.cs │ ├── Settings/ │ │ ├── DecompilerSettings.cs │ │ ├── DefaultSettingsFilePathProvider.cs │ │ ├── ILSpySettings.cs │ │ ├── ISettingsFilePathProvider.cs │ │ ├── ISettingsProvider.cs │ │ └── SettingsServiceBase.cs │ ├── TreeView/ │ │ ├── FlatListTreeNode.cs │ │ ├── PlatformAbstractions/ │ │ │ ├── IPlatformDataObject.cs │ │ │ ├── IPlatformDragDrop.cs │ │ │ ├── IPlatformDragEventArgs.cs │ │ │ ├── IPlatformRoutedEventArgs.cs │ │ │ ├── ITreeNodeImagesProvider.cs │ │ │ └── XPlatDragDropEffects.cs │ │ ├── SharpTreeNode.cs │ │ ├── SharpTreeNodeCollection.cs │ │ ├── TreeFlattener.cs │ │ └── TreeTraversal.cs │ └── packages.lock.json ├── ILSpy/ │ ├── AboutPage.cs │ ├── Analyzers/ │ │ ├── AnalyzeCommand.cs │ │ ├── AnalyzerEntityTreeNode.cs │ │ ├── AnalyzerRootNode.cs │ │ ├── AnalyzerSearchTreeNode.cs │ │ ├── AnalyzerTreeNode.cs │ │ ├── AnalyzerTreeView.xaml │ │ ├── AnalyzerTreeView.xaml.cs │ │ ├── AnalyzerTreeViewModel.cs │ │ ├── CopyAnalysisResultsContextMenuEntry.cs │ │ ├── RemoveAnalyzeContextMenuEntry.cs │ │ └── TreeNodes/ │ │ ├── AnalyzedAccessorTreeNode.cs │ │ ├── AnalyzedEventTreeNode.cs │ │ ├── AnalyzedFieldTreeNode.cs │ │ ├── AnalyzedMethodTreeNode.cs │ │ ├── AnalyzedModuleTreeNode.cs │ │ ├── AnalyzedPropertyTreeNode.cs │ │ └── AnalyzedTypeTreeNode.cs │ ├── App.xaml │ ├── App.xaml.cs │ ├── AppEnv/ │ │ ├── AppEnvironment.cs │ │ ├── CommandLineArguments.cs │ │ ├── CommandLineTools.cs │ │ └── SingleInstance.cs │ ├── AssemblyTree/ │ │ ├── AssemblyListPane.xaml │ │ ├── AssemblyListPane.xaml.cs │ │ └── AssemblyTreeModel.cs │ ├── AvalonEdit/ │ │ ├── ITextMarker.cs │ │ └── TextMarkerService.cs │ ├── Commands/ │ │ ├── BrowseBackCommand.cs │ │ ├── BrowseForwardCommand.cs │ │ ├── CheckForUpdatesCommand.cs │ │ ├── CommandWrapper.cs │ │ ├── CompareContextMenuEntry.cs │ │ ├── CopyFullyQualifiedNameContextMenuEntry.cs │ │ ├── CreateDiagramContextMenuEntry.cs │ │ ├── DecompileAllCommand.cs │ │ ├── DecompileCommand.cs │ │ ├── DecompileInNewViewCommand.cs │ │ ├── DelegateCommand.cs │ │ ├── DisassembleAllCommand.cs │ │ ├── ExitCommand.cs │ │ ├── ExportCommandAttribute.cs │ │ ├── ExtractPackageEntryContextMenuEntry.cs │ │ ├── GeneratePdbContextMenuEntry.cs │ │ ├── IProtocolHandler.cs │ │ ├── ManageAssemblyListsCommand.cs │ │ ├── OpenCommand.cs │ │ ├── OpenFromGacCommand.cs │ │ ├── Pdb2XmlCommand.cs │ │ ├── RefreshCommand.cs │ │ ├── RemoveAssembliesWithLoadErrors.cs │ │ ├── SaveCodeContextMenuEntry.cs │ │ ├── SaveCommand.cs │ │ ├── ScopeSearchToAssembly.cs │ │ ├── ScopeSearchToNamespace.cs │ │ ├── SearchMsdnContextMenuEntry.cs │ │ ├── SelectPdbContextMenuEntry.cs │ │ ├── SetThemeCommand.cs │ │ ├── ShowCFGContextMenuEntry.cs │ │ ├── ShowPane.cs │ │ ├── SimpleCommand.cs │ │ └── SortAssemblyListCommand.cs │ ├── ContextMenuEntry.cs │ ├── Controls/ │ │ ├── CollapsiblePanel.cs │ │ ├── CultureSelectionConverter.cs │ │ ├── CustomDialog.cs │ │ ├── ExtensionMethods.cs │ │ ├── GridViewColumnAutoSize.cs │ │ ├── MainMenu.xaml │ │ ├── MainMenu.xaml.cs │ │ ├── MainToolBar.xaml │ │ ├── MainToolBar.xaml.cs │ │ ├── MarkupExtensions.cs │ │ ├── ResourceObjectTable.xaml │ │ ├── ResourceObjectTable.xaml.cs │ │ ├── ResourceStringTable.xaml │ │ ├── ResourceStringTable.xaml.cs │ │ ├── SearchBox.cs │ │ ├── SearchBoxStyle.xaml │ │ ├── SortableGridViewColumn.cs │ │ ├── TreeView/ │ │ │ ├── EditTextBox.cs │ │ │ ├── ExtensionMethods.cs │ │ │ ├── GeneralAdorner.cs │ │ │ ├── InsertMarker.cs │ │ │ ├── LinesRenderer.cs │ │ │ ├── SharpGridView.cs │ │ │ ├── SharpTreeNodeView.cs │ │ │ ├── SharpTreeView.cs │ │ │ ├── SharpTreeView.xaml │ │ │ ├── SharpTreeViewAutomationPeer.cs │ │ │ ├── SharpTreeViewItem.cs │ │ │ ├── SharpTreeViewItemAutomationPeer.cs │ │ │ ├── SharpTreeViewTextSearch.cs │ │ │ ├── WpfWindowsDataObject.cs │ │ │ ├── WpfWindowsDragDropManager.cs │ │ │ ├── WpfWindowsDragEventArgs.cs │ │ │ └── WpfWindowsRoutedEventArgs.cs │ │ ├── XamlResourceExtension.cs │ │ ├── ZoomButtons.cs │ │ ├── ZoomScrollViewer.cs │ │ └── ZoomScrollViewer.xaml │ ├── DecompilationOptions.cs │ ├── Docking/ │ │ ├── CloseAllDocumentsCommand.cs │ │ ├── DockLayoutSettings.cs │ │ ├── DockWorkspace.cs │ │ ├── PaneStyleSelector.cs │ │ └── TabPageGuardConverter.cs │ ├── EntityReference.cs │ ├── ExtensionMethods.cs │ ├── GlobalUsings.cs │ ├── GuessFileType.cs │ ├── ILSpy.csproj │ ├── ILSpySettingsFilePathProvider.cs │ ├── ILSpyTraceListener.cs │ ├── ISmartTextOutput.cs │ ├── Images/ │ │ ├── AccessOverlayIcon.cs │ │ ├── Assembly.xaml │ │ ├── AssemblyList.xaml │ │ ├── AssemblyListGAC.xaml │ │ ├── AssemblyLoading.xaml │ │ ├── AssemblyWarning.xaml │ │ ├── Back.xaml │ │ ├── Class.xaml │ │ ├── Close.xaml │ │ ├── CollapseAll.xaml │ │ ├── Constructor.xaml │ │ ├── Copy.xaml │ │ ├── Delegate.xaml │ │ ├── Delete.xaml │ │ ├── DictionaryContain.xaml │ │ ├── Enum.xaml │ │ ├── EnumValue.xaml │ │ ├── Event.xaml │ │ ├── ExpandAll.xaml │ │ ├── ExportOverlay.xaml │ │ ├── ExtensionMethod.xaml │ │ ├── Field.xaml │ │ ├── FieldReadOnly.xaml │ │ ├── FindAssembly.xaml │ │ ├── Folder.Closed.xaml │ │ ├── Folder.Open.xaml │ │ ├── Forward.xaml │ │ ├── Header.xaml │ │ ├── Heap.xaml │ │ ├── ILSpy.pdn │ │ ├── Images.cs │ │ ├── Indexer.xaml │ │ ├── Interface.xaml │ │ ├── Library.xaml │ │ ├── ListFolder.Open.xaml │ │ ├── ListFolder.xaml │ │ ├── Literal.xaml │ │ ├── MemberIcon.cs │ │ ├── Metadata.xaml │ │ ├── MetadataFile.xaml │ │ ├── MetadataTable.xaml │ │ ├── MetadataTableGroup.xaml │ │ ├── Method.xaml │ │ ├── Namespace.xaml │ │ ├── OK.xaml │ │ ├── Open.xaml │ │ ├── Operator.xaml │ │ ├── OverlayCompilerControlled.xaml │ │ ├── OverlayInternal.xaml │ │ ├── OverlayPrivate.xaml │ │ ├── OverlayPrivateProtected.xaml │ │ ├── OverlayProtected.xaml │ │ ├── OverlayProtectedInternal.xaml │ │ ├── OverlayStatic.xaml │ │ ├── PInvokeMethod.xaml │ │ ├── ProgramDebugDatabase.xaml │ │ ├── Property.xaml │ │ ├── README.md │ │ ├── ReferenceFolder.xaml │ │ ├── ReferenceOverlay.xaml │ │ ├── Refresh.xaml │ │ ├── Resource.xaml │ │ ├── ResourceImage.xaml │ │ ├── ResourceResourcesFile.xaml │ │ ├── ResourceXml.xaml │ │ ├── ResourceXsd.xaml │ │ ├── ResourceXsl.xaml │ │ ├── ResourceXslt.xaml │ │ ├── ResultToJSON.xaml │ │ ├── Save.xaml │ │ ├── Search.xaml │ │ ├── SearchMsdn.xaml │ │ ├── ShowAll.xaml │ │ ├── ShowPrivateInternal.xaml │ │ ├── ShowPublicOnly.xaml │ │ ├── Sort.xaml │ │ ├── Struct.xaml │ │ ├── SubTypes.xaml │ │ ├── SuperTypes.xaml │ │ ├── SwitchSourceOrTarget.xaml │ │ ├── TypeIcon.cs │ │ ├── ViewCode.xaml │ │ ├── VirtualMethod.xaml │ │ ├── Warning.xaml │ │ ├── WebAssembly.xaml │ │ ├── WpfWindowsTreeNodeImagesProvider.cs │ │ ├── ZoomIn.xaml │ │ └── ZoomOut.xaml │ ├── LanguageSettings.cs │ ├── Languages/ │ │ ├── CSharpBracketSearcher.cs │ │ ├── CSharpHighlightingTokenWriter.cs │ │ ├── CSharpILMixedLanguage.cs │ │ ├── CSharpLanguage.cs │ │ ├── ILAstLanguage.cs │ │ ├── ILLanguage.cs │ │ ├── IResourceFileHandler.cs │ │ ├── Language.cs │ │ └── LanguageService.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── MainWindowViewModel.cs │ ├── Metadata/ │ │ ├── CoffHeaderTreeNode.cs │ │ ├── CorTables/ │ │ │ ├── AssemblyRefTableTreeNode.cs │ │ │ ├── AssemblyTableTreeNode.cs │ │ │ ├── ClassLayoutTableTreeNode.cs │ │ │ ├── ConstantTableTreeNode.cs │ │ │ ├── CustomAttributeTableTreeNode.cs │ │ │ ├── DeclSecurityTableTreeNode.cs │ │ │ ├── EventMapTableTreeNode.cs │ │ │ ├── EventTableTreeNode.cs │ │ │ ├── ExportedTypeTableTreeNode.cs │ │ │ ├── FieldLayoutTableTreeNode.cs │ │ │ ├── FieldMarshalTableTreeNode.cs │ │ │ ├── FieldRVATableTreeNode.cs │ │ │ ├── FieldTableTreeNode.cs │ │ │ ├── FileTableTreeNode.cs │ │ │ ├── GenericParamConstraintTableTreeNode.cs │ │ │ ├── GenericParamTableTreeNode.cs │ │ │ ├── ImplMapTableTreeNode.cs │ │ │ ├── InterfaceImplTableTreeNode.cs │ │ │ ├── ManifestResourceTableTreeNode.cs │ │ │ ├── MemberRefTableTreeNode.cs │ │ │ ├── MethodImplTableTreeNode.cs │ │ │ ├── MethodSemanticsTableTreeNode.cs │ │ │ ├── MethodSpecTableTreeNode.cs │ │ │ ├── MethodTableTreeNode.cs │ │ │ ├── ModuleRefTableTreeNode.cs │ │ │ ├── ModuleTableTreeNode.cs │ │ │ ├── NestedClassTableTreeNode.cs │ │ │ ├── ParamTableTreeNode.cs │ │ │ ├── PropertyMapTableTreeNode.cs │ │ │ ├── PropertyTableTreeNode.cs │ │ │ ├── PtrTableTreeNode.cs │ │ │ ├── StandAloneSigTableTreeNode.cs │ │ │ ├── TypeDefTableTreeNode.cs │ │ │ ├── TypeRefTableTreeNode.cs │ │ │ └── TypeSpecTableTreeNode.cs │ │ ├── DataDirectoriesTreeNode.cs │ │ ├── DebugDirectory/ │ │ │ ├── CodeViewTreeNode.cs │ │ │ ├── DebugDirectoryEntryTreeNode.cs │ │ │ └── PdbChecksumTreeNode.cs │ │ ├── DebugDirectoryTreeNode.cs │ │ ├── DebugMetadataTablesTreeNode.cs │ │ ├── DebugTables/ │ │ │ ├── CustomDebugInformationTableTreeNode.cs │ │ │ ├── DocumentTableTreeNode.cs │ │ │ ├── ImportScopeTableTreeNode.cs │ │ │ ├── LocalConstantTableTreeNode.cs │ │ │ ├── LocalScopeTableTreeNode.cs │ │ │ ├── LocalVariableTableTreeNode.cs │ │ │ ├── MethodDebugInformationTableTreeNode.cs │ │ │ └── StateMachineMethodTableTreeNode.cs │ │ ├── DosHeaderTreeNode.cs │ │ ├── FlagsFilterControl.xaml │ │ ├── FlagsFilterControl.xaml.cs │ │ ├── FlagsTooltip.xaml │ │ ├── FlagsTooltip.xaml.cs │ │ ├── GoToTokenCommand.cs │ │ ├── Heaps/ │ │ │ ├── BlobHeapTreeNode.cs │ │ │ ├── GuidHeapTreeNode.cs │ │ │ ├── StringHeapTreeNode.cs │ │ │ └── UserStringHeapTreeNode.cs │ │ ├── Helpers.cs │ │ ├── HexFilterControl.xaml │ │ ├── HexFilterControl.xaml.cs │ │ ├── MetaDataGrid.cs │ │ ├── MetadataHeapTreeNode.cs │ │ ├── MetadataProtocolHandler.cs │ │ ├── MetadataTableTreeNode.cs │ │ ├── MetadataTableViews.xaml │ │ ├── MetadataTableViews.xaml.cs │ │ ├── MetadataTablesTreeNode.cs │ │ ├── MetadataTreeNode.cs │ │ └── OptionalHeaderTreeNode.cs │ ├── NativeMethods.cs │ ├── NavigationHistory.cs │ ├── NavigationState.cs │ ├── Options/ │ │ ├── DecompilerSettingsPanel.xaml │ │ ├── DecompilerSettingsPanel.xaml.cs │ │ ├── DecompilerSettingsViewModel.cs │ │ ├── DisplaySettings.cs │ │ ├── DisplaySettingsPanel.xaml │ │ ├── DisplaySettingsPanel.xaml.cs │ │ ├── DisplaySettingsViewModel.cs │ │ ├── MiscSettings.cs │ │ ├── MiscSettingsPanel.xaml │ │ ├── MiscSettingsPanel.xaml.cs │ │ ├── MiscSettingsViewModel.cs │ │ ├── OptionsDialog.xaml │ │ ├── OptionsDialog.xaml.cs │ │ └── OptionsDialogViewModel.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── WPFAssemblyInfo.cs │ │ └── launchSettings.json │ ├── Search/ │ │ ├── SearchPane.xaml │ │ ├── SearchPane.xaml.cs │ │ ├── SearchPaneModel.cs │ │ └── SearchResultFactory.cs │ ├── SessionSettings.cs │ ├── SolutionWriter.cs │ ├── TaskHelper.cs │ ├── TextView/ │ │ ├── Asm-Mode.xshd │ │ ├── AvalonEditTextOutput.cs │ │ ├── BracketHighlightRenderer.cs │ │ ├── CSharp-Mode.xshd │ │ ├── CaretHighlightAdorner.cs │ │ ├── DecompilerTextEditor.cs │ │ ├── DecompilerTextView.cs │ │ ├── DecompilerTextView.xaml │ │ ├── DocumentationUIBuilder.cs │ │ ├── EditorCommands.cs │ │ ├── FoldingCommands.cs │ │ ├── ILAsm-Mode.xshd │ │ ├── OutputLengthExceededException.cs │ │ ├── ReferenceElementGenerator.cs │ │ ├── ThemeAwareHighlightingColorizer.cs │ │ ├── UIElementGenerator.cs │ │ ├── XML-Mode.xshd │ │ └── ZoomLevelToTextFormattingModeConverter.cs │ ├── Themes/ │ │ ├── Base.Dark.xaml │ │ ├── Base.Light.xaml │ │ ├── ResourceKeys.cs │ │ ├── SyntaxColor.cs │ │ ├── Theme.Dark.xaml │ │ ├── Theme.Light.xaml │ │ ├── Theme.RSharpDark.xaml │ │ ├── Theme.RSharpLight.xaml │ │ ├── Theme.VSCodeDarkPlus.xaml │ │ ├── Theme.VSCodeLightPlus.xaml │ │ ├── ThemeManager.cs │ │ ├── WindowStyleManagerBehavior.cs │ │ └── generic.xaml │ ├── TreeNodes/ │ │ ├── AssemblyListTreeNode.cs │ │ ├── AssemblyReferenceReferencedTypesTreeNode.cs │ │ ├── AssemblyReferenceTreeNode.cs │ │ ├── AssemblyTreeNode.cs │ │ ├── BaseTypesEntryNode.cs │ │ ├── BaseTypesTreeNode.cs │ │ ├── DerivedTypesEntryNode.cs │ │ ├── DerivedTypesTreeNode.cs │ │ ├── EventTreeNode.cs │ │ ├── ExportedTypeTreeNode.cs │ │ ├── FieldTreeNode.cs │ │ ├── FilterResult.cs │ │ ├── ILSpyTreeNode.cs │ │ ├── IMemberTreeNode.cs │ │ ├── MemberReferenceTreeNode.cs │ │ ├── MethodTreeNode.cs │ │ ├── ModuleReferenceTreeNode.cs │ │ ├── NamespaceTreeNode.cs │ │ ├── NaturalStringComparer.cs │ │ ├── PackageFolderTreeNode.cs │ │ ├── PropertyTreeNode.cs │ │ ├── ReferenceFolderTreeNode.cs │ │ ├── ResourceListTreeNode.cs │ │ ├── ResourceNodes/ │ │ │ ├── CursorResourceEntryNode.cs │ │ │ ├── IResourceNodeFactory.cs │ │ │ ├── IconResourceEntryNode.cs │ │ │ ├── ImageListResourceEntryNode.cs │ │ │ ├── ImageResourceEntryNode.cs │ │ │ ├── ResourceEntryNode.cs │ │ │ ├── ResourceTreeNode.cs │ │ │ ├── ResourcesFileTreeNode.cs │ │ │ ├── XamlResourceNode.cs │ │ │ └── XmlResourceNode.cs │ │ ├── ThreadingSupport.cs │ │ ├── TypeReferenceTreeNode.cs │ │ └── TypeTreeNode.cs │ ├── Updates/ │ │ ├── AppUpdateService.cs │ │ ├── AvailableVersionInfo.cs │ │ ├── UpdateService.cs │ │ └── UpdateSettings.cs │ ├── Util/ │ │ ├── GlobalUtils.cs │ │ ├── GraphVizGraph.cs │ │ ├── MessageBus.cs │ │ ├── ResourceHelper.cs │ │ ├── SettingsService.cs │ │ └── ShellHelper.cs │ ├── ViewModels/ │ │ ├── CompareViewModel.cs │ │ ├── DebugStepsPaneModel.cs │ │ ├── LegacyToolPaneModel.cs │ │ ├── ManageAssemblyListsViewModel.cs │ │ ├── PaneModel.cs │ │ ├── TabPageModel.cs │ │ ├── ToolPaneModel.cs │ │ └── UpdatePanelViewModel.cs │ ├── Views/ │ │ ├── CompareView.xaml │ │ ├── CompareView.xaml.cs │ │ ├── CreateListDialog.xaml │ │ ├── CreateListDialog.xaml.cs │ │ ├── DebugSteps.xaml │ │ ├── DebugSteps.xaml.cs │ │ ├── ManageAssemblyLIstsDialog.xaml.cs │ │ ├── ManageAssemblyListsDialog.xaml │ │ ├── OpenFromGacDialog.xaml │ │ ├── OpenFromGacDialog.xaml.cs │ │ ├── UpdatePanel.xaml │ │ └── UpdatePanel.xaml.cs │ └── app.manifest ├── ILSpy.AddIn/ │ ├── Decompiler/ │ │ └── Dummy.cs │ ├── ILSpy.AddIn.csproj │ ├── ILSpyAddIn.en-US.vsct │ ├── ILSpyAddIn.vsct │ ├── Key.snk │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── README.md │ └── source.extension.vsixmanifest.template ├── ILSpy.AddIn.Shared/ │ ├── AssemblyFileFinder.cs │ ├── Commands/ │ │ ├── AssemblyReferenceForILSpy.cs │ │ ├── NuGetReferenceForILSpy.cs │ │ ├── OpenCodeItemCommand.cs │ │ ├── OpenILSpyCommand.cs │ │ ├── OpenProjectOutputCommand.cs │ │ ├── OpenReferenceCommand.cs │ │ ├── ProjectItemForILSpy.cs │ │ └── ProjectReferenceForILSpy.cs │ ├── GlobalSuppressions.cs │ ├── Guids.cs │ ├── ILSpy.AddIn.Shared.projitems │ ├── ILSpy.AddIn.Shared.shproj │ ├── ILSpyAddInPackage.cs │ ├── ILSpyInstance.cs │ ├── PkgCmdID.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── SyntaxNodeExtensions.cs │ ├── Utils.cs │ ├── VSPackage.en-US.resx │ └── VSPackage.resx ├── ILSpy.AddIn.VS2022/ │ ├── Decompiler/ │ │ └── Dummy.cs │ ├── ILSpy.AddIn.VS2022.csproj │ ├── ILSpyAddIn.en-US.vsct │ ├── ILSpyAddIn.vsct │ ├── Key.snk │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── README.md │ └── source.extension.vsixmanifest.template ├── ILSpy.BamlDecompiler/ │ ├── BamlResourceEntryNode.cs │ ├── BamlResourceNodeFactory.cs │ ├── ILSpy.BamlDecompiler.csproj │ └── Properties/ │ ├── AssemblyInfo.cs │ └── launchSettings.json ├── ILSpy.BamlDecompiler.Tests/ │ ├── BamlTestRunner.cs │ ├── Cases/ │ │ ├── AttachedEvent.xaml │ │ ├── AttachedEvent.xaml.cs │ │ ├── AvalonDockBrushes.xaml │ │ ├── AvalonDockCommon.xaml │ │ ├── CustomControl.cs │ │ ├── Dictionary1.xaml │ │ ├── EscapeSequence.xaml │ │ ├── Issue1435.xaml │ │ ├── Issue1546.xaml │ │ ├── Issue1547.xaml │ │ ├── Issue1547.xaml.cs │ │ ├── Issue2052.xaml │ │ ├── Issue2097.xaml │ │ ├── Issue2097.xaml.cs │ │ ├── Issue2116.xaml │ │ ├── Issue2116.xaml.cs │ │ ├── Issue3318.xaml │ │ ├── Issue3318.xaml.cs │ │ ├── Issue445.xaml │ │ ├── Issue775.xaml │ │ ├── MarkupExtension.xaml │ │ ├── MyControl.xaml │ │ ├── MyControl.xaml.cs │ │ ├── NamespacePrefix.xaml │ │ ├── ReadonlyProperty.xaml │ │ ├── ReadonlyProperty.xaml.cs │ │ ├── Resources.xaml │ │ ├── Resources.xaml.cs │ │ ├── Simple.xaml │ │ ├── Simple.xaml.cs │ │ ├── SimpleDictionary.xaml │ │ ├── SimpleNames.xaml │ │ ├── SimpleNames.xaml.cs │ │ └── SimplePropertyElement.xaml │ ├── ILSpy.BamlDecompiler.Tests.csproj │ └── Mocks/ │ └── AvalonDock.cs ├── ILSpy.Installer/ │ ├── AppPackage.cs │ ├── ILSpy.Installer.csproj │ ├── README.md │ ├── setup.cs │ └── winui.wxl ├── ILSpy.Installer.sln ├── ILSpy.ReadyToRun/ │ ├── ILSpy.ReadyToRun.csproj │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── launchSettings.json │ ├── ReadyToRunDisassembler.cs │ ├── ReadyToRunLanguage.cs │ ├── ReadyToRunOptionPage.xaml │ ├── ReadyToRunOptionPage.xaml.cs │ └── ReadyToRunOptions.cs ├── ILSpy.Tests/ │ ├── Analyzers/ │ │ ├── AnalyzerScopeTests.cs │ │ ├── ExportAnalyzerAttributeTests.cs │ │ ├── MemberImplementsInterfaceAnalyzerTests.cs │ │ ├── MethodUsesAnalyzerTests.cs │ │ ├── TestCases/ │ │ │ └── MainAssembly.cs │ │ └── TypeUsedByAnalyzerTests.cs │ ├── BitmapContainer.resources │ ├── CommandLineArgumentsTests.cs │ ├── ILSpy.Tests.csproj │ ├── IconContainer.resx │ ├── ResourceReaderWriterTests.cs │ └── Test.resources ├── ILSpy.VSExtensions.sln ├── ILSpy.Wpf.slnf ├── ILSpy.XPlat.slnf ├── ILSpy.sln ├── LICENSE ├── NuGet.config ├── README.md ├── SECURITY.md ├── TestPlugin/ │ ├── AboutPageAddition.cs │ ├── ContextMenuCommand.cs │ ├── CustomLanguage.cs │ ├── CustomOptionPage.xaml │ ├── CustomOptionPage.xaml.cs │ ├── MainMenuCommand.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── Readme.txt │ └── TestPlugin.csproj ├── clean.bat ├── debugbuild.bat ├── decompiler-nuget-demos.ipynb ├── doc/ │ ├── Command Line.txt │ ├── ILAst Pattern Matching.md │ ├── ILAst.txt │ ├── ILSpyAboutPage.txt │ ├── IntPtr.txt │ ├── Pattern Matching.html │ ├── README.md │ ├── Resources.txt │ ├── copyright.txt │ └── third-party-notices.txt ├── global.json ├── publish.ps1 ├── publishlocaldev.ps1 ├── releasebuild.bat └── updatedeps.bat ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ; Top-most EditorConfig file root = true [*] charset = utf-8 # standardize on no BOM (except resx, see below) indent_style = tab indent_size = 4 guidelines = 110 tab_width = 4 end_of_line = crlf # .net tooling writes the BOM to resx files on running dotnet build ILSpy.sln regardless, # so we should direct text editors to NOT change the file [*.resx] charset = utf-8-bom [*.il] indent_style = space indent_size = 2 [*.{yml,yaml}] indent_style = space indent_size = 2 [*.{csproj,props}] indent_style = space indent_size = 2 [*.config] indent_style = space indent_size = 2 [*.nuspec] indent_style = space indent_size = 2 [*.vsixmanifest] indent_style = space indent_size = 2 [*.vsct] indent_style = space indent_size = 2 [*.cs] # New line preferences csharp_new_line_before_open_brace = methods, types, control_blocks, local_functions csharp_new_line_before_else = true csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_members_in_anonymous_types = false csharp_new_line_within_query_expression_clauses = false # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = false csharp_indent_switch_labels = true csharp_indent_labels = no_change # Avoid 'this.' in generated code unless absolutely necessary, but allow developers to use it dotnet_style_qualification_for_field = false:silent dotnet_style_qualification_for_property = false:silent dotnet_style_qualification_for_method = false:silent dotnet_style_qualification_for_event = false:silent # Do not use 'var' when generating code, but allow developers to use it csharp_style_var_for_built_in_types = false:silent csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = false:silent # Use language keywords instead of BCL types when generating code, but allow developers to use either dotnet_style_predefined_type_for_locals_parameters_members = true:silent dotnet_style_predefined_type_for_member_access = true:silent # Using directives dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = true # Wrapping csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = false # Code style csharp_prefer_braces = true:silent # Expression-level preferences dotnet_style_object_initializer = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_coalesce_expression = true:suggestion dotnet_style_null_propagation = true:suggestion # Expression-bodied members csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_accessors = true:silent # Pattern matching csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion # Null checking preferences csharp_style_throw_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion # Space preferences csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Naming rules # Naming styles: dotnet_naming_style.lower_camel_case_style.capitalization = camel_case dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case # Symbol groups: # all non-private fields dotnet_naming_symbols.fields_symbols.applicable_accessibilities = * dotnet_naming_symbols.fields_symbols.applicable_kinds = field # all private/protected fields except constants dotnet_naming_symbols.private_fields_symbols.applicable_accessibilities = private,protected,private_protected dotnet_naming_symbols.private_fields_symbols.applicable_kinds = field # Naming rules: # all non-private fields are pascal case dotnet_naming_rule.fields_rule.severity = warning dotnet_naming_rule.fields_rule.style = upper_camel_case_style dotnet_naming_rule.fields_rule.symbols = fields_symbols # all private fields are camel case dotnet_naming_rule.private_fields_rule.severity = warning dotnet_naming_rule.private_fields_rule.style = lower_camel_case_style dotnet_naming_rule.private_fields_rule.symbols = private_fields_symbols # General settings: csharp_using_directive_placement = outside_namespace:silent csharp_prefer_simple_using_statement = true:suggestion csharp_style_namespace_declarations = block_scoped:silent csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent csharp_style_prefer_primary_constructors = true:suggestion csharp_prefer_system_threading_lock = true:suggestion csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent csharp_style_prefer_null_check_over_type_check = true:suggestion csharp_prefer_simple_default_expression = true:suggestion csharp_style_prefer_local_over_anonymous_function = true:suggestion csharp_style_prefer_index_operator = true:suggestion csharp_style_prefer_range_operator = true:suggestion csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion csharp_style_prefer_tuple_swap = true:suggestion csharp_style_prefer_utf8_string_literals = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion csharp_prefer_static_local_function = true:suggestion csharp_prefer_static_anonymous_function = true:suggestion csharp_style_prefer_readonly_struct = true:suggestion csharp_style_prefer_readonly_struct_member = true:suggestion csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent csharp_style_prefer_switch_expression = true:suggestion csharp_style_prefer_pattern_matching = true:silent csharp_style_prefer_not_pattern = true:suggestion csharp_style_prefer_extended_property_pattern = true:suggestion dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_auto_properties = true:silent dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_compound_assignment = true:suggestion dotnet_style_prefer_simplified_interpolation = true:suggestion dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion dotnet_style_namespace_match_folder = true:suggestion dotnet_style_readonly_field = true:suggestion dotnet_style_require_accessibility_modifiers = omit_if_default:suggestion dotnet_style_allow_multiple_blank_lines_experimental = false:warning dotnet_style_allow_statement_immediately_after_block_experimental = true:silent dotnet_code_quality_unused_parameters = all:suggestion dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent # Errors and warnings dotnet_diagnostic.IDE0007.severity = none dotnet_diagnostic.CA1001.severity = warning dotnet_diagnostic.CA1009.severity = warning dotnet_diagnostic.CA1016.severity = warning #dotnet_diagnostic.CA1033.severity = warning # Disabled because currently, there are too many violations dotnet_diagnostic.CA1049.severity = warning dotnet_diagnostic.CA1060.severity = warning dotnet_diagnostic.CA1061.severity = warning dotnet_diagnostic.CA1063.severity = warning dotnet_diagnostic.CA1065.severity = warning dotnet_diagnostic.CA1301.severity = warning dotnet_diagnostic.CA1400.severity = warning dotnet_diagnostic.CA1401.severity = warning dotnet_diagnostic.CA1403.severity = warning dotnet_diagnostic.CA1404.severity = warning dotnet_diagnostic.CA1405.severity = warning dotnet_diagnostic.CA1410.severity = warning dotnet_diagnostic.CA1415.severity = warning dotnet_diagnostic.CA1821.severity = warning dotnet_diagnostic.CA1900.severity = warning dotnet_diagnostic.CA1901.severity = warning dotnet_diagnostic.CA2002.severity = warning dotnet_diagnostic.CA2100.severity = warning dotnet_diagnostic.CA2101.severity = warning dotnet_diagnostic.CA2108.severity = warning dotnet_diagnostic.CA2111.severity = warning dotnet_diagnostic.CA2112.severity = warning dotnet_diagnostic.CA2114.severity = warning dotnet_diagnostic.CA2116.severity = warning dotnet_diagnostic.CA2117.severity = warning dotnet_diagnostic.CA2122.severity = warning dotnet_diagnostic.CA2123.severity = warning dotnet_diagnostic.CA2124.severity = warning dotnet_diagnostic.CA2126.severity = warning dotnet_diagnostic.CA2131.severity = warning dotnet_diagnostic.CA2132.severity = warning dotnet_diagnostic.CA2133.severity = warning dotnet_diagnostic.CA2134.severity = warning dotnet_diagnostic.CA2137.severity = warning dotnet_diagnostic.CA2138.severity = warning dotnet_diagnostic.CA2140.severity = warning dotnet_diagnostic.CA2141.severity = warning dotnet_diagnostic.CA2146.severity = warning dotnet_diagnostic.CA2147.severity = warning dotnet_diagnostic.CA2149.severity = warning dotnet_diagnostic.CA2200.severity = warning dotnet_diagnostic.CA2202.severity = warning dotnet_diagnostic.CA2207.severity = warning dotnet_diagnostic.CA2212.severity = warning dotnet_diagnostic.CA2213.severity = warning dotnet_diagnostic.CA2214.severity = warning dotnet_diagnostic.CA2216.severity = warning dotnet_diagnostic.CA2220.severity = warning dotnet_diagnostic.CA2229.severity = warning dotnet_diagnostic.CA2231.severity = warning dotnet_diagnostic.CA2232.severity = warning dotnet_diagnostic.CA2235.severity = warning dotnet_diagnostic.CA2236.severity = warning dotnet_diagnostic.CA2237.severity = warning dotnet_diagnostic.CA2238.severity = warning dotnet_diagnostic.CA2240.severity = warning dotnet_diagnostic.CA2241.severity = warning dotnet_diagnostic.CA2242.severity = warning # MEF006: No importing constructor dotnet_diagnostic.MEF006.severity = silent ================================================ FILE: .git-blame-ignore-revs ================================================ # https://github.com/icsharpcode/ILSpy/issues/2128 0d9f871a4f7c478fdf3f7b699d74e77506e978a4 ================================================ FILE: .gitattributes ================================================ * text=auto *.cs text diff=csharp *.sln text eol=crlf *.csproj text eol=crlf ================================================ FILE: .github/FUNDING.yml ================================================ github: [dgrunwald, siegfriedpammer, christophwille] ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: General bug report about: Error dialog, crash, or anything else unrelated to the decompiled code title: '' labels: Bug assignees: '' --- ### Steps to reproduce 1. 2. 3. ### Error message shown ``` Copy and paste any error messages / relevant text and/or screenshot the incorrect behaviour. ``` ### Details * Product in use: e.g. ILSpy / ICSharpCode.Decompiler nuget package / VS extension * Version in use: e.g. 6.0.0 or a commit hash (use Help>About to see which ILSpy version you are using) * Any other relevant information to the issue, or your interest in contributing a fix. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: Enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is and why it's important to you. Ex. I'm always frustrated when [...] Any (even unsuccessful) ways in which you've tried to workaround the problem currently **Describe the solution you'd like** A clear and concise description of what you want to happen. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/wrong_decompilation.md ================================================ --- name: Wrong decompilation about: Decompiled code doesn't compile, or behaves differently to the original IL title: '' labels: Bug, Decompiler assignees: '' --- ### Input code Please provide the input that failed to decompile. If providing IL code as input, please provide enough context to allow us to re-assemble the IL. If uploading a complete assembly, please mention which method failed to decompile. ### Erroneous output ```c# If relevant, the incorrectly decompiled output, or an exception with stack trace. ``` If the output fails to re-compile, provide the compiler error message. If the output has the wrong behavior, explain how it differs from the expected behavior. ### Details * Product in use: e.g. ILSpy / ICSharpCode.Decompiler nuget package / VS extension * Version in use: e.g. 6.0.0 or a commit hash (use Help>About to see which ILSpy version you are using) * Any other relevant information to the issue, or your interest in contributing a fix. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Link to issue(s) this covers ### Problem Link to, or brief information about the issue ### Solution * Any comments on the approach taken, its consistency with surrounding code, etc. * Which part of this PR is most in need of attention/improvement? * [ ] At least one test covering the code changed ================================================ FILE: .github/dependabot.yml ================================================ # Set update schedule for GitHub Actions version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday interval: "weekly" ================================================ FILE: .github/workflows/build-frontends.yml ================================================ name: Build XPlat Frontends on: push: branches: '**' pull_request: branches: [ master, release/** ] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' dotnet-quality: 'preview' - name: Install dependencies run: dotnet restore ILSpy.XPlat.slnf -p:RestoreEnablePackagePruning=false - name: Build Debug run: dotnet msbuild ILSpy.XPlat.slnf -p:Configuration=Debug -bl:Debug.binlog - name: Build Release run: dotnet msbuild ILSpy.XPlat.slnf -p:Configuration=Release -bl:Release.binlog - name: list files if: always() run: ls -la - name: print VERSION if: always() run: cat VERSION - name: Upload binlog if: always() uses: actions/upload-artifact@v7 with: name: binlog path: '**/*.binlog' if-no-files-found: error ================================================ FILE: .github/workflows/build-ilspy.yml ================================================ name: Build ILSpy on: push: branches: '**' pull_request: branches: [ master, release/** ] permissions: contents: read jobs: Build: permissions: packages: write # for dotnet nuget push runs-on: windows-2025 strategy: fail-fast: false matrix: Configuration: [ Debug, Release ] env: BuildPlatform: Any CPU StagingDirectory: buildartifacts steps: - run: mkdir -p $env:StagingDirectory - uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 persist-credentials: false - uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' dotnet-quality: 'preview' env: DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet DOTNET_ROOT: ${{ runner.temp }}/.dotnet - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Install dotnet-format env: DOTNET_FORMAT_VERSION: 10.0.100-rtm.25531.102 DOTNET_FORMAT_SOURCE: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json run: dotnet tool install -g dotnet-format --version "${{env.DOTNET_FORMAT_VERSION}}" --add-source "${{env.DOTNET_FORMAT_SOURCE}}" - name: Install wix (locked version) run: dotnet tool install --global wix --version 6.0.2 - name: Install wix extesnion (locked version) run: wix.exe extension add -g WixToolset.UI.wixext/6.0.2 - name: Get Version id: version shell: pwsh run: | .\BuildTools\ghactions-install.ps1 Get-ChildItem Env: | Where-Object {$_.Name -Match "^ILSPY_"} | %{ echo "$($_.Name)=$($_.Value)" } | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append - name: Restore the application run: msbuild ILSpy.sln /t:Restore /p:RestoreEnablePackagePruning=false /p:Configuration=${{ matrix.configuration }} /p:Platform=$env:BuildPlatform - name: Build run: msbuild ILSpy.sln /p:Configuration=${{ matrix.configuration }} /p:Platform=$env:BuildPlatform /m - name: Format check run: dotnet-format whitespace --verify-no-changes --verbosity detailed ILSpy.sln - name: Execute unit tests run: dotnet test --solution ilspy.sln --configuration ${{ matrix.configuration }} --no-build --report-trx --results-directory test-results/${{ matrix.configuration }} - name: Upload Test Logs uses: actions/upload-artifact@v7 if: success() || failure() with: name: test-results-${{ matrix.configuration }} path: 'test-results/${{ matrix.configuration }}/*.trx' - name: Create Test Report uses: icsharpcode/test-summary-action@dist if: always() with: paths: "test-results/${{ matrix.configuration }}/*.trx" folded: true - name: Verify package contents if: matrix.configuration == 'debug' shell: pwsh run: | .\BuildTools\create-filelists.ps1 git diff --exit-code - name: Zip ILSpy (framework-dependent) run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries.zip .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*.dll .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*.exe .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*.config .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*.json .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*\ILSpy.resources.dll .\ILSpy\bin\${{ matrix.configuration }}\net10.0-windows\*\ILSpy.ReadyToRun.Plugin.resources.dll - name: Publish x64/arm64 framework-dependent/self-contained shell: pwsh run: .\publish.ps1 - name: Zip ILSpy Release (x64 self-contained) if: matrix.configuration == 'release' run: 7z a -tzip $env:StagingDirectory\ILSpy_selfcontained_x64.zip .\ILSpy\bin\Release\net10.0-windows\win-x64\publish\selfcontained\* - name: Zip ILSpy Release (arm64 framework-dependent) if: matrix.configuration == 'release' run: 7z a -tzip $env:StagingDirectory\ILSpy_binaries_arm64.zip .\ILSpy\bin\Release\net10.0-windows\win-arm64\publish\fwdependent\* - name: Pack NuGets if: matrix.configuration == 'release' run: | dotnet pack ICSharpCode.Decompiler --no-restore dotnet pack ICSharpCode.BamlDecompiler --no-restore dotnet pack ICSharpCode.ILSpyX --no-restore - name: Build Installer (x64 and arm64, framework-dependent) if: matrix.configuration == 'release' run: | msbuild ILSpy.Installer.sln /t:Restore /p:Configuration="Release" /p:Platform="Any CPU" msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" msbuild ILSpy.Installer.sln /p:Configuration="Release" /p:Platform="Any CPU" /p:PlatformForInstaller="ARM64" - name: Build VS Extensions (for 2017-2019 and 2022) if: matrix.configuration == 'release' run: | msbuild ILSpy.VSExtensions.sln /t:Restore /p:Configuration="Release" /p:Platform="Any CPU" msbuild ILSpy.VSExtensions.sln /p:Configuration="Release" /p:Platform="Any CPU" # https://github.com/actions/upload-artifact - name: Upload VSIX (VS 2019) release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy VS Addin for VS 2017-2019 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ILSpy.AddIn\bin\${{ matrix.configuration }}\net472\*.vsix if-no-files-found: error - name: Upload VSIX (VS 2022) release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy VS Addin for VS 2022 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ILSpy.AddIn.VS2022\bin\${{ matrix.configuration }}\net472\*.vsix if-no-files-found: error - name: Upload Decompiler NuGet release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ICSharpCode.Decompiler NuGet Package (${{ matrix.configuration }}) path: ICSharpCode.Decompiler\bin\Release\ICSharpCode.Decompiler*.nupkg if-no-files-found: error - name: Publish Decompiler NuGet if: github.ref == 'refs/heads/master' && matrix.configuration == 'release' run: | dotnet nuget push "ICSharpCode.Decompiler\bin\Release\ICSharpCode.Decompiler*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }} - name: Upload ILSpyX NuGet release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ICSharpCode.ILSpyX NuGet Package (${{ matrix.configuration }}) path: ICSharpCode.ILSpyX\bin\Release\ICSharpCode.ILSpyX*.nupkg if-no-files-found: error - name: Publish ILSpyX NuGet if: github.ref == 'refs/heads/master' && matrix.configuration == 'release' run: | dotnet nuget push "ICSharpCode.ILSpyX\bin\Release\ICSharpCode.ILSpyX*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }} - name: Upload BamlDecompiler NuGet release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ICSharpCode.BamlDecompiler NuGet Package (${{ matrix.configuration }}) path: ICSharpCode.BamlDecompiler\bin\Release\ICSharpCode.BamlDecompiler*.nupkg if-no-files-found: error - name: Publish DecomBamlDecompilerpiler NuGet if: github.ref == 'refs/heads/master' && matrix.configuration == 'release' run: | dotnet nuget push "ICSharpCode.BamlDecompiler\bin\Release\ICSharpCode.BamlDecompiler*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }} - name: Upload zip binaries build artifacts uses: actions/upload-artifact@v7 with: name: ILSpy ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ${{ env.StagingDirectory }}\ILSpy_binaries.zip if-no-files-found: error - name: Upload x64 self-contained zip (Release-only) if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy self-contained x64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ${{ env.StagingDirectory }}\ILSpy_selfcontained_x64.zip if-no-files-found: error - name: Upload arm64 framework-dependent zip (Release-only) if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy arm64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ${{ env.StagingDirectory }}\ILSpy_binaries_arm64.zip if-no-files-found: error - name: Upload x64 installer artifact if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy Installer x64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ILSpy.Installer\wix\*-x64.msi if-no-files-found: error - name: Upload arm64 installer artifact if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ILSpy Installer arm64 ${{ steps.version.outputs.ILSPY_VERSION_NUMBER }} (${{ matrix.configuration }}) path: ILSpy.Installer\wix\*-arm64.msi if-no-files-found: error - name: Upload ilspycmd release build artifacts if: matrix.configuration == 'release' uses: actions/upload-artifact@v7 with: name: ilspycmd dotnet tool (${{ matrix.configuration }}) path: ICSharpCode.ILSpyCmd\bin\Release\ilspycmd*.nupkg if-no-files-found: error ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ name: "CodeQL" on: push: branches: '**' pull_request: branches: [ master, release/** ] permissions: contents: read jobs: analyze: permissions: actions: read # for github/codeql-action/init to get workflow details security-events: write # for github/codeql-action/analyze to upload SARIF results name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'csharp' ] steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' dotnet-quality: 'preview' - name: Build run: dotnet build ILSpy.XPlat.slnf --configuration Release -p:RestoreEnablePackagePruning=false - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 ================================================ FILE: .github/workflows/generate-bom.yml ================================================ name: Generate BOM on: workflow_dispatch: permissions: contents: read jobs: build: runs-on: windows-2022 steps: - name: Checkout run: git config --global core.autocrlf true - uses: actions/checkout@v6 with: submodules: true persist-credentials: false - name: Install CycloneDX run: dotnet tool install --global CycloneDX - name: Analyze run: dotnet-CycloneDX ILSpy/ILSpy.csproj --output sbom --recursive --exclude-dev --exclude-test-projects - name: Upload BOM uses: actions/upload-artifact@v7 with: name: ILSpyBOM.xml path: sbom/bom.xml if-no-files-found: error ================================================ FILE: .github/workflows/lock.yml ================================================ name: 'Lock threads' on: schedule: - cron: '0 0 * * *' permissions: contents: read jobs: lock: permissions: issues: write # for dessant/lock-threads to lock issues runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v6.0.0 with: github-token: ${{ github.token }} issue-inactive-days: '90' issue-lock-reason: 'resolved' process-only: 'issues' ================================================ FILE: .github/workflows/scorecard.yml ================================================ name: Scorecard supply-chain security on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection (disabled) # branch_protection_rule: workflow_dispatch: # schedule ("Maintained") and push are disabled atm # schedule: # - cron: '25 1 * * 2' # push: # branches: [ "master" ] permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: security-events: write # Needed to upload the results to code-scanning dashboard. id-token: write # Needed to publish results and get a badge (see publish_results below). steps: - name: "Checkout code" uses: actions/checkout@v6 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@v2.4.3 # https://github.com/marketplace/actions/ossf-scorecard-action with: results_file: results.sarif results_format: sarif publish_results: true - name: "Upload artifact" uses: actions/upload-artifact@v7 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif ================================================ FILE: .gitignore ================================================ bin/ obj/ AppPackages/ BundleArtifacts/ *.user /Resharper-ILSpy-Style.xml _ReSharper*/ *.ReSharper *.patch .vs/ .idea/ /ILSpy.AddIn*/Packages/* /ILSpy.AddIn*/source.extension.vsixmanifest /ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/*.dll /ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/*.result.il /ICSharpCode.Decompiler.Tests/TestCases/Correctness/*.exe /ICSharpCode.Decompiler.Tests/TestCases/Correctness/*.exe.config multitargeting.props ILSpy.Installer/wix/ /VERSION /ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.cs **/.vscode/ DecompilerTests.config.json *.trx ================================================ FILE: .gitmodules ================================================ [submodule "ILSpy-tests"] path = ILSpy-tests url = https://github.com/icsharpcode/ILSpy-tests branch = master ================================================ FILE: .tgitconfig ================================================ [tgit] icon = ILSpy/Images/ILSpy.ico ================================================ FILE: BuildTools/ILSpy.AddIn.VS2022.vsix.filelist ================================================ [Content_Types].xml catalog.json en-US\ILSpy.AddIn.VS2022.resources.dll extension.vsixmanifest ICSharpCode.Decompiler.dll ICSharpCode.ILSpyX.dll ICSharpCode.TreeView.dll ILSpy.AddIn.VS2022.dll ILSpy.AddIn.VS2022.pkgdef ILSpy.BamlDecompiler.Plugin.dll ILSpy.deps.json ILSpy.dll ILSpy.exe ILSpy.ReadyToRun.Plugin.dll ILSpy.runtimeconfig.json ILSpy\AvalonDock.dll ILSpy\AvalonDock.Themes.VS2013.dll ILSpy\DataGridExtensions.dll ILSpy\Iced.dll ILSpy\ICSharpCode.AvalonEdit.dll ILSpy\ICSharpCode.Decompiler.dll ILSpy\ICSharpCode.ILSpyX.dll ILSpy\ICSharpCode.TreeView.dll ILSpy\ILCompiler.Reflection.ReadyToRun.dll ILSpy\ILSpy.BamlDecompiler.Plugin.deps.json ILSpy\ILSpy.BamlDecompiler.Plugin.dll ILSpy\ILSpy.deps.json ILSpy\ILSpy.dll ILSpy\ILSpy.exe ILSpy\ILSpy.ReadyToRun.Plugin.deps.json ILSpy\ILSpy.ReadyToRun.Plugin.dll ILSpy\ILSpy.runtimeconfig.json ILSpy\K4os.Compression.LZ4.dll ILSpy\Microsoft.DiaSymReader.Converter.Xml.dll ILSpy\Microsoft.DiaSymReader.dll ILSpy\Microsoft.DiaSymReader.PortablePdb.dll ILSpy\Microsoft.VisualStudio.Composition.dll ILSpy\Microsoft.VisualStudio.Composition.NetFxAttributes.dll ILSpy\Microsoft.VisualStudio.Validation.dll ILSpy\Microsoft.Xaml.Behaviors.dll ILSpy\Mono.Cecil.dll ILSpy\Mono.Cecil.Mdb.dll ILSpy\Mono.Cecil.Pdb.dll ILSpy\Mono.Cecil.Rocks.dll ILSpy\System.ComponentModel.Composition.dll ILSpy\System.Composition.AttributedModel.dll ILSpy\System.Composition.Convention.dll ILSpy\System.Composition.Hosting.dll ILSpy\System.Composition.Runtime.dll ILSpy\System.Composition.TypedParts.dll ILSpy\TomsToolbox.Essentials.dll ILSpy\TomsToolbox.ObservableCollections.dll ILSpy\TomsToolbox.Wpf.dll ILSpy\TomsToolbox.Wpf.Styles.dll ILSpy-Large.ico license.txt manifest.json Mono.Cecil.dll ================================================ FILE: BuildTools/ILSpy.AddIn.vsix.filelist ================================================ [Content_Types].xml catalog.json en-US\ILSpy.AddIn.resources.dll extension.vsixmanifest ICSharpCode.Decompiler.dll ICSharpCode.ILSpyX.dll ICSharpCode.TreeView.dll ILSpy.AddIn.dll ILSpy.AddIn.pkgdef ILSpy.BamlDecompiler.Plugin.dll ILSpy.deps.json ILSpy.dll ILSpy.exe ILSpy.ReadyToRun.Plugin.dll ILSpy.runtimeconfig.json ILSpy\AvalonDock.dll ILSpy\AvalonDock.Themes.VS2013.dll ILSpy\DataGridExtensions.dll ILSpy\Iced.dll ILSpy\ICSharpCode.AvalonEdit.dll ILSpy\ICSharpCode.Decompiler.dll ILSpy\ICSharpCode.ILSpyX.dll ILSpy\ICSharpCode.TreeView.dll ILSpy\ILCompiler.Reflection.ReadyToRun.dll ILSpy\ILSpy.BamlDecompiler.Plugin.deps.json ILSpy\ILSpy.BamlDecompiler.Plugin.dll ILSpy\ILSpy.deps.json ILSpy\ILSpy.dll ILSpy\ILSpy.exe ILSpy\ILSpy.ReadyToRun.Plugin.deps.json ILSpy\ILSpy.ReadyToRun.Plugin.dll ILSpy\ILSpy.runtimeconfig.json ILSpy\K4os.Compression.LZ4.dll ILSpy\Microsoft.DiaSymReader.Converter.Xml.dll ILSpy\Microsoft.DiaSymReader.dll ILSpy\Microsoft.DiaSymReader.PortablePdb.dll ILSpy\Microsoft.VisualStudio.Composition.dll ILSpy\Microsoft.VisualStudio.Composition.NetFxAttributes.dll ILSpy\Microsoft.VisualStudio.Validation.dll ILSpy\Microsoft.Xaml.Behaviors.dll ILSpy\Mono.Cecil.dll ILSpy\Mono.Cecil.Mdb.dll ILSpy\Mono.Cecil.Pdb.dll ILSpy\Mono.Cecil.Rocks.dll ILSpy\System.ComponentModel.Composition.dll ILSpy\System.Composition.AttributedModel.dll ILSpy\System.Composition.Convention.dll ILSpy\System.Composition.Hosting.dll ILSpy\System.Composition.Runtime.dll ILSpy\System.Composition.TypedParts.dll ILSpy\TomsToolbox.Essentials.dll ILSpy\TomsToolbox.ObservableCollections.dll ILSpy\TomsToolbox.Wpf.dll ILSpy\TomsToolbox.Wpf.Styles.dll ILSpy-Large.ico license.txt manifest.json Mono.Cecil.dll ================================================ FILE: BuildTools/ILSpy.msi.filelist ================================================ AvalonDock.dll_2384574099 AvalonDock.Themes.VS2013.dll_1693991188 DataGridExtensions.dll_3175011569 Iced.dll_3795180016 ICSharpCode.AvalonEdit.dll_2202357673 ICSharpCode.Decompiler.dll_1302002478 ICSharpCode.ILSpyX.dll_1982182100 ICSharpCode.TreeView.dll_128291808 ILCompiler.Reflection.ReadyToRun.dll_961256598 ILSpy.BamlDecompiler.Plugin.deps.json_425274809 ILSpy.BamlDecompiler.Plugin.dll_1186184332 ILSpy.deps.json_14220630 ILSpy.dll_828718029 ILSpy.exe_2512527281 ILSpy.ReadyToRun.Plugin.deps.json_3602879258 ILSpy.ReadyToRun.Plugin.dll_3493023742 ILSpy.ReadyToRun.Plugin.resources.dll_3072497650 ILSpy.resources.dll_1963394151 ILSpy.runtimeconfig.json_4170109836 K4os.Compression.LZ4.dll_841780051 Microsoft.DiaSymReader.Converter.Xml.dll_1504543009 Microsoft.DiaSymReader.dll_2664270437 Microsoft.DiaSymReader.PortablePdb.dll_645824024 Microsoft.VisualStudio.Composition.dll_3669554833 Microsoft.VisualStudio.Composition.NetFxAttributes.dll_4085550965 Microsoft.VisualStudio.Validation.dll_3370943218 Microsoft.Xaml.Behaviors.dll_3088279542 Mono.Cecil.dll_2043069113 Mono.Cecil.Mdb.dll_881308441 Mono.Cecil.Pdb.dll_94684032 Mono.Cecil.Rocks.dll_284129705 System.ComponentModel.Composition.dll_3956562358 System.Composition.AttributedModel.dll_2731655713 System.Composition.Convention.dll_1880631244 System.Composition.Hosting.dll_1479266732 System.Composition.Runtime.dll_4043919224 System.Composition.TypedParts.dll_1680333710 TomsToolbox.Essentials.dll_1205939788 TomsToolbox.ObservableCollections.dll_75279310 TomsToolbox.Wpf.dll_28003915 TomsToolbox.Wpf.Styles.dll_1483228554 ================================================ FILE: BuildTools/bom-classify-encodings.ps1 ================================================ <# .SYNOPSIS Classify text files by encoding under the current subtree, respecting .gitignore. .DESCRIPTION Enumerates tracked files and untracked-but-not-ignored files (via Git) beneath PWD. Skips likely-binary files (NUL probe). Classifies remaining files as: - 'utf8' : valid UTF-8 (no BOM) or empty file - 'utf8-with-bom' : starts with UTF-8 BOM (EF BB BF) - 'other' : text but not valid UTF-8 (e.g., UTF-16/ANSI) Outputs: 1) Relative paths of files classified as 'other' 2) A table by extension: UTF8 / UTF8-with-BOM / Other / Total Notes: - Read-only: this script makes no changes. - Requires Git and must be run inside a Git work tree. #> [CmdletBinding()] param() Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # --- Git enumeration --------------------------------------------------------- function Assert-InGitWorkTree { # Throws if not inside a Git work tree. $inside = (& git rev-parse --is-inside-work-tree 2>$null).Trim() if ($LASTEXITCODE -ne 0 -or $inside -ne 'true') { throw 'Not in a Git work tree.' } } function Get-GitFilesUnderPwd { <# Returns full paths to tracked + untracked-not-ignored files under PWD. #> Assert-InGitWorkTree $repoRoot = (& git rev-parse --show-toplevel).Trim() $pwdPath = (Get-Location).Path # cached (tracked) + others (untracked not ignored) $nulSeparated = & git -C $repoRoot ls-files -z --cached --others --exclude-standard $relativePaths = $nulSeparated.Split( [char]0, [System.StringSplitOptions]::RemoveEmptyEntries) foreach ($relPath in $relativePaths) { $fullPath = Join-Path $repoRoot $relPath # Only include files under the current subtree. if ($fullPath.StartsWith($pwdPath, [System.StringComparison]::OrdinalIgnoreCase)) { if (Test-Path -LiteralPath $fullPath -PathType Leaf) { $fullPath } } } } # --- Probes ------------------------------------------------------------------ function Test-ProbablyBinary { # Heuristic: treat as binary if the first 8 KiB contains any NUL byte. param([Parameter(Mandatory)][string]$Path) try { $stream = [System.IO.File]::Open($Path,'Open','Read','ReadWrite') try { $len = [int][Math]::Min(8192,$stream.Length) if ($len -le 0) { return $false } $buffer = [byte[]]::new($len) [void]$stream.Read($buffer,0,$len) return ($buffer -contains 0) } finally { $stream.Dispose() } } catch { return $false } } function Get-TextEncodingCategory { # Returns 'utf8', 'utf8-with-bom', 'other', or $null for likely-binary. param([Parameter(Mandatory)][string]$Path) $stream = [System.IO.File]::Open($Path,'Open','Read','ReadWrite') try { $fileLength = $stream.Length if ($fileLength -eq 0) { return 'utf8' } # BOM check (EF BB BF) $header = [byte[]]::new([Math]::Min(3,$fileLength)) [void]$stream.Read($header,0,$header.Length) if ($header.Length -ge 3 -and $header[0] -eq 0xEF -and $header[1] -eq 0xBB -and $header[2] -eq 0xBF) { return 'utf8-with-bom' } # Quick binary probe before expensive decoding $stream.Position = 0 $sampleLen = [int][Math]::Min(8192,$fileLength) $sample = [byte[]]::new($sampleLen) [void]$stream.Read($sample,0,$sampleLen) if ($sample -contains 0) { return $null } } finally { $stream.Dispose() } # Validate UTF-8 by decoding with throw-on-invalid option (no BOM). try { $bytes = [System.IO.File]::ReadAllBytes($Path) $utf8 = [System.Text.UTF8Encoding]::new($false,$true) [void]$utf8.GetString($bytes) return 'utf8' } catch { return 'other' } } # --- Main -------------------------------------------------------------------- $otherFiles = @() $byExtension = @{} $allFiles = Get-GitFilesUnderPwd foreach ($fullPath in $allFiles) { # Avoid decoding likely-binary files. if (Test-ProbablyBinary $fullPath) { continue } $category = Get-TextEncodingCategory $fullPath if (-not $category) { continue } $ext = [IO.Path]::GetExtension($fullPath).ToLower() if (-not $byExtension.ContainsKey($ext)) { $byExtension[$ext] = @{ 'utf8' = 0; 'utf8-with-bom' = 0; 'other' = 0 } } $byExtension[$ext][$category]++ if ($category -eq 'other') { $otherFiles += (Resolve-Path -LiteralPath $fullPath -Relative) } } # 1) Files in 'other' if ($otherFiles.Count -gt 0) { 'Files classified as ''other'':' $otherFiles | Sort-Object | ForEach-Object { " $_" } '' } # 2) Table by extension $rows = foreach ($kv in $byExtension.GetEnumerator()) { $ext = if ($kv.Key) { $kv.Key } else { '[noext]' } $u = [int]$kv.Value['utf8'] $b = [int]$kv.Value['utf8-with-bom'] $o = [int]$kv.Value['other'] [PSCustomObject]@{ Extension = $ext UTF8 = $u 'UTF8-with-BOM' = $b Other = $o Total = $u + $b + $o } } $rows | Sort-Object -Property ( @{Expression='Total';Descending=$true}, @{Expression='Extension';Descending=$false} ) | Format-Table -AutoSize ================================================ FILE: BuildTools/bom-strip.ps1 ================================================ <# .SYNOPSIS Strip UTF-8 BOM from selected text files under the current subtree, respecting .gitignore. .DESCRIPTION Enumerates tracked and untracked-but-not-ignored files under the current directory (via Git), filters to texty extensions and dotfiles, skips likely binary files (NUL probe), and removes a leading UTF-8 BOM (EF BB BF) in place. Refuses to run if there are uncommitted changes as a safeguard. Use -Force to override. Supports -WhatIf/-Confirm via ShouldProcess. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] param( [switch]$Force ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # --- File sets (ILSpy) ------------------------------------------------------ $Dotfiles = @( '.gitignore', '.editorconfig', '.gitattributes', '.gitmodules', '.tgitconfig', '.vsconfig' ) $AllowedExts = @( '.bat','.config','.cs','.csproj','.css','.filelist','.fs','.html','.il', '.ipynb','.js','.json','.less','.manifest','.md','.projitems','.props', '.ps1','.psd1','.shproj','.sln','.slnf','.svg','.template', '.tt', '.txt','.vb','.vsct','.vsixlangpack','.wxl','.xaml','.xml','.xshd','.yml' ) $IncludeNoExt = $true # include names like LICENSE # --- Git checks / enumeration ----------------------------------------------- function Assert-InGitWorkTree { $inside = (& git rev-parse --is-inside-work-tree 2>$null).Trim() if ($LASTEXITCODE -ne 0 -or $inside -ne 'true') { throw 'Not in a Git work tree.' } } function Assert-CleanWorkingTree { if ($Force) { return } $status = & git status --porcelain -z if ($LASTEXITCODE -ne 0) { throw 'git status failed.' } if (-not [string]::IsNullOrEmpty($status)) { throw 'Working tree not clean. Commit/stash changes or use -Force.' } } function Get-GitFilesUnderPwd { Assert-InGitWorkTree $repoRoot = (& git rev-parse --show-toplevel).Trim() $pwdPath = (Get-Location).Path $tracked = & git -C $repoRoot ls-files -z $others = & git -C $repoRoot ls-files --others --exclude-standard -z $allRel = ("$tracked$others").Split( [char]0, [System.StringSplitOptions]::RemoveEmptyEntries) foreach ($relPath in $allRel) { $fullPath = Join-Path $repoRoot $relPath if ($fullPath.StartsWith($pwdPath, [System.StringComparison]::OrdinalIgnoreCase)) { if (Test-Path -LiteralPath $fullPath -PathType Leaf) { $fullPath } } } } # --- Probes ----------------------------------------------------------------- function Test-HasUtf8Bom { param([Parameter(Mandatory)][string]$Path) try { $stream = [System.IO.File]::Open($Path,'Open','Read','ReadWrite') try { if ($stream.Length -lt 3) { return $false } $header = [byte[]]::new(3) [void]$stream.Read($header,0,3) return ($header[0] -eq 0xEF -and $header[1] -eq 0xBB -and $header[2] -eq 0xBF) } finally { $stream.Dispose() } } catch { return $false } } function Test-ProbablyBinary { # Binary if the first 8 KiB contains any NUL byte. param([Parameter(Mandatory)][string]$Path) try { $stream = [System.IO.File]::Open($Path,'Open','Read','ReadWrite') try { $len = [int][Math]::Min(8192,$stream.Length) if ($len -le 0) { return $false } $buffer = [byte[]]::new($len) [void]$stream.Read($buffer,0,$len) return ($buffer -contains 0) } finally { $stream.Dispose() } } catch { return $false } } # --- Mutation --------------------------------------------------------------- function Remove-Utf8BomInPlace { # Write the existing buffer from offset 3, no extra full-size allocation. param([Parameter(Mandatory)][string]$Path) $bytes = [System.IO.File]::ReadAllBytes($Path) if ($bytes.Length -lt 3) { return $false } if ($bytes[0] -ne 0xEF -or $bytes[1] -ne 0xBB -or $bytes[2] -ne 0xBF) { return $false } $stream = [System.IO.File]::Open($Path,'Create','Write','ReadWrite') try { $stream.Write($bytes, 3, $bytes.Length - 3) $stream.SetLength($bytes.Length - 3) } finally { $stream.Dispose() } return $true } # --- Main ------------------------------------------------------------------- Assert-InGitWorkTree Assert-CleanWorkingTree $allFiles = Get-GitFilesUnderPwd $targets = $allFiles | % { $fileName = [IO.Path]::GetFileName($_) $ext = [IO.Path]::GetExtension($fileName) $isDot = $Dotfiles -contains $fileName $isNoExt = -not $fileName.Contains('.') if ($isDot -or ($AllowedExts -contains $ext) -or ($IncludeNoExt -and $isNoExt -and -not $isDot)) { $_ } } | ? { Test-HasUtf8Bom $_ } | ? { -not (Test-ProbablyBinary $_) } $changed = 0 $byExtension = @{} $dotfileChanges = 0 $targets | % { $relative = Resolve-Path -LiteralPath $_ -Relative if ($PSCmdlet.ShouldProcess($relative,'Strip UTF-8 BOM')) { if (Remove-Utf8BomInPlace -Path $_) { $changed++ $fileName = [IO.Path]::GetFileName($_) if ($Dotfiles -contains $fileName) { $dotfileChanges++ } $ext = [IO.Path]::GetExtension($fileName) if (-not $byExtension.ContainsKey($ext)) { $byExtension[$ext] = 0 } $byExtension[$ext]++ "stripped BOM: $relative" } } } "Done. Stripped BOM from $changed file(s)." if ($byExtension.Keys.Count -gt 0) { "" "By extension:" $byExtension.GetEnumerator() | Sort-Object Name | % { $key = if ([string]::IsNullOrEmpty($_.Name)) { '[noext]' } else { $_.Name } " {0}: {1}" -f $key, $_.Value } } if ($dotfileChanges -gt 0) { " [dotfiles]: $dotfileChanges" } ================================================ FILE: BuildTools/create-filelists.ps1 ================================================ $ErrorActionPreference = "Stop"; $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False gci -Include *.vsix, *.msi -recurse | foreach ($_) { if (-not ($_.FullName -contains "\bin\Debug\")) { continue; } $idx=-1; $body=$false; $outputFileName = ".\BuildTools\$($_.Name -replace '-\d+\.\d+\.\d+\.\d+', '').filelist"; $lines = 7z l $_.FullName | foreach { if ($idx -eq -1) { $idx = $_.IndexOf("Name"); } $p = $body; if ($idx -gt 0) { $body = ($body -ne ($_ -match ' *-[ -]+')) } if ($p -and $body) { $_.Substring($idx) } } | sort [System.IO.File]::WriteAllLines($outputFileName, $lines, $Utf8NoBomEncoding) } ================================================ FILE: BuildTools/format.bat ================================================ @rem This file can be used to trigger the commit hook's formatting, @rem modifying the local formatting even if not committing all changes. pushd %~dp0\.. "%ProgramFiles%\Git\usr\bin\bash.exe" BuildTools\pre-commit --format popd ================================================ FILE: BuildTools/ghactions-install.ps1 ================================================ $ErrorActionPreference = "Stop" $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; # make sure this matches artifacts-only branches list in appveyor.yml! $masterBranches = '^refs/heads/(master|release/.+)$'; $decompilerVersionInfoTemplateFile = "ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs"; $versionParts = @{}; Get-Content $decompilerVersionInfoTemplateFile | where { $_ -match 'string (\w+) = "?(\w+)"?;' } | foreach { $versionParts.Add($Matches[1], $Matches[2]) } $major = $versionParts.Major; $minor = $versionParts.Minor; $build = $versionParts.Build; $versionName = $versionParts.VersionName; if ($versionName -ne "null") { $versionName = "-$versionName"; } else { $versionName = ""; } Write-Host "GITHUB_REF: '$env:GITHUB_REF'"; if ($env:GITHUB_REF -match $masterBranches) { $branch = ""; $suffix = ""; } elseif ($env:GITHUB_REF -match '^refs/pull/(\d+)/merge$') { $branch = ""; $suffix = "-pr" + $Matches[1]; } elseif ($env:GITHUB_REF -match '^refs/heads/(.+)$') { $branch = "-" + $Matches[1]; $suffix = ""; } else { $branch = ""; $suffix = ""; } $revision = [Int32]::Parse((git rev-list --count "$baseCommit..HEAD")) + $baseCommitRev; $newVersion="$major.$minor.$build.$revision"; $ilspyVersionNumber = "$newVersion$branch$versionName$suffix"; $ilspyVersionNumber = $ilspyVersionNumber.Replace("/", "-"); $env:ILSPY_VERSION_NUMBER="$ilspyVersionNumber"; $env:ILSPY_VERSION_NUMBER | Out-File "ILSPY_VERSION" Write-Host "new version: $ilspyVersionNumber"; ================================================ FILE: BuildTools/pre-commit ================================================ #!/bin/sh # # To enable this hook, copy/symlink this file to ".git/hooks/pre-commit". # mklink .git\hooks\pre-commit ..\..\BuildTools\pre-commit set -eu DOTNET_FORMAT_VERSION=10.0.100-rtm.25531.102 DOTNET_FORMAT_SOURCE="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json" DOTNET_PATH="$LOCALAPPDATA/ICSharpCode/ILSpy/dotnet-format-$DOTNET_FORMAT_VERSION" if [ ! -d "$DOTNET_PATH" ]; then echo "Downloading dotnet-format $DOTNET_FORMAT_VERSION..." dotnet tool install --tool-path "$DOTNET_PATH" dotnet-format --version "$DOTNET_FORMAT_VERSION" --add-source "$DOTNET_FORMAT_SOURCE" fi "$DOTNET_PATH/dotnet-format.exe" --version if [ "${1:-}" = "--format" ]; then # called via format.bat "$DOTNET_PATH/dotnet-format.exe" whitespace --no-restore --verbosity detailed ILSpy.sln elif git diff --quiet --ignore-submodules; then "$DOTNET_PATH/dotnet-format.exe" whitespace --no-restore --verbosity detailed ILSpy.sln git add -u -- \*\*.cs else echo Partial commit: only verifying formatting exec "$DOTNET_PATH/dotnet-format.exe" whitespace --verify-no-changes --no-restore --verbosity detailed ILSpy.sln fi ================================================ FILE: BuildTools/sort-resx.ps1 ================================================ $ErrorActionPreference = "Stop"; [Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null Write-Host "Sorting .resx files..."; Get-ChildItem -Include *.resx -Recurse | foreach ($_) { Write-Host $_.FullName; $doc = [System.Xml.Linq.XDocument]::Load($_.FullName); $descendants = [System.Linq.Enumerable]::ToArray($doc.Descendants("data")); [System.Xml.Linq.Extensions]::Remove($descendants); $ordered = [System.Linq.Enumerable]::OrderBy($descendants, [System.Func[System.Xml.Linq.XElement,string]] { param ($e) $e.Attribute("name").Value }, [System.StringComparer]::Ordinal); $doc.Root.Add($ordered); $doc.Save($_.FullName); } ================================================ FILE: BuildTools/update-assemblyinfo.ps1 ================================================ if (-not ($PSVersionTable.PSCompatibleVersions -contains "5.0")) { Write-Error "This script requires at least powershell version 5.0!"; return 255; } $ErrorActionPreference = "Stop" $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; # make sure this matches artifacts-only branches list in appveyor.yml! $masterBranches = '^(master|release/.+)$'; $decompilerVersionInfoTemplateFile = "ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs"; function Test-File([string]$filename) { return [System.IO.File]::Exists((Join-Path (Get-Location) $filename)); } function Test-Dir([string]$name) { return [System.IO.Directory]::Exists((Join-Path (Get-Location) $name)); } function Find-Git() { try { $executable = (get-command git).Path; return $executable -ne $null; } catch { #git not found in path, continue; } #we're on Windows if ($env:PROGRAMFILES -ne $null) { #hack for x86 powershell used by default (yuck!) if (${env:PROGRAMFILES(X86)} -eq ${env:PROGRAMFILES}) { $env:PROGRAMFILES = $env:PROGRAMFILES.Substring(0, $env:PROGRAMFILES.Length - 6); } #try to add git to path if ([System.IO.Directory]::Exists("$env:PROGRAMFILES\git\cmd\")) { $env:PATH = "$env:PATH;$env:PROGRAMFILES\git\cmd\"; return $true; } } return $false; } function No-Git() { return -not (((Test-Dir ".git") -or (Test-File ".git")) -and (Find-Git)); } function gitVersion() { if (No-Git) { return 0; } try { return [Int32]::Parse((git rev-list --count "$baseCommit..HEAD" 2>&1 | Tee-Object -Variable cmdOutput)) + $baseCommitRev; } catch { Write-Host $cmdOutput return 0; } } function gitCommitHash() { if (No-Git) { return "0000000000000000000000000000000000000000"; } try { return (git rev-list --max-count 1 HEAD 2>&1 | Tee-Object -Variable cmdOutput); } catch { Write-Host $cmdOutput return "0000000000000000000000000000000000000000"; } } function gitShortCommitHash() { if (No-Git) { return "00000000"; } try { return (git rev-parse --short=8 (git rev-list --max-count 1 HEAD 2>&1 | Tee-Object -Variable cmdOutput) 2>&1 | Tee-Object -Variable cmdOutput); } catch { Write-Host $cmdOutput return "00000000"; } } function gitBranch() { if (No-Git) { return "no-branch"; } if ($env:APPVEYOR_REPO_BRANCH -ne $null) { return $env:APPVEYOR_REPO_BRANCH; } elseif ($env:BUILD_SOURCEBRANCHNAME -ne $null) { return $env:BUILD_SOURCEBRANCHNAME; } else { return ((git branch --no-color).Split([System.Environment]::NewLine) | where { $_ -match "^\* " } | select -First 1).Substring(2); } } $templateFiles = ( @{Input=$decompilerVersionInfoTemplateFile; Output="ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.cs"}, @{Input="ILSpy.AddIn/source.extension.vsixmanifest.template"; Output = "ILSpy.AddIn/source.extension.vsixmanifest"}, @{Input="ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template"; Output = "ILSpy.AddIn.VS2022/source.extension.vsixmanifest"} ); [string]$mutexId = "ILSpyUpdateAssemblyInfo" + (Get-Location).ToString().GetHashCode(); Write-Host $mutexId; [bool]$createdNew = $false; $mutex = New-Object System.Threading.Mutex($true, $mutexId, [ref]$createdNew); try { if (-not $createdNew) { try { $mutex.WaitOne(10000); } catch [System.Threading.AbandonedMutexException] { } return 0; } if (-not (Test-File "ILSpy.sln")) { Write-Error "Working directory must be the ILSpy repo root!"; return 2; } $versionParts = @{}; Get-Content $decompilerVersionInfoTemplateFile | where { $_ -match 'string (\w+) = "?(\w+)"?;' } | foreach { $versionParts.Add($Matches[1], $Matches[2]) } $major = $versionParts.Major; $minor = $versionParts.Minor; $build = $versionParts.Build; $versionName = $versionParts.VersionName; $revision = gitVersion; $branchName = gitBranch; $gitCommitHash = gitCommitHash; $gitShortCommitHash = gitShortCommitHash; if ($branchName -match $masterBranches) { $postfixBranchName = ""; } else { $postfixBranchName = "-$branchName"; } if ($versionName -eq "null") { $versionName = ""; $postfixVersionName = ""; } else { $postfixVersionName = "-$versionName"; } $buildConfig = $args[0].ToString().ToLower(); if ($buildConfig -eq "release") { $buildConfig = ""; } else { $buildConfig = "-" + $buildConfig; } $fullVersionNumber = "$major.$minor.$build.$revision"; if ((-not (Test-File "VERSION")) -or (((Get-Content "VERSION") -Join [System.Environment]::NewLine) -ne "$fullVersionNumber$postfixVersionName")) { "$fullVersionNumber$postfixVersionName" | Out-File -Encoding utf8 -NoNewLine "VERSION"; } foreach ($file in $templateFiles) { [string]$in = (Get-Content $file.Input) -Join [System.Environment]::NewLine; $out = $in.Replace('$INSERTVERSION$', $fullVersionNumber); $out = $out.Replace('$INSERTMAJORVERSION$', $major); $out = $out.Replace('$INSERTREVISION$', $revision); $out = $out.Replace('$INSERTCOMMITHASH$', $gitCommitHash); $out = $out.Replace('$INSERTSHORTCOMMITHASH$', $gitShortCommitHash); $out = $out.Replace('$INSERTDATE$', [System.DateTime]::Now.ToString("MM/dd/yyyy")); $out = $out.Replace('$INSERTYEAR$', [System.DateTime]::Now.Year.ToString()); $out = $out.Replace('$INSERTBRANCHNAME$', $branchName); $out = $out.Replace('$INSERTBRANCHPOSTFIX$', $postfixBranchName); $out = $out.Replace('$INSERTVERSIONNAME$', $versionName); $out = $out.Replace('$INSERTVERSIONNAMEPOSTFIX$', $postfixVersionName); $out = $out.Replace('$INSERTBUILDCONFIG$', $buildConfig); if ((-not (Test-File $file.Output)) -or (((Get-Content $file.Output) -Join [System.Environment]::NewLine) -ne $out)) { $utf8NoBom = New-Object System.Text.UTF8Encoding($false); [System.IO.File]::WriteAllText($file.Output, $out, $utf8NoBom); } } } finally { $mutex.ReleaseMutex(); $mutex.Close(); } ================================================ FILE: Directory.Build.props ================================================ $(NoWarn);NU1510 true ================================================ FILE: Directory.Packages.props ================================================ true false ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlContext.cs ================================================ /* Copyright (c) 2015 Ki 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; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using Metadata = ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.BamlDecompiler.Baml { internal class BamlContext { public IDecompilerTypeSystem TypeSystem { get; } public KnownThings KnownThings { get; } Dictionary assemblyMap = new Dictionary(); public Dictionary AssemblyIdMap { get; } public Dictionary AttributeIdMap { get; } public Dictionary StringIdMap { get; } public Dictionary TypeIdMap { get; } BamlContext(IDecompilerTypeSystem typeSystem) { this.TypeSystem = typeSystem; KnownThings = new KnownThings(typeSystem); AssemblyIdMap = new Dictionary(); AttributeIdMap = new Dictionary(); StringIdMap = new Dictionary(); TypeIdMap = new Dictionary(); } public static BamlContext ConstructContext(IDecompilerTypeSystem typeSystem, BamlDocument document, CancellationToken token) { var ctx = new BamlContext(typeSystem); foreach (var record in document) { token.ThrowIfCancellationRequested(); if (record is AssemblyInfoRecord assemblyInfo) { if (assemblyInfo.AssemblyId == ctx.AssemblyIdMap.Count) ctx.AssemblyIdMap.Add(assemblyInfo.AssemblyId, assemblyInfo); } else if (record is AttributeInfoRecord attrInfo) { if (attrInfo.AttributeId == ctx.AttributeIdMap.Count) ctx.AttributeIdMap.Add(attrInfo.AttributeId, attrInfo); } else if (record is StringInfoRecord strInfo) { if (strInfo.StringId == ctx.StringIdMap.Count) ctx.StringIdMap.Add(strInfo.StringId, strInfo); } else if (record is TypeInfoRecord typeInfo) { if (typeInfo.TypeId == ctx.TypeIdMap.Count) ctx.TypeIdMap.Add(typeInfo.TypeId, typeInfo); } } return ctx; } public (string FullAssemblyName, IModule Assembly) ResolveAssembly(ushort id) { id &= 0xfff; if (!assemblyMap.TryGetValue(id, out var assembly)) { if (AssemblyIdMap.TryGetValue(id, out var assemblyRec)) { var assemblyName = Metadata.AssemblyNameReference.Parse(assemblyRec.AssemblyFullName); if (assemblyName.Name == TypeSystem.MainModule.AssemblyName) { assembly = (assemblyRec.AssemblyFullName, TypeSystem.MainModule); } else { assembly = (assemblyRec.AssemblyFullName, FindMatchingReference(assemblyName)); } } else assembly = (null, null); assemblyMap[id] = assembly; } return assembly; } private IModule FindMatchingReference(AssemblyNameReference name) { IModule bestMatch = null; foreach (var module in TypeSystem.ReferencedModules) { if (module.AssemblyName == name.Name) { // using highest version as criterion if (bestMatch == null || bestMatch.AssemblyVersion <= module.AssemblyVersion) { bestMatch = module; } } } return bestMatch; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlDocument.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; namespace ICSharpCode.BamlDecompiler.Baml { internal class BamlDocument : List { public string DocumentName { get; set; } public string Signature { get; set; } public BamlVersion ReaderVersion { get; set; } public BamlVersion UpdaterVersion { get; set; } public BamlVersion WriterVersion { get; set; } public struct BamlVersion { public ushort Major; public ushort Minor; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlNode.cs ================================================ /* Copyright (c) 2015 Ki 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.Threading; namespace ICSharpCode.BamlDecompiler.Baml { internal abstract class BamlNode { public BamlBlockNode Parent { get; set; } public abstract BamlRecordType Type { get; } public object Annotation { get; set; } public abstract BamlRecord Record { get; } public static bool IsHeader(BamlRecord rec) { switch (rec.Type) { case BamlRecordType.ConstructorParametersStart: case BamlRecordType.DocumentStart: case BamlRecordType.ElementStart: case BamlRecordType.KeyElementStart: case BamlRecordType.NamedElementStart: case BamlRecordType.PropertyArrayStart: case BamlRecordType.PropertyComplexStart: case BamlRecordType.PropertyDictionaryStart: case BamlRecordType.PropertyListStart: case BamlRecordType.StaticResourceStart: return true; } return false; } public static bool IsFooter(BamlRecord rec) { switch (rec.Type) { case BamlRecordType.ConstructorParametersEnd: case BamlRecordType.DocumentEnd: case BamlRecordType.ElementEnd: case BamlRecordType.KeyElementEnd: case BamlRecordType.PropertyArrayEnd: case BamlRecordType.PropertyComplexEnd: case BamlRecordType.PropertyDictionaryEnd: case BamlRecordType.PropertyListEnd: case BamlRecordType.StaticResourceEnd: return true; } return false; } public static bool IsMatch(BamlRecord header, BamlRecord footer) { switch (header.Type) { case BamlRecordType.ConstructorParametersStart: return footer.Type == BamlRecordType.ConstructorParametersEnd; case BamlRecordType.DocumentStart: return footer.Type == BamlRecordType.DocumentEnd; case BamlRecordType.KeyElementStart: return footer.Type == BamlRecordType.KeyElementEnd; case BamlRecordType.PropertyArrayStart: return footer.Type == BamlRecordType.PropertyArrayEnd; case BamlRecordType.PropertyComplexStart: return footer.Type == BamlRecordType.PropertyComplexEnd; case BamlRecordType.PropertyDictionaryStart: return footer.Type == BamlRecordType.PropertyDictionaryEnd; case BamlRecordType.PropertyListStart: return footer.Type == BamlRecordType.PropertyListEnd; case BamlRecordType.StaticResourceStart: return footer.Type == BamlRecordType.StaticResourceEnd; case BamlRecordType.ElementStart: case BamlRecordType.NamedElementStart: return footer.Type == BamlRecordType.ElementEnd; } return false; } public static BamlNode Parse(BamlDocument document, CancellationToken token) { Debug.Assert(document.Count > 0 && document[0].Type == BamlRecordType.DocumentStart); BamlBlockNode current = null; var stack = new Stack(); for (int i = 0; i < document.Count; i++) { token.ThrowIfCancellationRequested(); if (IsHeader(document[i])) { var prev = current; current = new BamlBlockNode { Header = document[i] }; if (prev != null) { prev.Children.Add(current); current.Parent = prev; stack.Push(prev); } } else if (IsFooter(document[i])) { if (current == null) throw new Exception("Unexpected footer."); while (!IsMatch(current.Header, document[i])) { // End record can be omited (sometimes). if (stack.Count > 0) current = stack.Pop(); } current.Footer = document[i]; if (stack.Count > 0) current = stack.Pop(); } else current.Children.Add(new BamlRecordNode(document[i]) { Parent = current }); } Debug.Assert(stack.Count == 0); return current; } } internal class BamlRecordNode : BamlNode { BamlRecord record; public override BamlRecord Record => record; public override BamlRecordType Type => Record.Type; public BamlRecordNode(BamlRecord record) => this.record = record; } internal class BamlBlockNode : BamlNode { public BamlRecord Header { get; set; } public IList Children { get; } public BamlRecord Footer { get; set; } public override BamlRecord Record => Header; public override BamlRecordType Type => Header.Type; public BamlBlockNode() => Children = new List(); } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlReader.cs ================================================ /* Copyright (c) 2015 Ki 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.IO; using System.Text; using System.Threading; namespace ICSharpCode.BamlDecompiler.Baml { internal class BamlBinaryReader : BinaryReader { public BamlBinaryReader(Stream stream) : base(stream) { } public int ReadEncodedInt() => Read7BitEncodedInt(); } internal class BamlReader { const string MSBAML_SIG = "MSBAML"; internal static bool IsBamlHeader(Stream str) { var pos = str.Position; try { var rdr = new BinaryReader(str, Encoding.Unicode); int len = (int)(rdr.ReadUInt32() >> 1); if (len != MSBAML_SIG.Length) return false; var sig = new string(rdr.ReadChars(len)); return sig == MSBAML_SIG; } finally { str.Position = pos; } } static string ReadSignature(Stream str) { var rdr = new BinaryReader(str, Encoding.Unicode); uint len = rdr.ReadUInt32(); var sig = new string(rdr.ReadChars((int)(len >> 1))); rdr.ReadBytes((int)(((len + 3) & ~3) - len)); return sig; } public static BamlDocument ReadDocument(Stream str, CancellationToken token) { var ret = new BamlDocument(); var reader = new BamlBinaryReader(str); ret.Signature = ReadSignature(str); if (ret.Signature != MSBAML_SIG) throw new NotSupportedException(); ret.ReaderVersion = new BamlDocument.BamlVersion { Major = reader.ReadUInt16(), Minor = reader.ReadUInt16() }; ret.UpdaterVersion = new BamlDocument.BamlVersion { Major = reader.ReadUInt16(), Minor = reader.ReadUInt16() }; ret.WriterVersion = new BamlDocument.BamlVersion { Major = reader.ReadUInt16(), Minor = reader.ReadUInt16() }; if (ret.ReaderVersion.Major != 0 || ret.ReaderVersion.Minor != 0x60 || ret.UpdaterVersion.Major != 0 || ret.UpdaterVersion.Minor != 0x60 || ret.WriterVersion.Major != 0 || ret.WriterVersion.Minor != 0x60) throw new NotSupportedException(); var recs = new Dictionary(); while (str.Position < str.Length) { token.ThrowIfCancellationRequested(); long pos = str.Position; var type = (BamlRecordType)reader.ReadByte(); BamlRecord rec = null; switch (type) { case BamlRecordType.AssemblyInfo: rec = new AssemblyInfoRecord(); break; case BamlRecordType.AttributeInfo: rec = new AttributeInfoRecord(); break; case BamlRecordType.ConstructorParametersStart: rec = new ConstructorParametersStartRecord(); break; case BamlRecordType.ConstructorParametersEnd: rec = new ConstructorParametersEndRecord(); break; case BamlRecordType.ConstructorParameterType: rec = new ConstructorParameterTypeRecord(); break; case BamlRecordType.ConnectionId: rec = new ConnectionIdRecord(); break; case BamlRecordType.ContentProperty: rec = new ContentPropertyRecord(); break; case BamlRecordType.DefAttribute: rec = new DefAttributeRecord(); break; case BamlRecordType.DefAttributeKeyString: rec = new DefAttributeKeyStringRecord(); break; case BamlRecordType.DefAttributeKeyType: rec = new DefAttributeKeyTypeRecord(); break; case BamlRecordType.DeferableContentStart: rec = new DeferableContentStartRecord(); break; case BamlRecordType.DocumentEnd: rec = new DocumentEndRecord(); break; case BamlRecordType.DocumentStart: rec = new DocumentStartRecord(); break; case BamlRecordType.ElementEnd: rec = new ElementEndRecord(); break; case BamlRecordType.ElementStart: rec = new ElementStartRecord(); break; case BamlRecordType.KeyElementEnd: rec = new KeyElementEndRecord(); break; case BamlRecordType.KeyElementStart: rec = new KeyElementStartRecord(); break; case BamlRecordType.LineNumberAndPosition: rec = new LineNumberAndPositionRecord(); break; case BamlRecordType.LinePosition: rec = new LinePositionRecord(); break; case BamlRecordType.LiteralContent: rec = new LiteralContentRecord(); break; case BamlRecordType.NamedElementStart: rec = new NamedElementStartRecord(); break; case BamlRecordType.OptimizedStaticResource: rec = new OptimizedStaticResourceRecord(); break; case BamlRecordType.PIMapping: rec = new PIMappingRecord(); break; case BamlRecordType.PresentationOptionsAttribute: rec = new PresentationOptionsAttributeRecord(); break; case BamlRecordType.Property: rec = new PropertyRecord(); break; case BamlRecordType.PropertyArrayEnd: rec = new PropertyArrayEndRecord(); break; case BamlRecordType.PropertyArrayStart: rec = new PropertyArrayStartRecord(); break; case BamlRecordType.PropertyComplexEnd: rec = new PropertyComplexEndRecord(); break; case BamlRecordType.PropertyComplexStart: rec = new PropertyComplexStartRecord(); break; case BamlRecordType.PropertyCustom: rec = new PropertyCustomRecord(); break; case BamlRecordType.PropertyDictionaryEnd: rec = new PropertyDictionaryEndRecord(); break; case BamlRecordType.PropertyDictionaryStart: rec = new PropertyDictionaryStartRecord(); break; case BamlRecordType.PropertyListEnd: rec = new PropertyListEndRecord(); break; case BamlRecordType.PropertyListStart: rec = new PropertyListStartRecord(); break; case BamlRecordType.PropertyStringReference: rec = new PropertyStringReferenceRecord(); break; case BamlRecordType.PropertyTypeReference: rec = new PropertyTypeReferenceRecord(); break; case BamlRecordType.PropertyWithConverter: rec = new PropertyWithConverterRecord(); break; case BamlRecordType.PropertyWithExtension: rec = new PropertyWithExtensionRecord(); break; case BamlRecordType.PropertyWithStaticResourceId: rec = new PropertyWithStaticResourceIdRecord(); break; case BamlRecordType.RoutedEvent: rec = new RoutedEventRecord(); break; case BamlRecordType.StaticResourceEnd: rec = new StaticResourceEndRecord(); break; case BamlRecordType.StaticResourceId: rec = new StaticResourceIdRecord(); break; case BamlRecordType.StaticResourceStart: rec = new StaticResourceStartRecord(); break; case BamlRecordType.StringInfo: rec = new StringInfoRecord(); break; case BamlRecordType.Text: rec = new TextRecord(); break; case BamlRecordType.TextWithConverter: rec = new TextWithConverterRecord(); break; case BamlRecordType.TextWithId: rec = new TextWithIdRecord(); break; case BamlRecordType.TypeInfo: rec = new TypeInfoRecord(); break; case BamlRecordType.TypeSerializerInfo: rec = new TypeSerializerInfoRecord(); break; case BamlRecordType.XmlnsProperty: rec = new XmlnsPropertyRecord(); break; case BamlRecordType.XmlAttribute: case BamlRecordType.ProcessingInstruction: case BamlRecordType.LastRecordType: case BamlRecordType.EndAttributes: case BamlRecordType.DefTag: case BamlRecordType.ClrEvent: case BamlRecordType.Comment: default: throw new NotSupportedException(); } rec.Position = pos; rec.Read(reader); ret.Add(rec); recs.Add(pos, rec); } for (int i = 0; i < ret.Count; i++) { if (ret[i] is IBamlDeferRecord defer) defer.ReadDefer(ret, i, _ => recs[_]); } return ret; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlRecords.cs ================================================ /* Copyright (c) 2015 Ki 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.Diagnostics; using System.IO; namespace ICSharpCode.BamlDecompiler.Baml { internal enum BamlRecordType : byte { ClrEvent = 0x13, Comment = 0x17, AssemblyInfo = 0x1c, AttributeInfo = 0x1f, ConstructorParametersStart = 0x2a, ConstructorParametersEnd = 0x2b, ConstructorParameterType = 0x2c, ConnectionId = 0x2d, ContentProperty = 0x2e, DefAttribute = 0x19, DefAttributeKeyString = 0x26, DefAttributeKeyType = 0x27, DeferableContentStart = 0x25, DefTag = 0x18, DocumentEnd = 0x2, DocumentStart = 0x1, ElementEnd = 0x4, ElementStart = 0x3, EndAttributes = 0x1a, KeyElementEnd = 0x29, KeyElementStart = 0x28, LastRecordType = 0x39, LineNumberAndPosition = 0x35, LinePosition = 0x36, LiteralContent = 0xf, NamedElementStart = 0x2f, OptimizedStaticResource = 0x37, PIMapping = 0x1b, PresentationOptionsAttribute = 0x34, ProcessingInstruction = 0x16, Property = 0x5, PropertyArrayEnd = 0xa, PropertyArrayStart = 0x9, PropertyComplexEnd = 0x8, PropertyComplexStart = 0x7, PropertyCustom = 0x6, PropertyDictionaryEnd = 0xe, PropertyDictionaryStart = 0xd, PropertyListEnd = 0xc, PropertyListStart = 0xb, PropertyStringReference = 0x21, PropertyTypeReference = 0x22, PropertyWithConverter = 0x24, PropertyWithExtension = 0x23, PropertyWithStaticResourceId = 0x38, RoutedEvent = 0x12, StaticResourceEnd = 0x31, StaticResourceId = 0x32, StaticResourceStart = 0x30, StringInfo = 0x20, Text = 0x10, TextWithConverter = 0x11, TextWithId = 0x33, TypeInfo = 0x1d, TypeSerializerInfo = 0x1e, XmlAttribute = 0x15, XmlnsProperty = 0x14 } internal abstract class BamlRecord { public abstract BamlRecordType Type { get; } public long Position { get; internal set; } public abstract void Read(BamlBinaryReader reader); public abstract void Write(BamlBinaryWriter writer); } internal abstract class SizedBamlRecord : BamlRecord { public override void Read(BamlBinaryReader reader) { long pos = reader.BaseStream.Position; int size = reader.ReadEncodedInt(); ReadData(reader, size - (int)(reader.BaseStream.Position - pos)); } int SizeofEncodedInt(int val) { if ((val & ~0x7F) == 0) { return 1; } if ((val & ~0x3FFF) == 0) { return 2; } if ((val & ~0x1FFFFF) == 0) { return 3; } if ((val & ~0xFFFFFFF) == 0) { return 4; } return 5; } public override void Write(BamlBinaryWriter writer) { long pos = writer.BaseStream.Position; WriteData(writer); var size = (int)(writer.BaseStream.Position - pos); size = SizeofEncodedInt(SizeofEncodedInt(size) + size) + size; writer.BaseStream.Position = pos; writer.WriteEncodedInt(size); WriteData(writer); } protected abstract void ReadData(BamlBinaryReader reader, int size); protected abstract void WriteData(BamlBinaryWriter writer); } internal interface IBamlDeferRecord { long Position { get; } BamlRecord Record { get; set; } void ReadDefer(BamlDocument doc, int index, Func resolve); void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr); } internal class XmlnsPropertyRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.XmlnsProperty; public string Prefix { get; set; } public string XmlNamespace { get; set; } public ushort[] AssemblyIds { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { Prefix = reader.ReadString(); XmlNamespace = reader.ReadString(); AssemblyIds = new ushort[reader.ReadUInt16()]; for (int i = 0; i < AssemblyIds.Length; i++) AssemblyIds[i] = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(Prefix); writer.Write(XmlNamespace); writer.Write((ushort)AssemblyIds.Length); foreach (ushort i in AssemblyIds) writer.Write(i); } } internal class PresentationOptionsAttributeRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.PresentationOptionsAttribute; public string Value { get; set; } public ushort NameId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { Value = reader.ReadString(); NameId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(Value); writer.Write(NameId); } } internal class PIMappingRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.PIMapping; public string XmlNamespace { get; set; } public string ClrNamespace { get; set; } public ushort AssemblyId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { XmlNamespace = reader.ReadString(); ClrNamespace = reader.ReadString(); AssemblyId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(XmlNamespace); writer.Write(ClrNamespace); writer.Write(AssemblyId); } } internal class AssemblyInfoRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.AssemblyInfo; public ushort AssemblyId { get; set; } public string AssemblyFullName { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { AssemblyId = reader.ReadUInt16(); AssemblyFullName = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(AssemblyId); writer.Write(AssemblyFullName); } } internal class PropertyRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.Property; public ushort AttributeId { get; set; } public string Value { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { AttributeId = reader.ReadUInt16(); Value = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(AttributeId); writer.Write(Value); } } internal class PropertyWithConverterRecord : PropertyRecord { public override BamlRecordType Type => BamlRecordType.PropertyWithConverter; public ushort ConverterTypeId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { base.ReadData(reader, size); ConverterTypeId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { base.WriteData(writer); writer.Write(ConverterTypeId); } } internal class PropertyCustomRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyCustom; public ushort AttributeId { get; set; } public ushort SerializerTypeId { get; set; } public byte[] Data { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { long pos = reader.BaseStream.Position; AttributeId = reader.ReadUInt16(); SerializerTypeId = reader.ReadUInt16(); Data = reader.ReadBytes(size - (int)(reader.BaseStream.Position - pos)); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(AttributeId); writer.Write(SerializerTypeId); writer.Write(Data); } } internal class DefAttributeRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.DefAttribute; public string Value { get; set; } public ushort NameId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { Value = reader.ReadString(); NameId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(Value); writer.Write(NameId); } } internal class DefAttributeKeyStringRecord : SizedBamlRecord, IBamlDeferRecord { internal uint pos = 0xffffffff; public override BamlRecordType Type => BamlRecordType.DefAttributeKeyString; public ushort ValueId { get; set; } public bool Shared { get; set; } public bool SharedSet { get; set; } public BamlRecord Record { get; set; } public void ReadDefer(BamlDocument doc, int index, Func resolve) { bool keys = true; do { switch (doc[index].Type) { case BamlRecordType.DefAttributeKeyString: case BamlRecordType.DefAttributeKeyType: case BamlRecordType.OptimizedStaticResource: keys = true; break; case BamlRecordType.StaticResourceStart: NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index); keys = true; break; case BamlRecordType.KeyElementStart: NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index); keys = true; break; default: keys = false; index--; break; } index++; } while (keys); Record = resolve(doc[index].Position + pos); } public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) { bool keys = true; do { switch (doc[index].Type) { case BamlRecordType.DefAttributeKeyString: case BamlRecordType.DefAttributeKeyType: case BamlRecordType.OptimizedStaticResource: keys = true; break; case BamlRecordType.StaticResourceStart: NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index); keys = true; break; case BamlRecordType.KeyElementStart: NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index); keys = true; break; default: keys = false; index--; break; } index++; } while (keys); wtr.BaseStream.Seek(pos, SeekOrigin.Begin); wtr.Write((uint)(Record.Position - doc[index].Position)); } protected override void ReadData(BamlBinaryReader reader, int size) { ValueId = reader.ReadUInt16(); pos = reader.ReadUInt32(); Shared = reader.ReadBoolean(); SharedSet = reader.ReadBoolean(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(ValueId); pos = (uint)writer.BaseStream.Position; writer.Write((uint)0); writer.Write(Shared); writer.Write(SharedSet); } static void NavigateTree(BamlDocument doc, BamlRecordType start, BamlRecordType end, ref int index) { index++; while (true) //Assume there alway is a end { if (doc[index].Type == start) NavigateTree(doc, start, end, ref index); else if (doc[index].Type == end) return; index++; } } } internal class TypeInfoRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.TypeInfo; public ushort TypeId { get; set; } public ushort AssemblyId { get; set; } public string TypeFullName { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { TypeId = reader.ReadUInt16(); AssemblyId = reader.ReadUInt16(); TypeFullName = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(TypeId); writer.Write(AssemblyId); writer.Write(TypeFullName); } } internal class TypeSerializerInfoRecord : TypeInfoRecord { public override BamlRecordType Type => BamlRecordType.TypeSerializerInfo; public ushort SerializerTypeId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { base.ReadData(reader, size); SerializerTypeId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { base.WriteData(writer); writer.Write(SerializerTypeId); } } internal class AttributeInfoRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.AttributeInfo; public ushort AttributeId { get; set; } public ushort OwnerTypeId { get; set; } public byte AttributeUsage { get; set; } public string Name { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { AttributeId = reader.ReadUInt16(); OwnerTypeId = reader.ReadUInt16(); AttributeUsage = reader.ReadByte(); Name = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(AttributeId); writer.Write(OwnerTypeId); writer.Write(AttributeUsage); writer.Write(Name); } } internal class StringInfoRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.StringInfo; public ushort StringId { get; set; } public string Value { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { StringId = reader.ReadUInt16(); Value = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(StringId); writer.Write(Value); } } internal class TextRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.Text; public string Value { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) => Value = reader.ReadString(); protected override void WriteData(BamlBinaryWriter writer) => writer.Write(Value); } internal class TextWithConverterRecord : TextRecord { public override BamlRecordType Type => BamlRecordType.TextWithConverter; public ushort ConverterTypeId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { base.ReadData(reader, size); ConverterTypeId = reader.ReadUInt16(); } protected override void WriteData(BamlBinaryWriter writer) { base.WriteData(writer); writer.Write(ConverterTypeId); } } internal class TextWithIdRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.TextWithId; public ushort ValueId { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) => ValueId = reader.ReadUInt16(); protected override void WriteData(BamlBinaryWriter writer) => writer.Write(ValueId); } internal class LiteralContentRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.LiteralContent; public string Value { get; set; } public uint Reserved0 { get; set; } public uint Reserved1 { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { Value = reader.ReadString(); Reserved0 = reader.ReadUInt32(); Reserved1 = reader.ReadUInt32(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(Value); writer.Write(Reserved0); writer.Write(Reserved1); } } internal class RoutedEventRecord : SizedBamlRecord { public override BamlRecordType Type => BamlRecordType.RoutedEvent; public string Value { get; set; } public ushort AttributeId { get; set; } public uint Reserved1 { get; set; } protected override void ReadData(BamlBinaryReader reader, int size) { AttributeId = reader.ReadUInt16(); Value = reader.ReadString(); } protected override void WriteData(BamlBinaryWriter writer) { writer.Write(Value); writer.Write(AttributeId); } } internal class DocumentStartRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.DocumentStart; public bool LoadAsync { get; set; } public uint MaxAsyncRecords { get; set; } public bool DebugBaml { get; set; } public override void Read(BamlBinaryReader reader) { LoadAsync = reader.ReadBoolean(); MaxAsyncRecords = reader.ReadUInt32(); DebugBaml = reader.ReadBoolean(); } public override void Write(BamlBinaryWriter writer) { writer.Write(LoadAsync); writer.Write(MaxAsyncRecords); writer.Write(DebugBaml); } } internal class DocumentEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.DocumentEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class ElementStartRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ElementStart; public ushort TypeId { get; set; } public byte Flags { get; set; } public override void Read(BamlBinaryReader reader) { TypeId = reader.ReadUInt16(); Flags = reader.ReadByte(); } public override void Write(BamlBinaryWriter writer) { writer.Write(TypeId); writer.Write(Flags); } } internal class ElementEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ElementEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class KeyElementStartRecord : DefAttributeKeyTypeRecord { public override BamlRecordType Type => BamlRecordType.KeyElementStart; } internal class KeyElementEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.KeyElementEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class ConnectionIdRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ConnectionId; public uint ConnectionId { get; set; } public override void Read(BamlBinaryReader reader) => ConnectionId = reader.ReadUInt32(); public override void Write(BamlBinaryWriter writer) => writer.Write(ConnectionId); } internal class PropertyWithExtensionRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyWithExtension; public ushort AttributeId { get; set; } public ushort Flags { get; set; } public ushort ValueId { get; set; } public override void Read(BamlBinaryReader reader) { AttributeId = reader.ReadUInt16(); Flags = reader.ReadUInt16(); ValueId = reader.ReadUInt16(); } public override void Write(BamlBinaryWriter writer) { writer.Write(AttributeId); writer.Write(Flags); writer.Write(ValueId); } } internal class PropertyTypeReferenceRecord : PropertyComplexStartRecord { public override BamlRecordType Type => BamlRecordType.PropertyTypeReference; public ushort TypeId { get; set; } public override void Read(BamlBinaryReader reader) { base.Read(reader); TypeId = reader.ReadUInt16(); } public override void Write(BamlBinaryWriter writer) { base.Write(writer); writer.Write(TypeId); } } internal class PropertyStringReferenceRecord : PropertyComplexStartRecord { public override BamlRecordType Type => BamlRecordType.PropertyStringReference; public ushort StringId { get; set; } public override void Read(BamlBinaryReader reader) { base.Read(reader); StringId = reader.ReadUInt16(); } public override void Write(BamlBinaryWriter writer) { base.Write(writer); writer.Write(StringId); } } internal class PropertyWithStaticResourceIdRecord : StaticResourceIdRecord { public override BamlRecordType Type => BamlRecordType.PropertyWithStaticResourceId; public ushort AttributeId { get; set; } public override void Read(BamlBinaryReader reader) { AttributeId = reader.ReadUInt16(); base.Read(reader); } public override void Write(BamlBinaryWriter writer) { writer.Write(AttributeId); base.Write(writer); } } internal class ContentPropertyRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ContentProperty; public ushort AttributeId { get; set; } public override void Read(BamlBinaryReader reader) => AttributeId = reader.ReadUInt16(); public override void Write(BamlBinaryWriter writer) => writer.Write(AttributeId); } internal class DefAttributeKeyTypeRecord : ElementStartRecord, IBamlDeferRecord { internal uint pos = 0xffffffff; public override BamlRecordType Type => BamlRecordType.DefAttributeKeyType; public bool Shared { get; set; } public bool SharedSet { get; set; } public BamlRecord Record { get; set; } public void ReadDefer(BamlDocument doc, int index, Func resolve) { bool keys = true; do { switch (doc[index].Type) { case BamlRecordType.DefAttributeKeyString: case BamlRecordType.DefAttributeKeyType: case BamlRecordType.OptimizedStaticResource: keys = true; break; case BamlRecordType.StaticResourceStart: NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index); keys = true; break; case BamlRecordType.KeyElementStart: NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index); keys = true; break; default: keys = false; index--; break; } index++; } while (keys); Record = resolve(doc[index].Position + pos); } public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) { bool keys = true; do { switch (doc[index].Type) { case BamlRecordType.DefAttributeKeyString: case BamlRecordType.DefAttributeKeyType: case BamlRecordType.OptimizedStaticResource: keys = true; break; case BamlRecordType.StaticResourceStart: NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index); keys = true; break; case BamlRecordType.KeyElementStart: NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index); keys = true; break; default: keys = false; index--; break; } index++; } while (keys); wtr.BaseStream.Seek(pos, SeekOrigin.Begin); wtr.Write((uint)(Record.Position - doc[index].Position)); } public override void Read(BamlBinaryReader reader) { base.Read(reader); pos = reader.ReadUInt32(); Shared = reader.ReadBoolean(); SharedSet = reader.ReadBoolean(); } public override void Write(BamlBinaryWriter writer) { base.Write(writer); pos = (uint)writer.BaseStream.Position; writer.Write((uint)0); writer.Write(Shared); writer.Write(SharedSet); } static void NavigateTree(BamlDocument doc, BamlRecordType start, BamlRecordType end, ref int index) { index++; while (true) { if (doc[index].Type == start) NavigateTree(doc, start, end, ref index); else if (doc[index].Type == end) return; index++; } } } internal class PropertyListStartRecord : PropertyComplexStartRecord { public override BamlRecordType Type => BamlRecordType.PropertyListStart; } internal class PropertyListEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyListEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class PropertyDictionaryStartRecord : PropertyComplexStartRecord { public override BamlRecordType Type => BamlRecordType.PropertyDictionaryStart; } internal class PropertyDictionaryEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyDictionaryEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class PropertyArrayStartRecord : PropertyComplexStartRecord { public override BamlRecordType Type => BamlRecordType.PropertyArrayStart; } internal class PropertyArrayEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyArrayEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class PropertyComplexStartRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyComplexStart; public ushort AttributeId { get; set; } public override void Read(BamlBinaryReader reader) => AttributeId = reader.ReadUInt16(); public override void Write(BamlBinaryWriter writer) => writer.Write(AttributeId); } internal class PropertyComplexEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.PropertyComplexEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class ConstructorParametersStartRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ConstructorParametersStart; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class ConstructorParametersEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ConstructorParametersEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class ConstructorParameterTypeRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.ConstructorParameterType; public ushort TypeId { get; set; } public override void Read(BamlBinaryReader reader) => TypeId = reader.ReadUInt16(); public override void Write(BamlBinaryWriter writer) => writer.Write(TypeId); } internal class DeferableContentStartRecord : BamlRecord, IBamlDeferRecord { long pos; internal uint size = 0xffffffff; public override BamlRecordType Type => BamlRecordType.DeferableContentStart; public BamlRecord Record { get; set; } public void ReadDefer(BamlDocument doc, int index, Func resolve) => Record = resolve(pos + size); public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) { wtr.BaseStream.Seek(pos, SeekOrigin.Begin); wtr.Write((uint)(Record.Position - (pos + 4))); } public override void Read(BamlBinaryReader reader) { size = reader.ReadUInt32(); pos = reader.BaseStream.Position; } public override void Write(BamlBinaryWriter writer) { pos = writer.BaseStream.Position; writer.Write((uint)0); } } internal class StaticResourceStartRecord : ElementStartRecord { public override BamlRecordType Type => BamlRecordType.StaticResourceStart; } internal class StaticResourceEndRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.StaticResourceEnd; public override void Read(BamlBinaryReader reader) { } public override void Write(BamlBinaryWriter writer) { } } internal class StaticResourceIdRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.StaticResourceId; public ushort StaticResourceId { get; set; } public override void Read(BamlBinaryReader reader) => StaticResourceId = reader.ReadUInt16(); public override void Write(BamlBinaryWriter writer) => writer.Write(StaticResourceId); } internal class OptimizedStaticResourceRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.OptimizedStaticResource; public byte Flags { get; set; } public ushort ValueId { get; set; } public bool IsType => (Flags & 1) != 0; public bool IsStatic => (Flags & 2) != 0; public override void Read(BamlBinaryReader reader) { Flags = reader.ReadByte(); ValueId = reader.ReadUInt16(); } public override void Write(BamlBinaryWriter writer) { writer.Write(Flags); writer.Write(ValueId); } } internal class LineNumberAndPositionRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.LineNumberAndPosition; public uint LineNumber { get; set; } public uint LinePosition { get; set; } public override void Read(BamlBinaryReader reader) { LineNumber = reader.ReadUInt32(); LinePosition = reader.ReadUInt32(); } public override void Write(BamlBinaryWriter writer) { writer.Write(LineNumber); writer.Write(LinePosition); } } internal class LinePositionRecord : BamlRecord { public override BamlRecordType Type => BamlRecordType.LinePosition; public uint LinePosition { get; set; } public override void Read(BamlBinaryReader reader) => LinePosition = reader.ReadUInt32(); public override void Write(BamlBinaryWriter writer) => writer.Write(LinePosition); } internal class NamedElementStartRecord : ElementStartRecord { public override BamlRecordType Type => BamlRecordType.NamedElementStart; public string RuntimeName { get; set; } public override void Read(BamlBinaryReader reader) { TypeId = reader.ReadUInt16(); RuntimeName = reader.ReadString(); } public override void Write(BamlBinaryWriter writer) { writer.Write(TypeId); if (RuntimeName != null) { writer.Write(RuntimeName); } } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/BamlWriter.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.IO; using System.Text; namespace ICSharpCode.BamlDecompiler.Baml { internal class BamlBinaryWriter : BinaryWriter { public BamlBinaryWriter(Stream stream) : base(stream) { } public void WriteEncodedInt(int val) => Write7BitEncodedInt(val); } internal class BamlWriter { public static void WriteDocument(BamlDocument doc, Stream str) { var writer = new BamlBinaryWriter(str); { var wtr = new BinaryWriter(str, Encoding.Unicode); int len = doc.Signature.Length * 2; wtr.Write(len); wtr.Write(doc.Signature.ToCharArray()); wtr.Write(new byte[((len + 3) & ~3) - len]); } writer.Write(doc.ReaderVersion.Major); writer.Write(doc.ReaderVersion.Minor); writer.Write(doc.UpdaterVersion.Major); writer.Write(doc.UpdaterVersion.Minor); writer.Write(doc.WriterVersion.Major); writer.Write(doc.WriterVersion.Minor); var defers = new List(); for (int i = 0; i < doc.Count; i++) { BamlRecord rec = doc[i]; rec.Position = str.Position; writer.Write((byte)rec.Type); rec.Write(writer); if (rec is IBamlDeferRecord) defers.Add(i); } foreach (int i in defers) (doc[i] as IBamlDeferRecord).WriteDefer(doc, i, writer); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/KnownMembers.cs ================================================ /* Copyright (c) 2015 Ki 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. */ namespace ICSharpCode.BamlDecompiler.Baml { // Auto generated. Do not modify. internal enum KnownMembers : short { Unknown = 0, AccessText_Text = 1, BeginStoryboard_Storyboard = 2, BitmapEffectGroup_Children = 3, Border_Background = 4, Border_BorderBrush = 5, Border_BorderThickness = 6, ButtonBase_Command = 7, ButtonBase_CommandParameter = 8, ButtonBase_CommandTarget = 9, ButtonBase_IsPressed = 10, ColumnDefinition_MaxWidth = 11, ColumnDefinition_MinWidth = 12, ColumnDefinition_Width = 13, ContentControl_Content = 14, ContentControl_ContentTemplate = 15, ContentControl_ContentTemplateSelector = 16, ContentControl_HasContent = 17, ContentElement_Focusable = 18, ContentPresenter_Content = 19, ContentPresenter_ContentSource = 20, ContentPresenter_ContentTemplate = 21, ContentPresenter_ContentTemplateSelector = 22, ContentPresenter_RecognizesAccessKey = 23, Control_Background = 24, Control_BorderBrush = 25, Control_BorderThickness = 26, Control_FontFamily = 27, Control_FontSize = 28, Control_FontStretch = 29, Control_FontStyle = 30, Control_FontWeight = 31, Control_Foreground = 32, Control_HorizontalContentAlignment = 33, Control_IsTabStop = 34, Control_Padding = 35, Control_TabIndex = 36, Control_Template = 37, Control_VerticalContentAlignment = 38, DockPanel_Dock = 39, DockPanel_LastChildFill = 40, DocumentViewerBase_Document = 41, DrawingGroup_Children = 42, FlowDocumentReader_Document = 43, FlowDocumentScrollViewer_Document = 44, FrameworkContentElement_Style = 45, FrameworkElement_FlowDirection = 46, FrameworkElement_Height = 47, FrameworkElement_HorizontalAlignment = 48, FrameworkElement_Margin = 49, FrameworkElement_MaxHeight = 50, FrameworkElement_MaxWidth = 51, FrameworkElement_MinHeight = 52, FrameworkElement_MinWidth = 53, FrameworkElement_Name = 54, FrameworkElement_Style = 55, FrameworkElement_VerticalAlignment = 56, FrameworkElement_Width = 57, GeneralTransformGroup_Children = 58, GeometryGroup_Children = 59, GradientBrush_GradientStops = 60, Grid_Column = 61, Grid_ColumnSpan = 62, Grid_Row = 63, Grid_RowSpan = 64, GridViewColumn_Header = 65, HeaderedContentControl_HasHeader = 66, HeaderedContentControl_Header = 67, HeaderedContentControl_HeaderTemplate = 68, HeaderedContentControl_HeaderTemplateSelector = 69, HeaderedItemsControl_HasHeader = 70, HeaderedItemsControl_Header = 71, HeaderedItemsControl_HeaderTemplate = 72, HeaderedItemsControl_HeaderTemplateSelector = 73, Hyperlink_NavigateUri = 74, Image_Source = 75, Image_Stretch = 76, ItemsControl_ItemContainerStyle = 77, ItemsControl_ItemContainerStyleSelector = 78, ItemsControl_ItemTemplate = 79, ItemsControl_ItemTemplateSelector = 80, ItemsControl_ItemsPanel = 81, ItemsControl_ItemsSource = 82, MaterialGroup_Children = 83, Model3DGroup_Children = 84, Page_Content = 85, Panel_Background = 86, Path_Data = 87, PathFigure_Segments = 88, PathGeometry_Figures = 89, Popup_Child = 90, Popup_IsOpen = 91, Popup_Placement = 92, Popup_PopupAnimation = 93, RowDefinition_Height = 94, RowDefinition_MaxHeight = 95, RowDefinition_MinHeight = 96, ScrollViewer_CanContentScroll = 97, ScrollViewer_HorizontalScrollBarVisibility = 98, ScrollViewer_VerticalScrollBarVisibility = 99, Shape_Fill = 100, Shape_Stroke = 101, Shape_StrokeThickness = 102, TextBlock_Background = 103, TextBlock_FontFamily = 104, TextBlock_FontSize = 105, TextBlock_FontStretch = 106, TextBlock_FontStyle = 107, TextBlock_FontWeight = 108, TextBlock_Foreground = 109, TextBlock_Text = 110, TextBlock_TextDecorations = 111, TextBlock_TextTrimming = 112, TextBlock_TextWrapping = 113, TextBox_Text = 114, TextElement_Background = 115, TextElement_FontFamily = 116, TextElement_FontSize = 117, TextElement_FontStretch = 118, TextElement_FontStyle = 119, TextElement_FontWeight = 120, TextElement_Foreground = 121, TimelineGroup_Children = 122, Track_IsDirectionReversed = 123, Track_Maximum = 124, Track_Minimum = 125, Track_Orientation = 126, Track_Value = 127, Track_ViewportSize = 128, Transform3DGroup_Children = 129, TransformGroup_Children = 130, UIElement_ClipToBounds = 131, UIElement_Focusable = 132, UIElement_IsEnabled = 133, UIElement_RenderTransform = 134, UIElement_Visibility = 135, Viewport3D_Children = 136, AdornedElementPlaceholder_Child = 138, AdornerDecorator_Child = 139, AnchoredBlock_Blocks = 140, ArrayExtension_Items = 141, BlockUIContainer_Child = 142, Bold_Inlines = 143, BooleanAnimationUsingKeyFrames_KeyFrames = 144, Border_Child = 145, BulletDecorator_Child = 146, Button_Content = 147, ButtonBase_Content = 148, ByteAnimationUsingKeyFrames_KeyFrames = 149, Canvas_Children = 150, CharAnimationUsingKeyFrames_KeyFrames = 151, CheckBox_Content = 152, ColorAnimationUsingKeyFrames_KeyFrames = 153, ComboBox_Items = 154, ComboBoxItem_Content = 155, ContextMenu_Items = 156, ControlTemplate_VisualTree = 157, DataTemplate_VisualTree = 158, DataTrigger_Setters = 159, DecimalAnimationUsingKeyFrames_KeyFrames = 160, Decorator_Child = 161, DockPanel_Children = 162, DocumentViewer_Document = 163, DoubleAnimationUsingKeyFrames_KeyFrames = 164, EventTrigger_Actions = 165, Expander_Content = 166, Figure_Blocks = 167, FixedDocument_Pages = 168, FixedDocumentSequence_References = 169, FixedPage_Children = 170, Floater_Blocks = 171, FlowDocument_Blocks = 172, FlowDocumentPageViewer_Document = 173, FrameworkTemplate_VisualTree = 174, Grid_Children = 175, GridView_Columns = 176, GridViewColumnHeader_Content = 177, GroupBox_Content = 178, GroupItem_Content = 179, HeaderedContentControl_Content = 180, HeaderedItemsControl_Items = 181, HierarchicalDataTemplate_VisualTree = 182, Hyperlink_Inlines = 183, InkCanvas_Children = 184, InkPresenter_Child = 185, InlineUIContainer_Child = 186, InputScopeName_NameValue = 187, Int16AnimationUsingKeyFrames_KeyFrames = 188, Int32AnimationUsingKeyFrames_KeyFrames = 189, Int64AnimationUsingKeyFrames_KeyFrames = 190, Italic_Inlines = 191, ItemsControl_Items = 192, ItemsPanelTemplate_VisualTree = 193, Label_Content = 194, LinearGradientBrush_GradientStops = 195, List_ListItems = 196, ListBox_Items = 197, ListBoxItem_Content = 198, ListItem_Blocks = 199, ListView_Items = 200, ListViewItem_Content = 201, MatrixAnimationUsingKeyFrames_KeyFrames = 202, Menu_Items = 203, MenuBase_Items = 204, MenuItem_Items = 205, ModelVisual3D_Children = 206, MultiBinding_Bindings = 207, MultiDataTrigger_Setters = 208, MultiTrigger_Setters = 209, ObjectAnimationUsingKeyFrames_KeyFrames = 210, PageContent_Child = 211, PageFunctionBase_Content = 212, Panel_Children = 213, Paragraph_Inlines = 214, ParallelTimeline_Children = 215, Point3DAnimationUsingKeyFrames_KeyFrames = 216, PointAnimationUsingKeyFrames_KeyFrames = 217, PriorityBinding_Bindings = 218, QuaternionAnimationUsingKeyFrames_KeyFrames = 219, RadialGradientBrush_GradientStops = 220, RadioButton_Content = 221, RectAnimationUsingKeyFrames_KeyFrames = 222, RepeatButton_Content = 223, RichTextBox_Document = 224, Rotation3DAnimationUsingKeyFrames_KeyFrames = 225, Run_Text = 226, ScrollViewer_Content = 227, Section_Blocks = 228, Selector_Items = 229, SingleAnimationUsingKeyFrames_KeyFrames = 230, SizeAnimationUsingKeyFrames_KeyFrames = 231, Span_Inlines = 232, StackPanel_Children = 233, StatusBar_Items = 234, StatusBarItem_Content = 235, Storyboard_Children = 236, StringAnimationUsingKeyFrames_KeyFrames = 237, Style_Setters = 238, TabControl_Items = 239, TabItem_Content = 240, TabPanel_Children = 241, Table_RowGroups = 242, TableCell_Blocks = 243, TableRow_Cells = 244, TableRowGroup_Rows = 245, TextBlock_Inlines = 246, ThicknessAnimationUsingKeyFrames_KeyFrames = 247, ToggleButton_Content = 248, ToolBar_Items = 249, ToolBarOverflowPanel_Children = 250, ToolBarPanel_Children = 251, ToolBarTray_ToolBars = 252, ToolTip_Content = 253, TreeView_Items = 254, TreeViewItem_Items = 255, Trigger_Setters = 256, Underline_Inlines = 257, UniformGrid_Children = 258, UserControl_Content = 259, Vector3DAnimationUsingKeyFrames_KeyFrames = 260, VectorAnimationUsingKeyFrames_KeyFrames = 261, Viewbox_Child = 262, Viewport3DVisual_Children = 263, VirtualizingPanel_Children = 264, VirtualizingStackPanel_Children = 265, Window_Content = 266, WrapPanel_Children = 267, XmlDataProvider_XmlSerializer = 268, } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/KnownThings.cs ================================================ /* Copyright (c) 2015 Ki 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; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler.Baml { internal partial class KnownThings { readonly IDecompilerTypeSystem typeSystem; readonly Dictionary assemblies; readonly Dictionary members; readonly Dictionary types; readonly Dictionary strings; readonly Dictionary resources; public KnownThings(IDecompilerTypeSystem typeSystem) { this.typeSystem = typeSystem; assemblies = new Dictionary(); types = new Dictionary(); members = new Dictionary(); strings = new Dictionary(); resources = new Dictionary(); try { InitAssemblies(); InitTypes(); InitMembers(); InitStrings(); InitResources(); } catch (Exception ex) { throw new ICSharpCode.Decompiler.DecompilerException(typeSystem.MainModule.MetadataFile, ex.Message, ex); } } public Func Types => id => types[id]; public Func Members => id => members[id]; public Func Strings => id => strings[id]; public Func Resources => id => resources[id]; public IModule FrameworkAssembly => assemblies[0]; IModule ResolveAssembly(string name) { IModule module = typeSystem.Modules.FirstOrDefault(m => m.AssemblyName == name); if (module == null) throw new Exception("Could not resolve known assembly '" + name + "'!"); return module; } ITypeDefinition InitType(IModule assembly, string ns, string name) => assembly.GetTypeDefinition(new TopLevelTypeName(ns, name)); KnownMember InitMember(KnownTypes parent, string name, ITypeDefinition type) => new KnownMember(parent, types[parent], name, type); } internal class KnownMember { public KnownMember(KnownTypes parent, ITypeDefinition declType, string name, ITypeDefinition type) { Parent = parent; Property = declType.GetProperties(p => p.Name == name, GetMemberOptions.IgnoreInheritedMembers).SingleOrDefault(); DeclaringType = declType; Name = name; Type = type; } public KnownTypes Parent { get; } public ITypeDefinition DeclaringType { get; } public IProperty Property { get; } public string Name { get; } public ITypeDefinition Type { get; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/KnownThings.g.cs ================================================ /* Copyright (c) 2015 Ki 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; namespace ICSharpCode.BamlDecompiler.Baml { internal partial class KnownThings { // Auto-generated. Do not modify. void InitAssemblies() { assemblies[0] = ResolveAssembly("mscorlib"); assemblies[1] = ResolveAssembly("System"); assemblies[2] = ResolveAssembly("WindowsBase"); assemblies[3] = ResolveAssembly("PresentationCore"); assemblies[4] = ResolveAssembly("PresentationFramework"); assemblies[5] = ResolveAssembly("System.Xml"); } void InitTypes() { types[KnownTypes.AccessText] = InitType(assemblies[4], "System.Windows.Controls", "AccessText"); types[KnownTypes.AdornedElementPlaceholder] = InitType(assemblies[4], "System.Windows.Controls", "AdornedElementPlaceholder"); types[KnownTypes.Adorner] = InitType(assemblies[4], "System.Windows.Documents", "Adorner"); types[KnownTypes.AdornerDecorator] = InitType(assemblies[4], "System.Windows.Documents", "AdornerDecorator"); types[KnownTypes.AdornerLayer] = InitType(assemblies[4], "System.Windows.Documents", "AdornerLayer"); types[KnownTypes.AffineTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AffineTransform3D"); types[KnownTypes.AmbientLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AmbientLight"); types[KnownTypes.AnchoredBlock] = InitType(assemblies[4], "System.Windows.Documents", "AnchoredBlock"); types[KnownTypes.Animatable] = InitType(assemblies[3], "System.Windows.Media.Animation", "Animatable"); types[KnownTypes.AnimationClock] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationClock"); types[KnownTypes.AnimationTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationTimeline"); types[KnownTypes.Application] = InitType(assemblies[4], "System.Windows", "Application"); types[KnownTypes.ArcSegment] = InitType(assemblies[3], "System.Windows.Media", "ArcSegment"); types[KnownTypes.ArrayExtension] = InitType(assemblies[4], "System.Windows.Markup", "ArrayExtension"); types[KnownTypes.AxisAngleRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AxisAngleRotation3D"); types[KnownTypes.BaseIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BaseIListConverter"); types[KnownTypes.BeginStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "BeginStoryboard"); types[KnownTypes.BevelBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BevelBitmapEffect"); types[KnownTypes.BezierSegment] = InitType(assemblies[3], "System.Windows.Media", "BezierSegment"); types[KnownTypes.Binding] = InitType(assemblies[4], "System.Windows.Data", "Binding"); types[KnownTypes.BindingBase] = InitType(assemblies[4], "System.Windows.Data", "BindingBase"); types[KnownTypes.BindingExpression] = InitType(assemblies[4], "System.Windows.Data", "BindingExpression"); types[KnownTypes.BindingExpressionBase] = InitType(assemblies[4], "System.Windows.Data", "BindingExpressionBase"); types[KnownTypes.BindingListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "BindingListCollectionView"); types[KnownTypes.BitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapDecoder"); types[KnownTypes.BitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffect"); types[KnownTypes.BitmapEffectCollection] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection"); types[KnownTypes.BitmapEffectGroup] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectGroup"); types[KnownTypes.BitmapEffectInput] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectInput"); types[KnownTypes.BitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapEncoder"); types[KnownTypes.BitmapFrame] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapFrame"); types[KnownTypes.BitmapImage] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapImage"); types[KnownTypes.BitmapMetadata] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapMetadata"); types[KnownTypes.BitmapPalette] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapPalette"); types[KnownTypes.BitmapSource] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapSource"); types[KnownTypes.Block] = InitType(assemblies[4], "System.Windows.Documents", "Block"); types[KnownTypes.BlockUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "BlockUIContainer"); types[KnownTypes.BlurBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BlurBitmapEffect"); types[KnownTypes.BmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapDecoder"); types[KnownTypes.BmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapEncoder"); types[KnownTypes.Bold] = InitType(assemblies[4], "System.Windows.Documents", "Bold"); types[KnownTypes.BoolIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BoolIListConverter"); types[KnownTypes.Boolean] = InitType(assemblies[0], "System", "Boolean"); types[KnownTypes.BooleanAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationBase"); types[KnownTypes.BooleanAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationUsingKeyFrames"); types[KnownTypes.BooleanConverter] = InitType(assemblies[1], "System.ComponentModel", "BooleanConverter"); types[KnownTypes.BooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrame"); types[KnownTypes.BooleanKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection"); types[KnownTypes.BooleanToVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "BooleanToVisibilityConverter"); types[KnownTypes.Border] = InitType(assemblies[4], "System.Windows.Controls", "Border"); types[KnownTypes.BorderGapMaskConverter] = InitType(assemblies[4], "System.Windows.Controls", "BorderGapMaskConverter"); types[KnownTypes.Brush] = InitType(assemblies[3], "System.Windows.Media", "Brush"); types[KnownTypes.BrushConverter] = InitType(assemblies[3], "System.Windows.Media", "BrushConverter"); types[KnownTypes.BulletDecorator] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "BulletDecorator"); types[KnownTypes.Button] = InitType(assemblies[4], "System.Windows.Controls", "Button"); types[KnownTypes.ButtonBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ButtonBase"); types[KnownTypes.Byte] = InitType(assemblies[0], "System", "Byte"); types[KnownTypes.ByteAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimation"); types[KnownTypes.ByteAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationBase"); types[KnownTypes.ByteAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationUsingKeyFrames"); types[KnownTypes.ByteConverter] = InitType(assemblies[1], "System.ComponentModel", "ByteConverter"); types[KnownTypes.ByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrame"); types[KnownTypes.ByteKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection"); types[KnownTypes.CachedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CachedBitmap"); types[KnownTypes.Camera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Camera"); types[KnownTypes.Canvas] = InitType(assemblies[4], "System.Windows.Controls", "Canvas"); types[KnownTypes.Char] = InitType(assemblies[0], "System", "Char"); types[KnownTypes.CharAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationBase"); types[KnownTypes.CharAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationUsingKeyFrames"); types[KnownTypes.CharConverter] = InitType(assemblies[1], "System.ComponentModel", "CharConverter"); types[KnownTypes.CharIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "CharIListConverter"); types[KnownTypes.CharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrame"); types[KnownTypes.CharKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection"); types[KnownTypes.CheckBox] = InitType(assemblies[4], "System.Windows.Controls", "CheckBox"); types[KnownTypes.Clock] = InitType(assemblies[3], "System.Windows.Media.Animation", "Clock"); types[KnownTypes.ClockController] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockController"); types[KnownTypes.ClockGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockGroup"); types[KnownTypes.CollectionContainer] = InitType(assemblies[4], "System.Windows.Data", "CollectionContainer"); types[KnownTypes.CollectionView] = InitType(assemblies[4], "System.Windows.Data", "CollectionView"); types[KnownTypes.CollectionViewSource] = InitType(assemblies[4], "System.Windows.Data", "CollectionViewSource"); types[KnownTypes.Color] = InitType(assemblies[3], "System.Windows.Media", "Color"); types[KnownTypes.ColorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimation"); types[KnownTypes.ColorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationBase"); types[KnownTypes.ColorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationUsingKeyFrames"); types[KnownTypes.ColorConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "ColorConvertedBitmap"); types[KnownTypes.ColorConvertedBitmapExtension] = InitType(assemblies[4], "System.Windows", "ColorConvertedBitmapExtension"); types[KnownTypes.ColorConverter] = InitType(assemblies[3], "System.Windows.Media", "ColorConverter"); types[KnownTypes.ColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrame"); types[KnownTypes.ColorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection"); types[KnownTypes.ColumnDefinition] = InitType(assemblies[4], "System.Windows.Controls", "ColumnDefinition"); types[KnownTypes.CombinedGeometry] = InitType(assemblies[3], "System.Windows.Media", "CombinedGeometry"); types[KnownTypes.ComboBox] = InitType(assemblies[4], "System.Windows.Controls", "ComboBox"); types[KnownTypes.ComboBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ComboBoxItem"); types[KnownTypes.CommandConverter] = InitType(assemblies[4], "System.Windows.Input", "CommandConverter"); types[KnownTypes.ComponentResourceKey] = InitType(assemblies[4], "System.Windows", "ComponentResourceKey"); types[KnownTypes.ComponentResourceKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "ComponentResourceKeyConverter"); types[KnownTypes.CompositionTarget] = InitType(assemblies[3], "System.Windows.Media", "CompositionTarget"); types[KnownTypes.Condition] = InitType(assemblies[4], "System.Windows", "Condition"); types[KnownTypes.ContainerVisual] = InitType(assemblies[3], "System.Windows.Media", "ContainerVisual"); types[KnownTypes.ContentControl] = InitType(assemblies[4], "System.Windows.Controls", "ContentControl"); types[KnownTypes.ContentElement] = InitType(assemblies[3], "System.Windows", "ContentElement"); types[KnownTypes.ContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ContentPresenter"); types[KnownTypes.ContentPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentPropertyAttribute"); types[KnownTypes.ContentWrapperAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentWrapperAttribute"); types[KnownTypes.ContextMenu] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenu"); types[KnownTypes.ContextMenuService] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenuService"); types[KnownTypes.Control] = InitType(assemblies[4], "System.Windows.Controls", "Control"); types[KnownTypes.ControlTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate"); types[KnownTypes.ControllableStoryboardAction] = InitType(assemblies[4], "System.Windows.Media.Animation", "ControllableStoryboardAction"); types[KnownTypes.CornerRadius] = InitType(assemblies[4], "System.Windows", "CornerRadius"); types[KnownTypes.CornerRadiusConverter] = InitType(assemblies[4], "System.Windows", "CornerRadiusConverter"); types[KnownTypes.CroppedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CroppedBitmap"); types[KnownTypes.CultureInfo] = InitType(assemblies[0], "System.Globalization", "CultureInfo"); types[KnownTypes.CultureInfoConverter] = InitType(assemblies[1], "System.ComponentModel", "CultureInfoConverter"); types[KnownTypes.CultureInfoIetfLanguageTagConverter] = InitType(assemblies[3], "System.Windows", "CultureInfoIetfLanguageTagConverter"); types[KnownTypes.Cursor] = InitType(assemblies[3], "System.Windows.Input", "Cursor"); types[KnownTypes.CursorConverter] = InitType(assemblies[3], "System.Windows.Input", "CursorConverter"); types[KnownTypes.DashStyle] = InitType(assemblies[3], "System.Windows.Media", "DashStyle"); types[KnownTypes.DataChangedEventManager] = InitType(assemblies[4], "System.Windows.Data", "DataChangedEventManager"); types[KnownTypes.DataTemplate] = InitType(assemblies[4], "System.Windows", "DataTemplate"); types[KnownTypes.DataTemplateKey] = InitType(assemblies[4], "System.Windows", "DataTemplateKey"); types[KnownTypes.DataTrigger] = InitType(assemblies[4], "System.Windows", "DataTrigger"); types[KnownTypes.DateTime] = InitType(assemblies[0], "System", "DateTime"); types[KnownTypes.DateTimeConverter] = InitType(assemblies[1], "System.ComponentModel", "DateTimeConverter"); types[KnownTypes.DateTimeConverter2] = InitType(assemblies[2], "System.Windows.Markup", "DateTimeConverter2"); types[KnownTypes.Decimal] = InitType(assemblies[0], "System", "Decimal"); types[KnownTypes.DecimalAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimation"); types[KnownTypes.DecimalAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationBase"); types[KnownTypes.DecimalAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationUsingKeyFrames"); types[KnownTypes.DecimalConverter] = InitType(assemblies[1], "System.ComponentModel", "DecimalConverter"); types[KnownTypes.DecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrame"); types[KnownTypes.DecimalKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection"); types[KnownTypes.Decorator] = InitType(assemblies[4], "System.Windows.Controls", "Decorator"); types[KnownTypes.DefinitionBase] = InitType(assemblies[4], "System.Windows.Controls", "DefinitionBase"); types[KnownTypes.DependencyObject] = InitType(assemblies[2], "System.Windows", "DependencyObject"); types[KnownTypes.DependencyProperty] = InitType(assemblies[2], "System.Windows", "DependencyProperty"); types[KnownTypes.DependencyPropertyConverter] = InitType(assemblies[4], "System.Windows.Markup", "DependencyPropertyConverter"); types[KnownTypes.DialogResultConverter] = InitType(assemblies[4], "System.Windows", "DialogResultConverter"); types[KnownTypes.DiffuseMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DiffuseMaterial"); types[KnownTypes.DirectionalLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DirectionalLight"); types[KnownTypes.DiscreteBooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteBooleanKeyFrame"); types[KnownTypes.DiscreteByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteByteKeyFrame"); types[KnownTypes.DiscreteCharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteCharKeyFrame"); types[KnownTypes.DiscreteColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteColorKeyFrame"); types[KnownTypes.DiscreteDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDecimalKeyFrame"); types[KnownTypes.DiscreteDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDoubleKeyFrame"); types[KnownTypes.DiscreteInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt16KeyFrame"); types[KnownTypes.DiscreteInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt32KeyFrame"); types[KnownTypes.DiscreteInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt64KeyFrame"); types[KnownTypes.DiscreteMatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteMatrixKeyFrame"); types[KnownTypes.DiscreteObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteObjectKeyFrame"); types[KnownTypes.DiscretePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePoint3DKeyFrame"); types[KnownTypes.DiscretePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePointKeyFrame"); types[KnownTypes.DiscreteQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteQuaternionKeyFrame"); types[KnownTypes.DiscreteRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRectKeyFrame"); types[KnownTypes.DiscreteRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRotation3DKeyFrame"); types[KnownTypes.DiscreteSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSingleKeyFrame"); types[KnownTypes.DiscreteSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSizeKeyFrame"); types[KnownTypes.DiscreteStringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteStringKeyFrame"); types[KnownTypes.DiscreteThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "DiscreteThicknessKeyFrame"); types[KnownTypes.DiscreteVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVector3DKeyFrame"); types[KnownTypes.DiscreteVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVectorKeyFrame"); types[KnownTypes.DockPanel] = InitType(assemblies[4], "System.Windows.Controls", "DockPanel"); types[KnownTypes.DocumentPageView] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentPageView"); types[KnownTypes.DocumentReference] = InitType(assemblies[4], "System.Windows.Documents", "DocumentReference"); types[KnownTypes.DocumentViewer] = InitType(assemblies[4], "System.Windows.Controls", "DocumentViewer"); types[KnownTypes.DocumentViewerBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentViewerBase"); types[KnownTypes.Double] = InitType(assemblies[0], "System", "Double"); types[KnownTypes.DoubleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimation"); types[KnownTypes.DoubleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationBase"); types[KnownTypes.DoubleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingKeyFrames"); types[KnownTypes.DoubleAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingPath"); types[KnownTypes.DoubleCollection] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollection"); types[KnownTypes.DoubleCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollectionConverter"); types[KnownTypes.DoubleConverter] = InitType(assemblies[1], "System.ComponentModel", "DoubleConverter"); types[KnownTypes.DoubleIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "DoubleIListConverter"); types[KnownTypes.DoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrame"); types[KnownTypes.DoubleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection"); types[KnownTypes.Drawing] = InitType(assemblies[3], "System.Windows.Media", "Drawing"); types[KnownTypes.DrawingBrush] = InitType(assemblies[3], "System.Windows.Media", "DrawingBrush"); types[KnownTypes.DrawingCollection] = InitType(assemblies[3], "System.Windows.Media", "DrawingCollection"); types[KnownTypes.DrawingContext] = InitType(assemblies[3], "System.Windows.Media", "DrawingContext"); types[KnownTypes.DrawingGroup] = InitType(assemblies[3], "System.Windows.Media", "DrawingGroup"); types[KnownTypes.DrawingImage] = InitType(assemblies[3], "System.Windows.Media", "DrawingImage"); types[KnownTypes.DrawingVisual] = InitType(assemblies[3], "System.Windows.Media", "DrawingVisual"); types[KnownTypes.DropShadowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "DropShadowBitmapEffect"); types[KnownTypes.Duration] = InitType(assemblies[3], "System.Windows", "Duration"); types[KnownTypes.DurationConverter] = InitType(assemblies[3], "System.Windows", "DurationConverter"); types[KnownTypes.DynamicResourceExtension] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtension"); types[KnownTypes.DynamicResourceExtensionConverter] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtensionConverter"); types[KnownTypes.Ellipse] = InitType(assemblies[4], "System.Windows.Shapes", "Ellipse"); types[KnownTypes.EllipseGeometry] = InitType(assemblies[3], "System.Windows.Media", "EllipseGeometry"); types[KnownTypes.EmbossBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "EmbossBitmapEffect"); types[KnownTypes.EmissiveMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "EmissiveMaterial"); types[KnownTypes.EnumConverter] = InitType(assemblies[1], "System.ComponentModel", "EnumConverter"); types[KnownTypes.EventManager] = InitType(assemblies[3], "System.Windows", "EventManager"); types[KnownTypes.EventSetter] = InitType(assemblies[4], "System.Windows", "EventSetter"); types[KnownTypes.EventTrigger] = InitType(assemblies[4], "System.Windows", "EventTrigger"); types[KnownTypes.Expander] = InitType(assemblies[4], "System.Windows.Controls", "Expander"); types[KnownTypes.Expression] = InitType(assemblies[2], "System.Windows", "Expression"); types[KnownTypes.ExpressionConverter] = InitType(assemblies[2], "System.Windows", "ExpressionConverter"); types[KnownTypes.Figure] = InitType(assemblies[4], "System.Windows.Documents", "Figure"); types[KnownTypes.FigureLength] = InitType(assemblies[4], "System.Windows", "FigureLength"); types[KnownTypes.FigureLengthConverter] = InitType(assemblies[4], "System.Windows", "FigureLengthConverter"); types[KnownTypes.FixedDocument] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocument"); types[KnownTypes.FixedDocumentSequence] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocumentSequence"); types[KnownTypes.FixedPage] = InitType(assemblies[4], "System.Windows.Documents", "FixedPage"); types[KnownTypes.Floater] = InitType(assemblies[4], "System.Windows.Documents", "Floater"); types[KnownTypes.FlowDocument] = InitType(assemblies[4], "System.Windows.Documents", "FlowDocument"); types[KnownTypes.FlowDocumentPageViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentPageViewer"); types[KnownTypes.FlowDocumentReader] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentReader"); types[KnownTypes.FlowDocumentScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentScrollViewer"); types[KnownTypes.FocusManager] = InitType(assemblies[3], "System.Windows.Input", "FocusManager"); types[KnownTypes.FontFamily] = InitType(assemblies[3], "System.Windows.Media", "FontFamily"); types[KnownTypes.FontFamilyConverter] = InitType(assemblies[3], "System.Windows.Media", "FontFamilyConverter"); types[KnownTypes.FontSizeConverter] = InitType(assemblies[4], "System.Windows", "FontSizeConverter"); types[KnownTypes.FontStretch] = InitType(assemblies[3], "System.Windows", "FontStretch"); types[KnownTypes.FontStretchConverter] = InitType(assemblies[3], "System.Windows", "FontStretchConverter"); types[KnownTypes.FontStyle] = InitType(assemblies[3], "System.Windows", "FontStyle"); types[KnownTypes.FontStyleConverter] = InitType(assemblies[3], "System.Windows", "FontStyleConverter"); types[KnownTypes.FontWeight] = InitType(assemblies[3], "System.Windows", "FontWeight"); types[KnownTypes.FontWeightConverter] = InitType(assemblies[3], "System.Windows", "FontWeightConverter"); types[KnownTypes.FormatConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "FormatConvertedBitmap"); types[KnownTypes.Frame] = InitType(assemblies[4], "System.Windows.Controls", "Frame"); types[KnownTypes.FrameworkContentElement] = InitType(assemblies[4], "System.Windows", "FrameworkContentElement"); types[KnownTypes.FrameworkElement] = InitType(assemblies[4], "System.Windows", "FrameworkElement"); types[KnownTypes.FrameworkElementFactory] = InitType(assemblies[4], "System.Windows", "FrameworkElementFactory"); types[KnownTypes.FrameworkPropertyMetadata] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadata"); types[KnownTypes.FrameworkPropertyMetadataOptions] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadataOptions"); types[KnownTypes.FrameworkRichTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkRichTextComposition"); types[KnownTypes.FrameworkTemplate] = InitType(assemblies[4], "System.Windows", "FrameworkTemplate"); types[KnownTypes.FrameworkTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkTextComposition"); types[KnownTypes.Freezable] = InitType(assemblies[2], "System.Windows", "Freezable"); types[KnownTypes.GeneralTransform] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransform"); types[KnownTypes.GeneralTransformCollection] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection"); types[KnownTypes.GeneralTransformGroup] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformGroup"); types[KnownTypes.Geometry] = InitType(assemblies[3], "System.Windows.Media", "Geometry"); types[KnownTypes.Geometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Geometry3D"); types[KnownTypes.GeometryCollection] = InitType(assemblies[3], "System.Windows.Media", "GeometryCollection"); types[KnownTypes.GeometryConverter] = InitType(assemblies[3], "System.Windows.Media", "GeometryConverter"); types[KnownTypes.GeometryDrawing] = InitType(assemblies[3], "System.Windows.Media", "GeometryDrawing"); types[KnownTypes.GeometryGroup] = InitType(assemblies[3], "System.Windows.Media", "GeometryGroup"); types[KnownTypes.GeometryModel3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "GeometryModel3D"); types[KnownTypes.GestureRecognizer] = InitType(assemblies[3], "System.Windows.Ink", "GestureRecognizer"); types[KnownTypes.GifBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapDecoder"); types[KnownTypes.GifBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapEncoder"); types[KnownTypes.GlyphRun] = InitType(assemblies[3], "System.Windows.Media", "GlyphRun"); types[KnownTypes.GlyphRunDrawing] = InitType(assemblies[3], "System.Windows.Media", "GlyphRunDrawing"); types[KnownTypes.GlyphTypeface] = InitType(assemblies[3], "System.Windows.Media", "GlyphTypeface"); types[KnownTypes.Glyphs] = InitType(assemblies[4], "System.Windows.Documents", "Glyphs"); types[KnownTypes.GradientBrush] = InitType(assemblies[3], "System.Windows.Media", "GradientBrush"); types[KnownTypes.GradientStop] = InitType(assemblies[3], "System.Windows.Media", "GradientStop"); types[KnownTypes.GradientStopCollection] = InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection"); types[KnownTypes.Grid] = InitType(assemblies[4], "System.Windows.Controls", "Grid"); types[KnownTypes.GridLength] = InitType(assemblies[4], "System.Windows", "GridLength"); types[KnownTypes.GridLengthConverter] = InitType(assemblies[4], "System.Windows", "GridLengthConverter"); types[KnownTypes.GridSplitter] = InitType(assemblies[4], "System.Windows.Controls", "GridSplitter"); types[KnownTypes.GridView] = InitType(assemblies[4], "System.Windows.Controls", "GridView"); types[KnownTypes.GridViewColumn] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumn"); types[KnownTypes.GridViewColumnHeader] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnHeader"); types[KnownTypes.GridViewHeaderRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewHeaderRowPresenter"); types[KnownTypes.GridViewRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewRowPresenter"); types[KnownTypes.GridViewRowPresenterBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "GridViewRowPresenterBase"); types[KnownTypes.GroupBox] = InitType(assemblies[4], "System.Windows.Controls", "GroupBox"); types[KnownTypes.GroupItem] = InitType(assemblies[4], "System.Windows.Controls", "GroupItem"); types[KnownTypes.Guid] = InitType(assemblies[0], "System", "Guid"); types[KnownTypes.GuidConverter] = InitType(assemblies[1], "System.ComponentModel", "GuidConverter"); types[KnownTypes.GuidelineSet] = InitType(assemblies[3], "System.Windows.Media", "GuidelineSet"); types[KnownTypes.HeaderedContentControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedContentControl"); types[KnownTypes.HeaderedItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedItemsControl"); types[KnownTypes.HierarchicalDataTemplate] = InitType(assemblies[4], "System.Windows", "HierarchicalDataTemplate"); types[KnownTypes.HostVisual] = InitType(assemblies[3], "System.Windows.Media", "HostVisual"); types[KnownTypes.Hyperlink] = InitType(assemblies[4], "System.Windows.Documents", "Hyperlink"); types[KnownTypes.IAddChild] = InitType(assemblies[3], "System.Windows.Markup", "IAddChild"); types[KnownTypes.IAddChildInternal] = InitType(assemblies[3], "System.Windows.Markup", "IAddChildInternal"); types[KnownTypes.ICommand] = InitType(assemblies[3], "System.Windows.Input", "ICommand"); types[KnownTypes.IComponentConnector] = InitType(assemblies[2], "System.Windows.Markup", "IComponentConnector"); types[KnownTypes.INameScope] = InitType(assemblies[2], "System.Windows.Markup", "INameScope"); types[KnownTypes.IStyleConnector] = InitType(assemblies[4], "System.Windows.Markup", "IStyleConnector"); types[KnownTypes.IconBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "IconBitmapDecoder"); types[KnownTypes.Image] = InitType(assemblies[4], "System.Windows.Controls", "Image"); types[KnownTypes.ImageBrush] = InitType(assemblies[3], "System.Windows.Media", "ImageBrush"); types[KnownTypes.ImageDrawing] = InitType(assemblies[3], "System.Windows.Media", "ImageDrawing"); types[KnownTypes.ImageMetadata] = InitType(assemblies[3], "System.Windows.Media", "ImageMetadata"); types[KnownTypes.ImageSource] = InitType(assemblies[3], "System.Windows.Media", "ImageSource"); types[KnownTypes.ImageSourceConverter] = InitType(assemblies[3], "System.Windows.Media", "ImageSourceConverter"); types[KnownTypes.InPlaceBitmapMetadataWriter] = InitType(assemblies[3], "System.Windows.Media.Imaging", "InPlaceBitmapMetadataWriter"); types[KnownTypes.InkCanvas] = InitType(assemblies[4], "System.Windows.Controls", "InkCanvas"); types[KnownTypes.InkPresenter] = InitType(assemblies[4], "System.Windows.Controls", "InkPresenter"); types[KnownTypes.Inline] = InitType(assemblies[4], "System.Windows.Documents", "Inline"); types[KnownTypes.InlineCollection] = InitType(assemblies[4], "System.Windows.Documents", "InlineCollection"); types[KnownTypes.InlineUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "InlineUIContainer"); types[KnownTypes.InputBinding] = InitType(assemblies[3], "System.Windows.Input", "InputBinding"); types[KnownTypes.InputDevice] = InitType(assemblies[3], "System.Windows.Input", "InputDevice"); types[KnownTypes.InputLanguageManager] = InitType(assemblies[3], "System.Windows.Input", "InputLanguageManager"); types[KnownTypes.InputManager] = InitType(assemblies[3], "System.Windows.Input", "InputManager"); types[KnownTypes.InputMethod] = InitType(assemblies[3], "System.Windows.Input", "InputMethod"); types[KnownTypes.InputScope] = InitType(assemblies[3], "System.Windows.Input", "InputScope"); types[KnownTypes.InputScopeConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeConverter"); types[KnownTypes.InputScopeName] = InitType(assemblies[3], "System.Windows.Input", "InputScopeName"); types[KnownTypes.InputScopeNameConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeNameConverter"); types[KnownTypes.Int16] = InitType(assemblies[0], "System", "Int16"); types[KnownTypes.Int16Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16Animation"); types[KnownTypes.Int16AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationBase"); types[KnownTypes.Int16AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationUsingKeyFrames"); types[KnownTypes.Int16Converter] = InitType(assemblies[1], "System.ComponentModel", "Int16Converter"); types[KnownTypes.Int16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrame"); types[KnownTypes.Int16KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection"); types[KnownTypes.Int32] = InitType(assemblies[0], "System", "Int32"); types[KnownTypes.Int32Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32Animation"); types[KnownTypes.Int32AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationBase"); types[KnownTypes.Int32AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationUsingKeyFrames"); types[KnownTypes.Int32Collection] = InitType(assemblies[3], "System.Windows.Media", "Int32Collection"); types[KnownTypes.Int32CollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "Int32CollectionConverter"); types[KnownTypes.Int32Converter] = InitType(assemblies[1], "System.ComponentModel", "Int32Converter"); types[KnownTypes.Int32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrame"); types[KnownTypes.Int32KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection"); types[KnownTypes.Int32Rect] = InitType(assemblies[2], "System.Windows", "Int32Rect"); types[KnownTypes.Int32RectConverter] = InitType(assemblies[2], "System.Windows", "Int32RectConverter"); types[KnownTypes.Int64] = InitType(assemblies[0], "System", "Int64"); types[KnownTypes.Int64Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64Animation"); types[KnownTypes.Int64AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationBase"); types[KnownTypes.Int64AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationUsingKeyFrames"); types[KnownTypes.Int64Converter] = InitType(assemblies[1], "System.ComponentModel", "Int64Converter"); types[KnownTypes.Int64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrame"); types[KnownTypes.Int64KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection"); types[KnownTypes.Italic] = InitType(assemblies[4], "System.Windows.Documents", "Italic"); types[KnownTypes.ItemCollection] = InitType(assemblies[4], "System.Windows.Controls", "ItemCollection"); types[KnownTypes.ItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "ItemsControl"); types[KnownTypes.ItemsPanelTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate"); types[KnownTypes.ItemsPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPresenter"); types[KnownTypes.JournalEntry] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntry"); types[KnownTypes.JournalEntryListConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryListConverter"); types[KnownTypes.JournalEntryUnifiedViewConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryUnifiedViewConverter"); types[KnownTypes.JpegBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapDecoder"); types[KnownTypes.JpegBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapEncoder"); types[KnownTypes.KeyBinding] = InitType(assemblies[3], "System.Windows.Input", "KeyBinding"); types[KnownTypes.KeyConverter] = InitType(assemblies[2], "System.Windows.Input", "KeyConverter"); types[KnownTypes.KeyGesture] = InitType(assemblies[3], "System.Windows.Input", "KeyGesture"); types[KnownTypes.KeyGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "KeyGestureConverter"); types[KnownTypes.KeySpline] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeySpline"); types[KnownTypes.KeySplineConverter] = InitType(assemblies[3], "System.Windows", "KeySplineConverter"); types[KnownTypes.KeyTime] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeyTime"); types[KnownTypes.KeyTimeConverter] = InitType(assemblies[3], "System.Windows", "KeyTimeConverter"); types[KnownTypes.KeyboardDevice] = InitType(assemblies[3], "System.Windows.Input", "KeyboardDevice"); types[KnownTypes.Label] = InitType(assemblies[4], "System.Windows.Controls", "Label"); types[KnownTypes.LateBoundBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "LateBoundBitmapDecoder"); types[KnownTypes.LengthConverter] = InitType(assemblies[4], "System.Windows", "LengthConverter"); types[KnownTypes.Light] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Light"); types[KnownTypes.Line] = InitType(assemblies[4], "System.Windows.Shapes", "Line"); types[KnownTypes.LineBreak] = InitType(assemblies[4], "System.Windows.Documents", "LineBreak"); types[KnownTypes.LineGeometry] = InitType(assemblies[3], "System.Windows.Media", "LineGeometry"); types[KnownTypes.LineSegment] = InitType(assemblies[3], "System.Windows.Media", "LineSegment"); types[KnownTypes.LinearByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearByteKeyFrame"); types[KnownTypes.LinearColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearColorKeyFrame"); types[KnownTypes.LinearDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDecimalKeyFrame"); types[KnownTypes.LinearDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDoubleKeyFrame"); types[KnownTypes.LinearGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "LinearGradientBrush"); types[KnownTypes.LinearInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt16KeyFrame"); types[KnownTypes.LinearInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt32KeyFrame"); types[KnownTypes.LinearInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt64KeyFrame"); types[KnownTypes.LinearPoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPoint3DKeyFrame"); types[KnownTypes.LinearPointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPointKeyFrame"); types[KnownTypes.LinearQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearQuaternionKeyFrame"); types[KnownTypes.LinearRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRectKeyFrame"); types[KnownTypes.LinearRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRotation3DKeyFrame"); types[KnownTypes.LinearSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSingleKeyFrame"); types[KnownTypes.LinearSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSizeKeyFrame"); types[KnownTypes.LinearThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "LinearThicknessKeyFrame"); types[KnownTypes.LinearVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVector3DKeyFrame"); types[KnownTypes.LinearVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVectorKeyFrame"); types[KnownTypes.List] = InitType(assemblies[4], "System.Windows.Documents", "List"); types[KnownTypes.ListBox] = InitType(assemblies[4], "System.Windows.Controls", "ListBox"); types[KnownTypes.ListBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ListBoxItem"); types[KnownTypes.ListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "ListCollectionView"); types[KnownTypes.ListItem] = InitType(assemblies[4], "System.Windows.Documents", "ListItem"); types[KnownTypes.ListView] = InitType(assemblies[4], "System.Windows.Controls", "ListView"); types[KnownTypes.ListViewItem] = InitType(assemblies[4], "System.Windows.Controls", "ListViewItem"); types[KnownTypes.Localization] = InitType(assemblies[4], "System.Windows", "Localization"); types[KnownTypes.LostFocusEventManager] = InitType(assemblies[4], "System.Windows", "LostFocusEventManager"); types[KnownTypes.MarkupExtension] = InitType(assemblies[2], "System.Windows.Markup", "MarkupExtension"); types[KnownTypes.Material] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Material"); types[KnownTypes.MaterialCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection"); types[KnownTypes.MaterialGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialGroup"); types[KnownTypes.Matrix] = InitType(assemblies[2], "System.Windows.Media", "Matrix"); types[KnownTypes.Matrix3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3D"); types[KnownTypes.Matrix3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3DConverter"); types[KnownTypes.MatrixAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationBase"); types[KnownTypes.MatrixAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingKeyFrames"); types[KnownTypes.MatrixAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingPath"); types[KnownTypes.MatrixCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixCamera"); types[KnownTypes.MatrixConverter] = InitType(assemblies[2], "System.Windows.Media", "MatrixConverter"); types[KnownTypes.MatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrame"); types[KnownTypes.MatrixKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection"); types[KnownTypes.MatrixTransform] = InitType(assemblies[3], "System.Windows.Media", "MatrixTransform"); types[KnownTypes.MatrixTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixTransform3D"); types[KnownTypes.MediaClock] = InitType(assemblies[3], "System.Windows.Media", "MediaClock"); types[KnownTypes.MediaElement] = InitType(assemblies[4], "System.Windows.Controls", "MediaElement"); types[KnownTypes.MediaPlayer] = InitType(assemblies[3], "System.Windows.Media", "MediaPlayer"); types[KnownTypes.MediaTimeline] = InitType(assemblies[3], "System.Windows.Media", "MediaTimeline"); types[KnownTypes.Menu] = InitType(assemblies[4], "System.Windows.Controls", "Menu"); types[KnownTypes.MenuBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "MenuBase"); types[KnownTypes.MenuItem] = InitType(assemblies[4], "System.Windows.Controls", "MenuItem"); types[KnownTypes.MenuScrollingVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "MenuScrollingVisibilityConverter"); types[KnownTypes.MeshGeometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MeshGeometry3D"); types[KnownTypes.Model3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3D"); types[KnownTypes.Model3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection"); types[KnownTypes.Model3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DGroup"); types[KnownTypes.ModelVisual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ModelVisual3D"); types[KnownTypes.ModifierKeysConverter] = InitType(assemblies[2], "System.Windows.Input", "ModifierKeysConverter"); types[KnownTypes.MouseActionConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseActionConverter"); types[KnownTypes.MouseBinding] = InitType(assemblies[3], "System.Windows.Input", "MouseBinding"); types[KnownTypes.MouseDevice] = InitType(assemblies[3], "System.Windows.Input", "MouseDevice"); types[KnownTypes.MouseGesture] = InitType(assemblies[3], "System.Windows.Input", "MouseGesture"); types[KnownTypes.MouseGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseGestureConverter"); types[KnownTypes.MultiBinding] = InitType(assemblies[4], "System.Windows.Data", "MultiBinding"); types[KnownTypes.MultiBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "MultiBindingExpression"); types[KnownTypes.MultiDataTrigger] = InitType(assemblies[4], "System.Windows", "MultiDataTrigger"); types[KnownTypes.MultiTrigger] = InitType(assemblies[4], "System.Windows", "MultiTrigger"); types[KnownTypes.NameScope] = InitType(assemblies[4], "System.Windows", "NameScope"); types[KnownTypes.NavigationWindow] = InitType(assemblies[4], "System.Windows.Navigation", "NavigationWindow"); types[KnownTypes.NullExtension] = InitType(assemblies[4], "System.Windows.Markup", "NullExtension"); types[KnownTypes.NullableBoolConverter] = InitType(assemblies[4], "System.Windows", "NullableBoolConverter"); types[KnownTypes.NullableConverter] = InitType(assemblies[1], "System.ComponentModel", "NullableConverter"); types[KnownTypes.NumberSubstitution] = InitType(assemblies[3], "System.Windows.Media", "NumberSubstitution"); types[KnownTypes.Object] = InitType(assemblies[0], "System", "Object"); types[KnownTypes.ObjectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationBase"); types[KnownTypes.ObjectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationUsingKeyFrames"); types[KnownTypes.ObjectDataProvider] = InitType(assemblies[4], "System.Windows.Data", "ObjectDataProvider"); types[KnownTypes.ObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrame"); types[KnownTypes.ObjectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection"); types[KnownTypes.OrthographicCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "OrthographicCamera"); types[KnownTypes.OuterGlowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "OuterGlowBitmapEffect"); types[KnownTypes.Page] = InitType(assemblies[4], "System.Windows.Controls", "Page"); types[KnownTypes.PageContent] = InitType(assemblies[4], "System.Windows.Documents", "PageContent"); types[KnownTypes.PageFunctionBase] = InitType(assemblies[4], "System.Windows.Navigation", "PageFunctionBase"); types[KnownTypes.Panel] = InitType(assemblies[4], "System.Windows.Controls", "Panel"); types[KnownTypes.Paragraph] = InitType(assemblies[4], "System.Windows.Documents", "Paragraph"); types[KnownTypes.ParallelTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "ParallelTimeline"); types[KnownTypes.ParserContext] = InitType(assemblies[4], "System.Windows.Markup", "ParserContext"); types[KnownTypes.PasswordBox] = InitType(assemblies[4], "System.Windows.Controls", "PasswordBox"); types[KnownTypes.Path] = InitType(assemblies[4], "System.Windows.Shapes", "Path"); types[KnownTypes.PathFigure] = InitType(assemblies[3], "System.Windows.Media", "PathFigure"); types[KnownTypes.PathFigureCollection] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection"); types[KnownTypes.PathFigureCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollectionConverter"); types[KnownTypes.PathGeometry] = InitType(assemblies[3], "System.Windows.Media", "PathGeometry"); types[KnownTypes.PathSegment] = InitType(assemblies[3], "System.Windows.Media", "PathSegment"); types[KnownTypes.PathSegmentCollection] = InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection"); types[KnownTypes.PauseStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "PauseStoryboard"); types[KnownTypes.Pen] = InitType(assemblies[3], "System.Windows.Media", "Pen"); types[KnownTypes.PerspectiveCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PerspectiveCamera"); types[KnownTypes.PixelFormat] = InitType(assemblies[3], "System.Windows.Media", "PixelFormat"); types[KnownTypes.PixelFormatConverter] = InitType(assemblies[3], "System.Windows.Media", "PixelFormatConverter"); types[KnownTypes.PngBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapDecoder"); types[KnownTypes.PngBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapEncoder"); types[KnownTypes.Point] = InitType(assemblies[2], "System.Windows", "Point"); types[KnownTypes.Point3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3D"); types[KnownTypes.Point3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimation"); types[KnownTypes.Point3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationBase"); types[KnownTypes.Point3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationUsingKeyFrames"); types[KnownTypes.Point3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollection"); types[KnownTypes.Point3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollectionConverter"); types[KnownTypes.Point3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DConverter"); types[KnownTypes.Point3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrame"); types[KnownTypes.Point3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection"); types[KnownTypes.Point4D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4D"); types[KnownTypes.Point4DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4DConverter"); types[KnownTypes.PointAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimation"); types[KnownTypes.PointAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationBase"); types[KnownTypes.PointAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingKeyFrames"); types[KnownTypes.PointAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingPath"); types[KnownTypes.PointCollection] = InitType(assemblies[3], "System.Windows.Media", "PointCollection"); types[KnownTypes.PointCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PointCollectionConverter"); types[KnownTypes.PointConverter] = InitType(assemblies[2], "System.Windows", "PointConverter"); types[KnownTypes.PointIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "PointIListConverter"); types[KnownTypes.PointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrame"); types[KnownTypes.PointKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection"); types[KnownTypes.PointLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLight"); types[KnownTypes.PointLightBase] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLightBase"); types[KnownTypes.PolyBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyBezierSegment"); types[KnownTypes.PolyLineSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyLineSegment"); types[KnownTypes.PolyQuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyQuadraticBezierSegment"); types[KnownTypes.Polygon] = InitType(assemblies[4], "System.Windows.Shapes", "Polygon"); types[KnownTypes.Polyline] = InitType(assemblies[4], "System.Windows.Shapes", "Polyline"); types[KnownTypes.Popup] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Popup"); types[KnownTypes.PresentationSource] = InitType(assemblies[3], "System.Windows", "PresentationSource"); types[KnownTypes.PriorityBinding] = InitType(assemblies[4], "System.Windows.Data", "PriorityBinding"); types[KnownTypes.PriorityBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "PriorityBindingExpression"); types[KnownTypes.ProgressBar] = InitType(assemblies[4], "System.Windows.Controls", "ProgressBar"); types[KnownTypes.ProjectionCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ProjectionCamera"); types[KnownTypes.PropertyPath] = InitType(assemblies[4], "System.Windows", "PropertyPath"); types[KnownTypes.PropertyPathConverter] = InitType(assemblies[4], "System.Windows", "PropertyPathConverter"); types[KnownTypes.QuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "QuadraticBezierSegment"); types[KnownTypes.Quaternion] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Quaternion"); types[KnownTypes.QuaternionAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimation"); types[KnownTypes.QuaternionAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationBase"); types[KnownTypes.QuaternionAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationUsingKeyFrames"); types[KnownTypes.QuaternionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionConverter"); types[KnownTypes.QuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrame"); types[KnownTypes.QuaternionKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection"); types[KnownTypes.QuaternionRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionRotation3D"); types[KnownTypes.RadialGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "RadialGradientBrush"); types[KnownTypes.RadioButton] = InitType(assemblies[4], "System.Windows.Controls", "RadioButton"); types[KnownTypes.RangeBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RangeBase"); types[KnownTypes.Rect] = InitType(assemblies[2], "System.Windows", "Rect"); types[KnownTypes.Rect3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3D"); types[KnownTypes.Rect3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3DConverter"); types[KnownTypes.RectAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimation"); types[KnownTypes.RectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationBase"); types[KnownTypes.RectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationUsingKeyFrames"); types[KnownTypes.RectConverter] = InitType(assemblies[2], "System.Windows", "RectConverter"); types[KnownTypes.RectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrame"); types[KnownTypes.RectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection"); types[KnownTypes.Rectangle] = InitType(assemblies[4], "System.Windows.Shapes", "Rectangle"); types[KnownTypes.RectangleGeometry] = InitType(assemblies[3], "System.Windows.Media", "RectangleGeometry"); types[KnownTypes.RelativeSource] = InitType(assemblies[4], "System.Windows.Data", "RelativeSource"); types[KnownTypes.RemoveStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "RemoveStoryboard"); types[KnownTypes.RenderOptions] = InitType(assemblies[3], "System.Windows.Media", "RenderOptions"); types[KnownTypes.RenderTargetBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "RenderTargetBitmap"); types[KnownTypes.RepeatBehavior] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehavior"); types[KnownTypes.RepeatBehaviorConverter] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehaviorConverter"); types[KnownTypes.RepeatButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RepeatButton"); types[KnownTypes.ResizeGrip] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ResizeGrip"); types[KnownTypes.ResourceDictionary] = InitType(assemblies[4], "System.Windows", "ResourceDictionary"); types[KnownTypes.ResourceKey] = InitType(assemblies[4], "System.Windows", "ResourceKey"); types[KnownTypes.ResumeStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "ResumeStoryboard"); types[KnownTypes.RichTextBox] = InitType(assemblies[4], "System.Windows.Controls", "RichTextBox"); types[KnownTypes.RotateTransform] = InitType(assemblies[3], "System.Windows.Media", "RotateTransform"); types[KnownTypes.RotateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "RotateTransform3D"); types[KnownTypes.Rotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rotation3D"); types[KnownTypes.Rotation3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimation"); types[KnownTypes.Rotation3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationBase"); types[KnownTypes.Rotation3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationUsingKeyFrames"); types[KnownTypes.Rotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrame"); types[KnownTypes.Rotation3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection"); types[KnownTypes.RoutedCommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedCommand"); types[KnownTypes.RoutedEvent] = InitType(assemblies[3], "System.Windows", "RoutedEvent"); types[KnownTypes.RoutedEventConverter] = InitType(assemblies[4], "System.Windows.Markup", "RoutedEventConverter"); types[KnownTypes.RoutedUICommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedUICommand"); types[KnownTypes.RoutingStrategy] = InitType(assemblies[3], "System.Windows", "RoutingStrategy"); types[KnownTypes.RowDefinition] = InitType(assemblies[4], "System.Windows.Controls", "RowDefinition"); types[KnownTypes.Run] = InitType(assemblies[4], "System.Windows.Documents", "Run"); types[KnownTypes.RuntimeNamePropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "RuntimeNamePropertyAttribute"); types[KnownTypes.SByte] = InitType(assemblies[0], "System", "SByte"); types[KnownTypes.SByteConverter] = InitType(assemblies[1], "System.ComponentModel", "SByteConverter"); types[KnownTypes.ScaleTransform] = InitType(assemblies[3], "System.Windows.Media", "ScaleTransform"); types[KnownTypes.ScaleTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ScaleTransform3D"); types[KnownTypes.ScrollBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ScrollBar"); types[KnownTypes.ScrollContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ScrollContentPresenter"); types[KnownTypes.ScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "ScrollViewer"); types[KnownTypes.Section] = InitType(assemblies[4], "System.Windows.Documents", "Section"); types[KnownTypes.SeekStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "SeekStoryboard"); types[KnownTypes.Selector] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Selector"); types[KnownTypes.Separator] = InitType(assemblies[4], "System.Windows.Controls", "Separator"); types[KnownTypes.SetStoryboardSpeedRatio] = InitType(assemblies[4], "System.Windows.Media.Animation", "SetStoryboardSpeedRatio"); types[KnownTypes.Setter] = InitType(assemblies[4], "System.Windows", "Setter"); types[KnownTypes.SetterBase] = InitType(assemblies[4], "System.Windows", "SetterBase"); types[KnownTypes.Shape] = InitType(assemblies[4], "System.Windows.Shapes", "Shape"); types[KnownTypes.Single] = InitType(assemblies[0], "System", "Single"); types[KnownTypes.SingleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimation"); types[KnownTypes.SingleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationBase"); types[KnownTypes.SingleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationUsingKeyFrames"); types[KnownTypes.SingleConverter] = InitType(assemblies[1], "System.ComponentModel", "SingleConverter"); types[KnownTypes.SingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrame"); types[KnownTypes.SingleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection"); types[KnownTypes.Size] = InitType(assemblies[2], "System.Windows", "Size"); types[KnownTypes.Size3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3D"); types[KnownTypes.Size3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3DConverter"); types[KnownTypes.SizeAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimation"); types[KnownTypes.SizeAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationBase"); types[KnownTypes.SizeAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationUsingKeyFrames"); types[KnownTypes.SizeConverter] = InitType(assemblies[2], "System.Windows", "SizeConverter"); types[KnownTypes.SizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrame"); types[KnownTypes.SizeKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection"); types[KnownTypes.SkewTransform] = InitType(assemblies[3], "System.Windows.Media", "SkewTransform"); types[KnownTypes.SkipStoryboardToFill] = InitType(assemblies[4], "System.Windows.Media.Animation", "SkipStoryboardToFill"); types[KnownTypes.Slider] = InitType(assemblies[4], "System.Windows.Controls", "Slider"); types[KnownTypes.SolidColorBrush] = InitType(assemblies[3], "System.Windows.Media", "SolidColorBrush"); types[KnownTypes.SoundPlayerAction] = InitType(assemblies[4], "System.Windows.Controls", "SoundPlayerAction"); types[KnownTypes.Span] = InitType(assemblies[4], "System.Windows.Documents", "Span"); types[KnownTypes.SpecularMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpecularMaterial"); types[KnownTypes.SpellCheck] = InitType(assemblies[4], "System.Windows.Controls", "SpellCheck"); types[KnownTypes.SplineByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineByteKeyFrame"); types[KnownTypes.SplineColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineColorKeyFrame"); types[KnownTypes.SplineDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDecimalKeyFrame"); types[KnownTypes.SplineDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDoubleKeyFrame"); types[KnownTypes.SplineInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt16KeyFrame"); types[KnownTypes.SplineInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt32KeyFrame"); types[KnownTypes.SplineInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt64KeyFrame"); types[KnownTypes.SplinePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePoint3DKeyFrame"); types[KnownTypes.SplinePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePointKeyFrame"); types[KnownTypes.SplineQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineQuaternionKeyFrame"); types[KnownTypes.SplineRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRectKeyFrame"); types[KnownTypes.SplineRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRotation3DKeyFrame"); types[KnownTypes.SplineSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSingleKeyFrame"); types[KnownTypes.SplineSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSizeKeyFrame"); types[KnownTypes.SplineThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "SplineThicknessKeyFrame"); types[KnownTypes.SplineVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVector3DKeyFrame"); types[KnownTypes.SplineVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVectorKeyFrame"); types[KnownTypes.SpotLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpotLight"); types[KnownTypes.StackPanel] = InitType(assemblies[4], "System.Windows.Controls", "StackPanel"); types[KnownTypes.StaticExtension] = InitType(assemblies[4], "System.Windows.Markup", "StaticExtension"); types[KnownTypes.StaticResourceExtension] = InitType(assemblies[4], "System.Windows", "StaticResourceExtension"); types[KnownTypes.StatusBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBar"); types[KnownTypes.StatusBarItem] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBarItem"); types[KnownTypes.StickyNoteControl] = InitType(assemblies[4], "System.Windows.Controls", "StickyNoteControl"); types[KnownTypes.StopStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "StopStoryboard"); types[KnownTypes.Storyboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard"); types[KnownTypes.StreamGeometry] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometry"); types[KnownTypes.StreamGeometryContext] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometryContext"); types[KnownTypes.StreamResourceInfo] = InitType(assemblies[4], "System.Windows.Resources", "StreamResourceInfo"); types[KnownTypes.String] = InitType(assemblies[0], "System", "String"); types[KnownTypes.StringAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationBase"); types[KnownTypes.StringAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationUsingKeyFrames"); types[KnownTypes.StringConverter] = InitType(assemblies[1], "System.ComponentModel", "StringConverter"); types[KnownTypes.StringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrame"); types[KnownTypes.StringKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection"); types[KnownTypes.StrokeCollection] = InitType(assemblies[3], "System.Windows.Ink", "StrokeCollection"); types[KnownTypes.StrokeCollectionConverter] = InitType(assemblies[3], "System.Windows", "StrokeCollectionConverter"); types[KnownTypes.Style] = InitType(assemblies[4], "System.Windows", "Style"); types[KnownTypes.Stylus] = InitType(assemblies[3], "System.Windows.Input", "Stylus"); types[KnownTypes.StylusDevice] = InitType(assemblies[3], "System.Windows.Input", "StylusDevice"); types[KnownTypes.TabControl] = InitType(assemblies[4], "System.Windows.Controls", "TabControl"); types[KnownTypes.TabItem] = InitType(assemblies[4], "System.Windows.Controls", "TabItem"); types[KnownTypes.TabPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TabPanel"); types[KnownTypes.Table] = InitType(assemblies[4], "System.Windows.Documents", "Table"); types[KnownTypes.TableCell] = InitType(assemblies[4], "System.Windows.Documents", "TableCell"); types[KnownTypes.TableColumn] = InitType(assemblies[4], "System.Windows.Documents", "TableColumn"); types[KnownTypes.TableRow] = InitType(assemblies[4], "System.Windows.Documents", "TableRow"); types[KnownTypes.TableRowGroup] = InitType(assemblies[4], "System.Windows.Documents", "TableRowGroup"); types[KnownTypes.TabletDevice] = InitType(assemblies[3], "System.Windows.Input", "TabletDevice"); types[KnownTypes.TemplateBindingExpression] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpression"); types[KnownTypes.TemplateBindingExpressionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpressionConverter"); types[KnownTypes.TemplateBindingExtension] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtension"); types[KnownTypes.TemplateBindingExtensionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtensionConverter"); types[KnownTypes.TemplateKey] = InitType(assemblies[4], "System.Windows", "TemplateKey"); types[KnownTypes.TemplateKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "TemplateKeyConverter"); types[KnownTypes.TextBlock] = InitType(assemblies[4], "System.Windows.Controls", "TextBlock"); types[KnownTypes.TextBox] = InitType(assemblies[4], "System.Windows.Controls", "TextBox"); types[KnownTypes.TextBoxBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TextBoxBase"); types[KnownTypes.TextComposition] = InitType(assemblies[3], "System.Windows.Input", "TextComposition"); types[KnownTypes.TextCompositionManager] = InitType(assemblies[3], "System.Windows.Input", "TextCompositionManager"); types[KnownTypes.TextDecoration] = InitType(assemblies[3], "System.Windows", "TextDecoration"); types[KnownTypes.TextDecorationCollection] = InitType(assemblies[3], "System.Windows", "TextDecorationCollection"); types[KnownTypes.TextDecorationCollectionConverter] = InitType(assemblies[3], "System.Windows", "TextDecorationCollectionConverter"); types[KnownTypes.TextEffect] = InitType(assemblies[3], "System.Windows.Media", "TextEffect"); types[KnownTypes.TextEffectCollection] = InitType(assemblies[3], "System.Windows.Media", "TextEffectCollection"); types[KnownTypes.TextElement] = InitType(assemblies[4], "System.Windows.Documents", "TextElement"); types[KnownTypes.TextSearch] = InitType(assemblies[4], "System.Windows.Controls", "TextSearch"); types[KnownTypes.ThemeDictionaryExtension] = InitType(assemblies[4], "System.Windows", "ThemeDictionaryExtension"); types[KnownTypes.Thickness] = InitType(assemblies[4], "System.Windows", "Thickness"); types[KnownTypes.ThicknessAnimation] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimation"); types[KnownTypes.ThicknessAnimationBase] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationBase"); types[KnownTypes.ThicknessAnimationUsingKeyFrames] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationUsingKeyFrames"); types[KnownTypes.ThicknessConverter] = InitType(assemblies[4], "System.Windows", "ThicknessConverter"); types[KnownTypes.ThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrame"); types[KnownTypes.ThicknessKeyFrameCollection] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection"); types[KnownTypes.Thumb] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Thumb"); types[KnownTypes.TickBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TickBar"); types[KnownTypes.TiffBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapDecoder"); types[KnownTypes.TiffBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapEncoder"); types[KnownTypes.TileBrush] = InitType(assemblies[3], "System.Windows.Media", "TileBrush"); types[KnownTypes.TimeSpan] = InitType(assemblies[0], "System", "TimeSpan"); types[KnownTypes.TimeSpanConverter] = InitType(assemblies[1], "System.ComponentModel", "TimeSpanConverter"); types[KnownTypes.Timeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "Timeline"); types[KnownTypes.TimelineCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection"); types[KnownTypes.TimelineGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineGroup"); types[KnownTypes.ToggleButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToggleButton"); types[KnownTypes.ToolBar] = InitType(assemblies[4], "System.Windows.Controls", "ToolBar"); types[KnownTypes.ToolBarOverflowPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarOverflowPanel"); types[KnownTypes.ToolBarPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarPanel"); types[KnownTypes.ToolBarTray] = InitType(assemblies[4], "System.Windows.Controls", "ToolBarTray"); types[KnownTypes.ToolTip] = InitType(assemblies[4], "System.Windows.Controls", "ToolTip"); types[KnownTypes.ToolTipService] = InitType(assemblies[4], "System.Windows.Controls", "ToolTipService"); types[KnownTypes.Track] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Track"); types[KnownTypes.Transform] = InitType(assemblies[3], "System.Windows.Media", "Transform"); types[KnownTypes.Transform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3D"); types[KnownTypes.Transform3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection"); types[KnownTypes.Transform3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DGroup"); types[KnownTypes.TransformCollection] = InitType(assemblies[3], "System.Windows.Media", "TransformCollection"); types[KnownTypes.TransformConverter] = InitType(assemblies[3], "System.Windows.Media", "TransformConverter"); types[KnownTypes.TransformGroup] = InitType(assemblies[3], "System.Windows.Media", "TransformGroup"); types[KnownTypes.TransformedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TransformedBitmap"); types[KnownTypes.TranslateTransform] = InitType(assemblies[3], "System.Windows.Media", "TranslateTransform"); types[KnownTypes.TranslateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "TranslateTransform3D"); types[KnownTypes.TreeView] = InitType(assemblies[4], "System.Windows.Controls", "TreeView"); types[KnownTypes.TreeViewItem] = InitType(assemblies[4], "System.Windows.Controls", "TreeViewItem"); types[KnownTypes.Trigger] = InitType(assemblies[4], "System.Windows", "Trigger"); types[KnownTypes.TriggerAction] = InitType(assemblies[4], "System.Windows", "TriggerAction"); types[KnownTypes.TriggerBase] = InitType(assemblies[4], "System.Windows", "TriggerBase"); types[KnownTypes.TypeExtension] = InitType(assemblies[4], "System.Windows.Markup", "TypeExtension"); types[KnownTypes.TypeTypeConverter] = InitType(assemblies[2], "System.Windows.Markup", "TypeTypeConverter"); types[KnownTypes.Typography] = InitType(assemblies[4], "System.Windows.Documents", "Typography"); types[KnownTypes.UIElement] = InitType(assemblies[3], "System.Windows", "UIElement"); types[KnownTypes.UInt16] = InitType(assemblies[0], "System", "UInt16"); types[KnownTypes.UInt16Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt16Converter"); types[KnownTypes.UInt32] = InitType(assemblies[0], "System", "UInt32"); types[KnownTypes.UInt32Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt32Converter"); types[KnownTypes.UInt64] = InitType(assemblies[0], "System", "UInt64"); types[KnownTypes.UInt64Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt64Converter"); types[KnownTypes.UShortIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "UShortIListConverter"); types[KnownTypes.Underline] = InitType(assemblies[4], "System.Windows.Documents", "Underline"); types[KnownTypes.UniformGrid] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "UniformGrid"); types[KnownTypes.Uri] = InitType(assemblies[1], "System", "Uri"); types[KnownTypes.UriTypeConverter] = InitType(assemblies[1], "System", "UriTypeConverter"); types[KnownTypes.UserControl] = InitType(assemblies[4], "System.Windows.Controls", "UserControl"); types[KnownTypes.Validation] = InitType(assemblies[4], "System.Windows.Controls", "Validation"); types[KnownTypes.Vector] = InitType(assemblies[2], "System.Windows", "Vector"); types[KnownTypes.Vector3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3D"); types[KnownTypes.Vector3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimation"); types[KnownTypes.Vector3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationBase"); types[KnownTypes.Vector3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationUsingKeyFrames"); types[KnownTypes.Vector3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollection"); types[KnownTypes.Vector3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollectionConverter"); types[KnownTypes.Vector3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DConverter"); types[KnownTypes.Vector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrame"); types[KnownTypes.Vector3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection"); types[KnownTypes.VectorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimation"); types[KnownTypes.VectorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationBase"); types[KnownTypes.VectorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationUsingKeyFrames"); types[KnownTypes.VectorCollection] = InitType(assemblies[3], "System.Windows.Media", "VectorCollection"); types[KnownTypes.VectorCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "VectorCollectionConverter"); types[KnownTypes.VectorConverter] = InitType(assemblies[2], "System.Windows", "VectorConverter"); types[KnownTypes.VectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrame"); types[KnownTypes.VectorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection"); types[KnownTypes.VideoDrawing] = InitType(assemblies[3], "System.Windows.Media", "VideoDrawing"); types[KnownTypes.ViewBase] = InitType(assemblies[4], "System.Windows.Controls", "ViewBase"); types[KnownTypes.Viewbox] = InitType(assemblies[4], "System.Windows.Controls", "Viewbox"); types[KnownTypes.Viewport3D] = InitType(assemblies[4], "System.Windows.Controls", "Viewport3D"); types[KnownTypes.Viewport3DVisual] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Viewport3DVisual"); types[KnownTypes.VirtualizingPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingPanel"); types[KnownTypes.VirtualizingStackPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingStackPanel"); types[KnownTypes.Visual] = InitType(assemblies[3], "System.Windows.Media", "Visual"); types[KnownTypes.Visual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3D"); types[KnownTypes.VisualBrush] = InitType(assemblies[3], "System.Windows.Media", "VisualBrush"); types[KnownTypes.VisualTarget] = InitType(assemblies[3], "System.Windows.Media", "VisualTarget"); types[KnownTypes.WeakEventManager] = InitType(assemblies[2], "System.Windows", "WeakEventManager"); types[KnownTypes.WhitespaceSignificantCollectionAttribute] = InitType(assemblies[2], "System.Windows.Markup", "WhitespaceSignificantCollectionAttribute"); types[KnownTypes.Window] = InitType(assemblies[4], "System.Windows", "Window"); types[KnownTypes.WmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapDecoder"); types[KnownTypes.WmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapEncoder"); types[KnownTypes.WrapPanel] = InitType(assemblies[4], "System.Windows.Controls", "WrapPanel"); types[KnownTypes.WriteableBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WriteableBitmap"); types[KnownTypes.XamlBrushSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlBrushSerializer"); types[KnownTypes.XamlInt32CollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlInt32CollectionSerializer"); types[KnownTypes.XamlPathDataSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPathDataSerializer"); types[KnownTypes.XamlPoint3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPoint3DCollectionSerializer"); types[KnownTypes.XamlPointCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPointCollectionSerializer"); types[KnownTypes.XamlReader] = InitType(assemblies[4], "System.Windows.Markup", "XamlReader"); types[KnownTypes.XamlStyleSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlStyleSerializer"); types[KnownTypes.XamlTemplateSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlTemplateSerializer"); types[KnownTypes.XamlVector3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlVector3DCollectionSerializer"); types[KnownTypes.XamlWriter] = InitType(assemblies[4], "System.Windows.Markup", "XamlWriter"); types[KnownTypes.XmlDataProvider] = InitType(assemblies[4], "System.Windows.Data", "XmlDataProvider"); types[KnownTypes.XmlLangPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "XmlLangPropertyAttribute"); types[KnownTypes.XmlLanguage] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguage"); types[KnownTypes.XmlLanguageConverter] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguageConverter"); types[KnownTypes.XmlNamespaceMapping] = InitType(assemblies[4], "System.Windows.Data", "XmlNamespaceMapping"); types[KnownTypes.ZoomPercentageConverter] = InitType(assemblies[4], "System.Windows.Documents", "ZoomPercentageConverter"); } void InitMembers() { members[KnownMembers.AccessText_Text] = InitMember(KnownTypes.AccessText, "Text", InitType(assemblies[0], "System", "String")); members[KnownMembers.BeginStoryboard_Storyboard] = InitMember(KnownTypes.BeginStoryboard, "Storyboard", InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard")); members[KnownMembers.BitmapEffectGroup_Children] = InitMember(KnownTypes.BitmapEffectGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection")); members[KnownMembers.Border_Background] = InitMember(KnownTypes.Border, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Border_BorderBrush] = InitMember(KnownTypes.Border, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Border_BorderThickness] = InitMember(KnownTypes.Border, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); members[KnownMembers.ButtonBase_Command] = InitMember(KnownTypes.ButtonBase, "Command", InitType(assemblies[3], "System.Windows.Input", "ICommand")); members[KnownMembers.ButtonBase_CommandParameter] = InitMember(KnownTypes.ButtonBase, "CommandParameter", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ButtonBase_CommandTarget] = InitMember(KnownTypes.ButtonBase, "CommandTarget", InitType(assemblies[3], "System.Windows", "IInputElement")); members[KnownMembers.ButtonBase_IsPressed] = InitMember(KnownTypes.ButtonBase, "IsPressed", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.ColumnDefinition_MaxWidth] = InitMember(KnownTypes.ColumnDefinition, "MaxWidth", InitType(assemblies[0], "System", "Double")); members[KnownMembers.ColumnDefinition_MinWidth] = InitMember(KnownTypes.ColumnDefinition, "MinWidth", InitType(assemblies[0], "System", "Double")); members[KnownMembers.ColumnDefinition_Width] = InitMember(KnownTypes.ColumnDefinition, "Width", InitType(assemblies[4], "System.Windows", "GridLength")); members[KnownMembers.ContentControl_Content] = InitMember(KnownTypes.ContentControl, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ContentControl_ContentTemplate] = InitMember(KnownTypes.ContentControl, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); members[KnownMembers.ContentControl_ContentTemplateSelector] = InitMember(KnownTypes.ContentControl, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); members[KnownMembers.ContentControl_HasContent] = InitMember(KnownTypes.ContentControl, "HasContent", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.ContentElement_Focusable] = InitMember(KnownTypes.ContentElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.ContentPresenter_Content] = InitMember(KnownTypes.ContentPresenter, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ContentPresenter_ContentSource] = InitMember(KnownTypes.ContentPresenter, "ContentSource", InitType(assemblies[0], "System", "String")); members[KnownMembers.ContentPresenter_ContentTemplate] = InitMember(KnownTypes.ContentPresenter, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); members[KnownMembers.ContentPresenter_ContentTemplateSelector] = InitMember(KnownTypes.ContentPresenter, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); members[KnownMembers.ContentPresenter_RecognizesAccessKey] = InitMember(KnownTypes.ContentPresenter, "RecognizesAccessKey", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.Control_Background] = InitMember(KnownTypes.Control, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Control_BorderBrush] = InitMember(KnownTypes.Control, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Control_BorderThickness] = InitMember(KnownTypes.Control, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); members[KnownMembers.Control_FontFamily] = InitMember(KnownTypes.Control, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); members[KnownMembers.Control_FontSize] = InitMember(KnownTypes.Control, "FontSize", InitType(assemblies[0], "System", "Double")); members[KnownMembers.Control_FontStretch] = InitMember(KnownTypes.Control, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); members[KnownMembers.Control_FontStyle] = InitMember(KnownTypes.Control, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); members[KnownMembers.Control_FontWeight] = InitMember(KnownTypes.Control, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); members[KnownMembers.Control_Foreground] = InitMember(KnownTypes.Control, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Control_HorizontalContentAlignment] = InitMember(KnownTypes.Control, "HorizontalContentAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); members[KnownMembers.Control_IsTabStop] = InitMember(KnownTypes.Control, "IsTabStop", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.Control_Padding] = InitMember(KnownTypes.Control, "Padding", InitType(assemblies[4], "System.Windows", "Thickness")); members[KnownMembers.Control_TabIndex] = InitMember(KnownTypes.Control, "TabIndex", InitType(assemblies[0], "System", "Int32")); members[KnownMembers.Control_Template] = InitMember(KnownTypes.Control, "Template", InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate")); members[KnownMembers.Control_VerticalContentAlignment] = InitMember(KnownTypes.Control, "VerticalContentAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); members[KnownMembers.DockPanel_Dock] = InitMember(KnownTypes.DockPanel, "Dock", InitType(assemblies[4], "System.Windows.Controls", "Dock")); members[KnownMembers.DockPanel_LastChildFill] = InitMember(KnownTypes.DockPanel, "LastChildFill", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.DocumentViewerBase_Document] = InitMember(KnownTypes.DocumentViewerBase, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); members[KnownMembers.DrawingGroup_Children] = InitMember(KnownTypes.DrawingGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "DrawingCollection")); members[KnownMembers.FlowDocumentReader_Document] = InitMember(KnownTypes.FlowDocumentReader, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); members[KnownMembers.FlowDocumentScrollViewer_Document] = InitMember(KnownTypes.FlowDocumentScrollViewer, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); members[KnownMembers.FrameworkContentElement_Style] = InitMember(KnownTypes.FrameworkContentElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); members[KnownMembers.FrameworkElement_FlowDirection] = InitMember(KnownTypes.FrameworkElement, "FlowDirection", InitType(assemblies[3], "System.Windows", "FlowDirection")); members[KnownMembers.FrameworkElement_Height] = InitMember(KnownTypes.FrameworkElement, "Height", InitType(assemblies[0], "System", "Double")); members[KnownMembers.FrameworkElement_HorizontalAlignment] = InitMember(KnownTypes.FrameworkElement, "HorizontalAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); members[KnownMembers.FrameworkElement_Margin] = InitMember(KnownTypes.FrameworkElement, "Margin", InitType(assemblies[4], "System.Windows", "Thickness")); members[KnownMembers.FrameworkElement_MaxHeight] = InitMember(KnownTypes.FrameworkElement, "MaxHeight", InitType(assemblies[0], "System", "Double")); members[KnownMembers.FrameworkElement_MaxWidth] = InitMember(KnownTypes.FrameworkElement, "MaxWidth", InitType(assemblies[0], "System", "Double")); members[KnownMembers.FrameworkElement_MinHeight] = InitMember(KnownTypes.FrameworkElement, "MinHeight", InitType(assemblies[0], "System", "Double")); members[KnownMembers.FrameworkElement_MinWidth] = InitMember(KnownTypes.FrameworkElement, "MinWidth", InitType(assemblies[0], "System", "Double")); members[KnownMembers.FrameworkElement_Name] = InitMember(KnownTypes.FrameworkElement, "Name", InitType(assemblies[0], "System", "String")); members[KnownMembers.FrameworkElement_Style] = InitMember(KnownTypes.FrameworkElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); members[KnownMembers.FrameworkElement_VerticalAlignment] = InitMember(KnownTypes.FrameworkElement, "VerticalAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); members[KnownMembers.FrameworkElement_Width] = InitMember(KnownTypes.FrameworkElement, "Width", InitType(assemblies[0], "System", "Double")); members[KnownMembers.GeneralTransformGroup_Children] = InitMember(KnownTypes.GeneralTransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection")); members[KnownMembers.GeometryGroup_Children] = InitMember(KnownTypes.GeometryGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeometryCollection")); members[KnownMembers.GradientBrush_GradientStops] = InitMember(KnownTypes.GradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); members[KnownMembers.Grid_Column] = InitMember(KnownTypes.Grid, "Column", InitType(assemblies[0], "System", "Int32")); members[KnownMembers.Grid_ColumnSpan] = InitMember(KnownTypes.Grid, "ColumnSpan", InitType(assemblies[0], "System", "Int32")); members[KnownMembers.Grid_Row] = InitMember(KnownTypes.Grid, "Row", InitType(assemblies[0], "System", "Int32")); members[KnownMembers.Grid_RowSpan] = InitMember(KnownTypes.Grid, "RowSpan", InitType(assemblies[0], "System", "Int32")); members[KnownMembers.GridViewColumn_Header] = InitMember(KnownTypes.GridViewColumn, "Header", InitType(assemblies[0], "System", "Object")); members[KnownMembers.HeaderedContentControl_HasHeader] = InitMember(KnownTypes.HeaderedContentControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.HeaderedContentControl_Header] = InitMember(KnownTypes.HeaderedContentControl, "Header", InitType(assemblies[0], "System", "Object")); members[KnownMembers.HeaderedContentControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); members[KnownMembers.HeaderedContentControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); members[KnownMembers.HeaderedItemsControl_HasHeader] = InitMember(KnownTypes.HeaderedItemsControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.HeaderedItemsControl_Header] = InitMember(KnownTypes.HeaderedItemsControl, "Header", InitType(assemblies[0], "System", "Object")); members[KnownMembers.HeaderedItemsControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); members[KnownMembers.HeaderedItemsControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); members[KnownMembers.Hyperlink_NavigateUri] = InitMember(KnownTypes.Hyperlink, "NavigateUri", InitType(assemblies[1], "System", "Uri")); members[KnownMembers.Image_Source] = InitMember(KnownTypes.Image, "Source", InitType(assemblies[3], "System.Windows.Media", "ImageSource")); members[KnownMembers.Image_Stretch] = InitMember(KnownTypes.Image, "Stretch", InitType(assemblies[3], "System.Windows.Media", "Stretch")); members[KnownMembers.ItemsControl_ItemContainerStyle] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyle", InitType(assemblies[4], "System.Windows", "Style")); members[KnownMembers.ItemsControl_ItemContainerStyleSelector] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyleSelector", InitType(assemblies[4], "System.Windows.Controls", "StyleSelector")); members[KnownMembers.ItemsControl_ItemTemplate] = InitMember(KnownTypes.ItemsControl, "ItemTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); members[KnownMembers.ItemsControl_ItemTemplateSelector] = InitMember(KnownTypes.ItemsControl, "ItemTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); members[KnownMembers.ItemsControl_ItemsPanel] = InitMember(KnownTypes.ItemsControl, "ItemsPanel", InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate")); members[KnownMembers.ItemsControl_ItemsSource] = InitMember(KnownTypes.ItemsControl, "ItemsSource", InitType(assemblies[0], "System.Collections", "IEnumerable")); members[KnownMembers.MaterialGroup_Children] = InitMember(KnownTypes.MaterialGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection")); members[KnownMembers.Model3DGroup_Children] = InitMember(KnownTypes.Model3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection")); members[KnownMembers.Page_Content] = InitMember(KnownTypes.Page, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Panel_Background] = InitMember(KnownTypes.Panel, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Path_Data] = InitMember(KnownTypes.Path, "Data", InitType(assemblies[3], "System.Windows.Media", "Geometry")); members[KnownMembers.PathFigure_Segments] = InitMember(KnownTypes.PathFigure, "Segments", InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection")); members[KnownMembers.PathGeometry_Figures] = InitMember(KnownTypes.PathGeometry, "Figures", InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection")); members[KnownMembers.Popup_Child] = InitMember(KnownTypes.Popup, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.Popup_IsOpen] = InitMember(KnownTypes.Popup, "IsOpen", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.Popup_Placement] = InitMember(KnownTypes.Popup, "Placement", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PlacementMode")); members[KnownMembers.Popup_PopupAnimation] = InitMember(KnownTypes.Popup, "PopupAnimation", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PopupAnimation")); members[KnownMembers.RowDefinition_Height] = InitMember(KnownTypes.RowDefinition, "Height", InitType(assemblies[4], "System.Windows", "GridLength")); members[KnownMembers.RowDefinition_MaxHeight] = InitMember(KnownTypes.RowDefinition, "MaxHeight", InitType(assemblies[0], "System", "Double")); members[KnownMembers.RowDefinition_MinHeight] = InitMember(KnownTypes.RowDefinition, "MinHeight", InitType(assemblies[0], "System", "Double")); members[KnownMembers.ScrollViewer_CanContentScroll] = InitMember(KnownTypes.ScrollViewer, "CanContentScroll", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.ScrollViewer_HorizontalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "HorizontalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); members[KnownMembers.ScrollViewer_VerticalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "VerticalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); members[KnownMembers.Shape_Fill] = InitMember(KnownTypes.Shape, "Fill", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Shape_Stroke] = InitMember(KnownTypes.Shape, "Stroke", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.Shape_StrokeThickness] = InitMember(KnownTypes.Shape, "StrokeThickness", InitType(assemblies[0], "System", "Double")); members[KnownMembers.TextBlock_Background] = InitMember(KnownTypes.TextBlock, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.TextBlock_FontFamily] = InitMember(KnownTypes.TextBlock, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); members[KnownMembers.TextBlock_FontSize] = InitMember(KnownTypes.TextBlock, "FontSize", InitType(assemblies[0], "System", "Double")); members[KnownMembers.TextBlock_FontStretch] = InitMember(KnownTypes.TextBlock, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); members[KnownMembers.TextBlock_FontStyle] = InitMember(KnownTypes.TextBlock, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); members[KnownMembers.TextBlock_FontWeight] = InitMember(KnownTypes.TextBlock, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); members[KnownMembers.TextBlock_Foreground] = InitMember(KnownTypes.TextBlock, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.TextBlock_Text] = InitMember(KnownTypes.TextBlock, "Text", InitType(assemblies[0], "System", "String")); members[KnownMembers.TextBlock_TextDecorations] = InitMember(KnownTypes.TextBlock, "TextDecorations", InitType(assemblies[3], "System.Windows", "TextDecorationCollection")); members[KnownMembers.TextBlock_TextTrimming] = InitMember(KnownTypes.TextBlock, "TextTrimming", InitType(assemblies[3], "System.Windows", "TextTrimming")); members[KnownMembers.TextBlock_TextWrapping] = InitMember(KnownTypes.TextBlock, "TextWrapping", InitType(assemblies[3], "System.Windows", "TextWrapping")); members[KnownMembers.TextBox_Text] = InitMember(KnownTypes.TextBox, "Text", InitType(assemblies[0], "System", "String")); members[KnownMembers.TextElement_Background] = InitMember(KnownTypes.TextElement, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.TextElement_FontFamily] = InitMember(KnownTypes.TextElement, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); members[KnownMembers.TextElement_FontSize] = InitMember(KnownTypes.TextElement, "FontSize", InitType(assemblies[0], "System", "Double")); members[KnownMembers.TextElement_FontStretch] = InitMember(KnownTypes.TextElement, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); members[KnownMembers.TextElement_FontStyle] = InitMember(KnownTypes.TextElement, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); members[KnownMembers.TextElement_FontWeight] = InitMember(KnownTypes.TextElement, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); members[KnownMembers.TextElement_Foreground] = InitMember(KnownTypes.TextElement, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); members[KnownMembers.TimelineGroup_Children] = InitMember(KnownTypes.TimelineGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); members[KnownMembers.Track_IsDirectionReversed] = InitMember(KnownTypes.Track, "IsDirectionReversed", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.Track_Maximum] = InitMember(KnownTypes.Track, "Maximum", InitType(assemblies[0], "System", "Double")); members[KnownMembers.Track_Minimum] = InitMember(KnownTypes.Track, "Minimum", InitType(assemblies[0], "System", "Double")); members[KnownMembers.Track_Orientation] = InitMember(KnownTypes.Track, "Orientation", InitType(assemblies[4], "System.Windows.Controls", "Orientation")); members[KnownMembers.Track_Value] = InitMember(KnownTypes.Track, "Value", InitType(assemblies[0], "System", "Double")); members[KnownMembers.Track_ViewportSize] = InitMember(KnownTypes.Track, "ViewportSize", InitType(assemblies[0], "System", "Double")); members[KnownMembers.Transform3DGroup_Children] = InitMember(KnownTypes.Transform3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection")); members[KnownMembers.TransformGroup_Children] = InitMember(KnownTypes.TransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "TransformCollection")); members[KnownMembers.UIElement_ClipToBounds] = InitMember(KnownTypes.UIElement, "ClipToBounds", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.UIElement_Focusable] = InitMember(KnownTypes.UIElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.UIElement_IsEnabled] = InitMember(KnownTypes.UIElement, "IsEnabled", InitType(assemblies[0], "System", "Boolean")); members[KnownMembers.UIElement_RenderTransform] = InitMember(KnownTypes.UIElement, "RenderTransform", InitType(assemblies[3], "System.Windows.Media", "Transform")); members[KnownMembers.UIElement_Visibility] = InitMember(KnownTypes.UIElement, "Visibility", InitType(assemblies[3], "System.Windows", "Visibility")); members[KnownMembers.Viewport3D_Children] = InitMember(KnownTypes.Viewport3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); members[KnownMembers.AdornedElementPlaceholder_Child] = InitMember(KnownTypes.AdornedElementPlaceholder, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.AdornerDecorator_Child] = InitMember(KnownTypes.AdornerDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.AnchoredBlock_Blocks] = InitMember(KnownTypes.AnchoredBlock, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.ArrayExtension_Items] = InitMember(KnownTypes.ArrayExtension, "Items", InitType(assemblies[0], "System.Collections", "IList")); members[KnownMembers.BlockUIContainer_Child] = InitMember(KnownTypes.BlockUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.Bold_Inlines] = InitMember(KnownTypes.Bold, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.BooleanAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.BooleanAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection")); members[KnownMembers.Border_Child] = InitMember(KnownTypes.Border, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.BulletDecorator_Child] = InitMember(KnownTypes.BulletDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.Button_Content] = InitMember(KnownTypes.Button, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ButtonBase_Content] = InitMember(KnownTypes.ButtonBase, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ByteAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ByteAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection")); members[KnownMembers.Canvas_Children] = InitMember(KnownTypes.Canvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.CharAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.CharAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection")); members[KnownMembers.CheckBox_Content] = InitMember(KnownTypes.CheckBox, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ColorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ColorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection")); members[KnownMembers.ComboBox_Items] = InitMember(KnownTypes.ComboBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ComboBoxItem_Content] = InitMember(KnownTypes.ComboBoxItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ContextMenu_Items] = InitMember(KnownTypes.ContextMenu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ControlTemplate_VisualTree] = InitMember(KnownTypes.ControlTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); members[KnownMembers.DataTemplate_VisualTree] = InitMember(KnownTypes.DataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); members[KnownMembers.DataTrigger_Setters] = InitMember(KnownTypes.DataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); members[KnownMembers.DecimalAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DecimalAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection")); members[KnownMembers.Decorator_Child] = InitMember(KnownTypes.Decorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.DockPanel_Children] = InitMember(KnownTypes.DockPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.DocumentViewer_Document] = InitMember(KnownTypes.DocumentViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); members[KnownMembers.DoubleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DoubleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection")); members[KnownMembers.EventTrigger_Actions] = InitMember(KnownTypes.EventTrigger, "Actions", InitType(assemblies[4], "System.Windows", "TriggerActionCollection")); members[KnownMembers.Expander_Content] = InitMember(KnownTypes.Expander, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Figure_Blocks] = InitMember(KnownTypes.Figure, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.FixedDocument_Pages] = InitMember(KnownTypes.FixedDocument, "Pages", InitType(assemblies[4], "System.Windows.Documents", "PageContentCollection")); members[KnownMembers.FixedDocumentSequence_References] = InitMember(KnownTypes.FixedDocumentSequence, "References", InitType(assemblies[4], "System.Windows.Documents", "DocumentReferenceCollection")); members[KnownMembers.FixedPage_Children] = InitMember(KnownTypes.FixedPage, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.Floater_Blocks] = InitMember(KnownTypes.Floater, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.FlowDocument_Blocks] = InitMember(KnownTypes.FlowDocument, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.FlowDocumentPageViewer_Document] = InitMember(KnownTypes.FlowDocumentPageViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); members[KnownMembers.FrameworkTemplate_VisualTree] = InitMember(KnownTypes.FrameworkTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); members[KnownMembers.Grid_Children] = InitMember(KnownTypes.Grid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.GridView_Columns] = InitMember(KnownTypes.GridView, "Columns", InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnCollection")); members[KnownMembers.GridViewColumnHeader_Content] = InitMember(KnownTypes.GridViewColumnHeader, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.GroupBox_Content] = InitMember(KnownTypes.GroupBox, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.GroupItem_Content] = InitMember(KnownTypes.GroupItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.HeaderedContentControl_Content] = InitMember(KnownTypes.HeaderedContentControl, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.HeaderedItemsControl_Items] = InitMember(KnownTypes.HeaderedItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.HierarchicalDataTemplate_VisualTree] = InitMember(KnownTypes.HierarchicalDataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); members[KnownMembers.Hyperlink_Inlines] = InitMember(KnownTypes.Hyperlink, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.InkCanvas_Children] = InitMember(KnownTypes.InkCanvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.InkPresenter_Child] = InitMember(KnownTypes.InkPresenter, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.InlineUIContainer_Child] = InitMember(KnownTypes.InlineUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.InputScopeName_NameValue] = InitMember(KnownTypes.InputScopeName, "NameValue", InitType(assemblies[3], "System.Windows.Input", "InputScopeNameValue")); members[KnownMembers.Int16AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int16AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection")); members[KnownMembers.Int32AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int32AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection")); members[KnownMembers.Int64AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int64AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection")); members[KnownMembers.Italic_Inlines] = InitMember(KnownTypes.Italic, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.ItemsControl_Items] = InitMember(KnownTypes.ItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ItemsPanelTemplate_VisualTree] = InitMember(KnownTypes.ItemsPanelTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); members[KnownMembers.Label_Content] = InitMember(KnownTypes.Label, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.LinearGradientBrush_GradientStops] = InitMember(KnownTypes.LinearGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); members[KnownMembers.List_ListItems] = InitMember(KnownTypes.List, "ListItems", InitType(assemblies[4], "System.Windows.Documents", "ListItemCollection")); members[KnownMembers.ListBox_Items] = InitMember(KnownTypes.ListBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ListBoxItem_Content] = InitMember(KnownTypes.ListBoxItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ListItem_Blocks] = InitMember(KnownTypes.ListItem, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.ListView_Items] = InitMember(KnownTypes.ListView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ListViewItem_Content] = InitMember(KnownTypes.ListViewItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.MatrixAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.MatrixAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection")); members[KnownMembers.Menu_Items] = InitMember(KnownTypes.Menu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.MenuBase_Items] = InitMember(KnownTypes.MenuBase, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.MenuItem_Items] = InitMember(KnownTypes.MenuItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ModelVisual3D_Children] = InitMember(KnownTypes.ModelVisual3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); members[KnownMembers.MultiBinding_Bindings] = InitMember(KnownTypes.MultiBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); members[KnownMembers.MultiDataTrigger_Setters] = InitMember(KnownTypes.MultiDataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); members[KnownMembers.MultiTrigger_Setters] = InitMember(KnownTypes.MultiTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); members[KnownMembers.ObjectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ObjectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection")); members[KnownMembers.PageContent_Child] = InitMember(KnownTypes.PageContent, "Child", InitType(assemblies[4], "System.Windows.Documents", "FixedPage")); members[KnownMembers.PageFunctionBase_Content] = InitMember(KnownTypes.PageFunctionBase, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Panel_Children] = InitMember(KnownTypes.Panel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.Paragraph_Inlines] = InitMember(KnownTypes.Paragraph, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.ParallelTimeline_Children] = InitMember(KnownTypes.ParallelTimeline, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); members[KnownMembers.Point3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Point3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection")); members[KnownMembers.PointAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.PointAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection")); members[KnownMembers.PriorityBinding_Bindings] = InitMember(KnownTypes.PriorityBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); members[KnownMembers.QuaternionAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.QuaternionAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection")); members[KnownMembers.RadialGradientBrush_GradientStops] = InitMember(KnownTypes.RadialGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); members[KnownMembers.RadioButton_Content] = InitMember(KnownTypes.RadioButton, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.RectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.RectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection")); members[KnownMembers.RepeatButton_Content] = InitMember(KnownTypes.RepeatButton, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.RichTextBox_Document] = InitMember(KnownTypes.RichTextBox, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); members[KnownMembers.Rotation3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Rotation3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection")); members[KnownMembers.Run_Text] = InitMember(KnownTypes.Run, "Text", InitType(assemblies[0], "System", "String")); members[KnownMembers.ScrollViewer_Content] = InitMember(KnownTypes.ScrollViewer, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Section_Blocks] = InitMember(KnownTypes.Section, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.Selector_Items] = InitMember(KnownTypes.Selector, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.SingleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SingleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection")); members[KnownMembers.SizeAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SizeAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection")); members[KnownMembers.Span_Inlines] = InitMember(KnownTypes.Span, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.StackPanel_Children] = InitMember(KnownTypes.StackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.StatusBar_Items] = InitMember(KnownTypes.StatusBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.StatusBarItem_Content] = InitMember(KnownTypes.StatusBarItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Storyboard_Children] = InitMember(KnownTypes.Storyboard, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); members[KnownMembers.StringAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.StringAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection")); members[KnownMembers.Style_Setters] = InitMember(KnownTypes.Style, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); members[KnownMembers.TabControl_Items] = InitMember(KnownTypes.TabControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.TabItem_Content] = InitMember(KnownTypes.TabItem, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.TabPanel_Children] = InitMember(KnownTypes.TabPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.Table_RowGroups] = InitMember(KnownTypes.Table, "RowGroups", InitType(assemblies[4], "System.Windows.Documents", "TableRowGroupCollection")); members[KnownMembers.TableCell_Blocks] = InitMember(KnownTypes.TableCell, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); members[KnownMembers.TableRow_Cells] = InitMember(KnownTypes.TableRow, "Cells", InitType(assemblies[4], "System.Windows.Documents", "TableCellCollection")); members[KnownMembers.TableRowGroup_Rows] = InitMember(KnownTypes.TableRowGroup, "Rows", InitType(assemblies[4], "System.Windows.Documents", "TableRowCollection")); members[KnownMembers.TextBlock_Inlines] = InitMember(KnownTypes.TextBlock, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.ThicknessAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ThicknessAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection")); members[KnownMembers.ToggleButton_Content] = InitMember(KnownTypes.ToggleButton, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.ToolBar_Items] = InitMember(KnownTypes.ToolBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.ToolBarOverflowPanel_Children] = InitMember(KnownTypes.ToolBarOverflowPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.ToolBarPanel_Children] = InitMember(KnownTypes.ToolBarPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.ToolBarTray_ToolBars] = InitMember(KnownTypes.ToolBarTray, "ToolBars", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); members[KnownMembers.ToolTip_Content] = InitMember(KnownTypes.ToolTip, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.TreeView_Items] = InitMember(KnownTypes.TreeView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.TreeViewItem_Items] = InitMember(KnownTypes.TreeViewItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); members[KnownMembers.Trigger_Setters] = InitMember(KnownTypes.Trigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); members[KnownMembers.Underline_Inlines] = InitMember(KnownTypes.Underline, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); members[KnownMembers.UniformGrid_Children] = InitMember(KnownTypes.UniformGrid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.UserControl_Content] = InitMember(KnownTypes.UserControl, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.Vector3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Vector3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection")); members[KnownMembers.VectorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.VectorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection")); members[KnownMembers.Viewbox_Child] = InitMember(KnownTypes.Viewbox, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); members[KnownMembers.Viewport3DVisual_Children] = InitMember(KnownTypes.Viewport3DVisual, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); members[KnownMembers.VirtualizingPanel_Children] = InitMember(KnownTypes.VirtualizingPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.VirtualizingStackPanel_Children] = InitMember(KnownTypes.VirtualizingStackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.Window_Content] = InitMember(KnownTypes.Window, "Content", InitType(assemblies[0], "System", "Object")); members[KnownMembers.WrapPanel_Children] = InitMember(KnownTypes.WrapPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); members[KnownMembers.XmlDataProvider_XmlSerializer] = InitMember(KnownTypes.XmlDataProvider, "XmlSerializer", InitType(assemblies[5], "System.Xml.Serialization", "IXmlSerializable")); } void InitStrings() { strings[1] = "Name"; strings[2] = "Uid"; } void InitResources() { resources[1] = ("SystemColors", "ActiveBorderBrushKey", "ActiveBorderBrush"); resources[2] = ("SystemColors", "ActiveCaptionBrushKey", "ActiveCaptionBrush"); resources[3] = ("SystemColors", "ActiveCaptionTextBrushKey", "ActiveCaptionTextBrush"); resources[4] = ("SystemColors", "AppWorkspaceBrushKey", "AppWorkspaceBrush"); resources[5] = ("SystemColors", "ControlBrushKey", "ControlBrush"); resources[6] = ("SystemColors", "ControlDarkBrushKey", "ControlDarkBrush"); resources[7] = ("SystemColors", "ControlDarkDarkBrushKey", "ControlDarkDarkBrush"); resources[8] = ("SystemColors", "ControlLightBrushKey", "ControlLightBrush"); resources[9] = ("SystemColors", "ControlLightLightBrushKey", "ControlLightLightBrush"); resources[10] = ("SystemColors", "ControlTextBrushKey", "ControlTextBrush"); resources[11] = ("SystemColors", "DesktopBrushKey", "DesktopBrush"); resources[12] = ("SystemColors", "GradientActiveCaptionBrushKey", "GradientActiveCaptionBrush"); resources[13] = ("SystemColors", "GradientInactiveCaptionBrushKey", "GradientInactiveCaptionBrush"); resources[14] = ("SystemColors", "GrayTextBrushKey", "GrayTextBrush"); resources[15] = ("SystemColors", "HighlightBrushKey", "HighlightBrush"); resources[16] = ("SystemColors", "HighlightTextBrushKey", "HighlightTextBrush"); resources[17] = ("SystemColors", "HotTrackBrushKey", "HotTrackBrush"); resources[18] = ("SystemColors", "InactiveBorderBrushKey", "InactiveBorderBrush"); resources[19] = ("SystemColors", "InactiveCaptionBrushKey", "InactiveCaptionBrush"); resources[20] = ("SystemColors", "InactiveCaptionTextBrushKey", "InactiveCaptionTextBrush"); resources[21] = ("SystemColors", "InfoBrushKey", "InfoBrush"); resources[22] = ("SystemColors", "InfoTextBrushKey", "InfoTextBrush"); resources[23] = ("SystemColors", "MenuBrushKey", "MenuBrush"); resources[24] = ("SystemColors", "MenuBarBrushKey", "MenuBarBrush"); resources[25] = ("SystemColors", "MenuHighlightBrushKey", "MenuHighlightBrush"); resources[26] = ("SystemColors", "MenuTextBrushKey", "MenuTextBrush"); resources[27] = ("SystemColors", "ScrollBarBrushKey", "ScrollBarBrush"); resources[28] = ("SystemColors", "WindowBrushKey", "WindowBrush"); resources[29] = ("SystemColors", "WindowFrameBrushKey", "WindowFrameBrush"); resources[30] = ("SystemColors", "WindowTextBrushKey", "WindowTextBrush"); resources[31] = ("SystemColors", "ActiveBorderColorKey", "ActiveBorderColor"); resources[32] = ("SystemColors", "ActiveCaptionColorKey", "ActiveCaptionColor"); resources[33] = ("SystemColors", "ActiveCaptionTextColorKey", "ActiveCaptionTextColor"); resources[34] = ("SystemColors", "AppWorkspaceColorKey", "AppWorkspaceColor"); resources[35] = ("SystemColors", "ControlColorKey", "ControlColor"); resources[36] = ("SystemColors", "ControlDarkColorKey", "ControlDarkColor"); resources[37] = ("SystemColors", "ControlDarkDarkColorKey", "ControlDarkDarkColor"); resources[38] = ("SystemColors", "ControlLightColorKey", "ControlLightColor"); resources[39] = ("SystemColors", "ControlLightLightColorKey", "ControlLightLightColor"); resources[40] = ("SystemColors", "ControlTextColorKey", "ControlTextColor"); resources[41] = ("SystemColors", "DesktopColorKey", "DesktopColor"); resources[42] = ("SystemColors", "GradientActiveCaptionColorKey", "GradientActiveCaptionColor"); resources[43] = ("SystemColors", "GradientInactiveCaptionColorKey", "GradientInactiveCaptionColor"); resources[44] = ("SystemColors", "GrayTextColorKey", "GrayTextColor"); resources[45] = ("SystemColors", "HighlightColorKey", "HighlightColor"); resources[46] = ("SystemColors", "HighlightTextColorKey", "HighlightTextColor"); resources[47] = ("SystemColors", "HotTrackColorKey", "HotTrackColor"); resources[48] = ("SystemColors", "InactiveBorderColorKey", "InactiveBorderColor"); resources[49] = ("SystemColors", "InactiveCaptionColorKey", "InactiveCaptionColor"); resources[50] = ("SystemColors", "InactiveCaptionTextColorKey", "InactiveCaptionTextColor"); resources[51] = ("SystemColors", "InfoColorKey", "InfoColor"); resources[52] = ("SystemColors", "InfoTextColorKey", "InfoTextColor"); resources[53] = ("SystemColors", "MenuColorKey", "MenuColor"); resources[54] = ("SystemColors", "MenuBarColorKey", "MenuBarColor"); resources[55] = ("SystemColors", "MenuHighlightColorKey", "MenuHighlightColor"); resources[56] = ("SystemColors", "MenuTextColorKey", "MenuTextColor"); resources[57] = ("SystemColors", "ScrollBarColorKey", "ScrollBarColor"); resources[58] = ("SystemColors", "WindowColorKey", "WindowColor"); resources[59] = ("SystemColors", "WindowFrameColorKey", "WindowFrameColor"); resources[60] = ("SystemColors", "WindowTextColorKey", "WindowTextColor"); resources[63] = ("SystemFonts", "CaptionFontSizeKey", "CaptionFontSize"); resources[64] = ("SystemFonts", "CaptionFontFamilyKey", "CaptionFontFamily"); resources[65] = ("SystemFonts", "CaptionFontStyleKey", "CaptionFontStyle"); resources[66] = ("SystemFonts", "CaptionFontWeightKey", "CaptionFontWeight"); resources[67] = ("SystemFonts", "CaptionFontTextDecorationsKey", "CaptionFontTextDecorations"); resources[68] = ("SystemFonts", "SmallCaptionFontSizeKey", "SmallCaptionFontSize"); resources[69] = ("SystemFonts", "SmallCaptionFontFamilyKey", "SmallCaptionFontFamily"); resources[70] = ("SystemFonts", "SmallCaptionFontStyleKey", "SmallCaptionFontStyle"); resources[71] = ("SystemFonts", "SmallCaptionFontWeightKey", "SmallCaptionFontWeight"); resources[72] = ("SystemFonts", "SmallCaptionFontTextDecorationsKey", "SmallCaptionFontTextDecorations"); resources[73] = ("SystemFonts", "MenuFontSizeKey", "MenuFontSize"); resources[74] = ("SystemFonts", "MenuFontFamilyKey", "MenuFontFamily"); resources[75] = ("SystemFonts", "MenuFontStyleKey", "MenuFontStyle"); resources[76] = ("SystemFonts", "MenuFontWeightKey", "MenuFontWeight"); resources[77] = ("SystemFonts", "MenuFontTextDecorationsKey", "MenuFontTextDecorations"); resources[78] = ("SystemFonts", "StatusFontSizeKey", "StatusFontSize"); resources[79] = ("SystemFonts", "StatusFontFamilyKey", "StatusFontFamily"); resources[80] = ("SystemFonts", "StatusFontStyleKey", "StatusFontStyle"); resources[81] = ("SystemFonts", "StatusFontWeightKey", "StatusFontWeight"); resources[82] = ("SystemFonts", "StatusFontTextDecorationsKey", "StatusFontTextDecorations"); resources[83] = ("SystemFonts", "MessageFontSizeKey", "MessageFontSize"); resources[84] = ("SystemFonts", "MessageFontFamilyKey", "MessageFontFamily"); resources[85] = ("SystemFonts", "MessageFontStyleKey", "MessageFontStyle"); resources[86] = ("SystemFonts", "MessageFontWeightKey", "MessageFontWeight"); resources[87] = ("SystemFonts", "MessageFontTextDecorationsKey", "MessageFontTextDecorations"); resources[88] = ("SystemFonts", "IconFontSizeKey", "IconFontSize"); resources[89] = ("SystemFonts", "IconFontFamilyKey", "IconFontFamily"); resources[90] = ("SystemFonts", "IconFontStyleKey", "IconFontStyle"); resources[91] = ("SystemFonts", "IconFontWeightKey", "IconFontWeight"); resources[92] = ("SystemFonts", "IconFontTextDecorationsKey", "IconFontTextDecorations"); resources[95] = ("SystemParameters", "ThinHorizontalBorderHeightKey", "ThinHorizontalBorderHeight"); resources[96] = ("SystemParameters", "ThinVerticalBorderWidthKey", "ThinVerticalBorderWidth"); resources[97] = ("SystemParameters", "CursorWidthKey", "CursorWidth"); resources[98] = ("SystemParameters", "CursorHeightKey", "CursorHeight"); resources[99] = ("SystemParameters", "ThickHorizontalBorderHeightKey", "ThickHorizontalBorderHeight"); resources[100] = ("SystemParameters", "ThickVerticalBorderWidthKey", "ThickVerticalBorderWidth"); resources[101] = ("SystemParameters", "FixedFrameHorizontalBorderHeightKey", "FixedFrameHorizontalBorderHeight"); resources[102] = ("SystemParameters", "FixedFrameVerticalBorderWidthKey", "FixedFrameVerticalBorderWidth"); resources[103] = ("SystemParameters", "FocusHorizontalBorderHeightKey", "FocusHorizontalBorderHeight"); resources[104] = ("SystemParameters", "FocusVerticalBorderWidthKey", "FocusVerticalBorderWidth"); resources[105] = ("SystemParameters", "FullPrimaryScreenWidthKey", "FullPrimaryScreenWidth"); resources[106] = ("SystemParameters", "FullPrimaryScreenHeightKey", "FullPrimaryScreenHeight"); resources[107] = ("SystemParameters", "HorizontalScrollBarButtonWidthKey", "HorizontalScrollBarButtonWidth"); resources[108] = ("SystemParameters", "HorizontalScrollBarHeightKey", "HorizontalScrollBarHeight"); resources[109] = ("SystemParameters", "HorizontalScrollBarThumbWidthKey", "HorizontalScrollBarThumbWidth"); resources[110] = ("SystemParameters", "IconWidthKey", "IconWidth"); resources[111] = ("SystemParameters", "IconHeightKey", "IconHeight"); resources[112] = ("SystemParameters", "IconGridWidthKey", "IconGridWidth"); resources[113] = ("SystemParameters", "IconGridHeightKey", "IconGridHeight"); resources[114] = ("SystemParameters", "MaximizedPrimaryScreenWidthKey", "MaximizedPrimaryScreenWidth"); resources[115] = ("SystemParameters", "MaximizedPrimaryScreenHeightKey", "MaximizedPrimaryScreenHeight"); resources[116] = ("SystemParameters", "MaximumWindowTrackWidthKey", "MaximumWindowTrackWidth"); resources[117] = ("SystemParameters", "MaximumWindowTrackHeightKey", "MaximumWindowTrackHeight"); resources[118] = ("SystemParameters", "MenuCheckmarkWidthKey", "MenuCheckmarkWidth"); resources[119] = ("SystemParameters", "MenuCheckmarkHeightKey", "MenuCheckmarkHeight"); resources[120] = ("SystemParameters", "MenuButtonWidthKey", "MenuButtonWidth"); resources[121] = ("SystemParameters", "MenuButtonHeightKey", "MenuButtonHeight"); resources[122] = ("SystemParameters", "MinimumWindowWidthKey", "MinimumWindowWidth"); resources[123] = ("SystemParameters", "MinimumWindowHeightKey", "MinimumWindowHeight"); resources[124] = ("SystemParameters", "MinimizedWindowWidthKey", "MinimizedWindowWidth"); resources[125] = ("SystemParameters", "MinimizedWindowHeightKey", "MinimizedWindowHeight"); resources[126] = ("SystemParameters", "MinimizedGridWidthKey", "MinimizedGridWidth"); resources[127] = ("SystemParameters", "MinimizedGridHeightKey", "MinimizedGridHeight"); resources[128] = ("SystemParameters", "MinimumWindowTrackWidthKey", "MinimumWindowTrackWidth"); resources[129] = ("SystemParameters", "MinimumWindowTrackHeightKey", "MinimumWindowTrackHeight"); resources[130] = ("SystemParameters", "PrimaryScreenWidthKey", "PrimaryScreenWidth"); resources[131] = ("SystemParameters", "PrimaryScreenHeightKey", "PrimaryScreenHeight"); resources[132] = ("SystemParameters", "WindowCaptionButtonWidthKey", "WindowCaptionButtonWidth"); resources[133] = ("SystemParameters", "WindowCaptionButtonHeightKey", "WindowCaptionButtonHeight"); resources[134] = ("SystemParameters", "ResizeFrameHorizontalBorderHeightKey", "ResizeFrameHorizontalBorderHeight"); resources[135] = ("SystemParameters", "ResizeFrameVerticalBorderWidthKey", "ResizeFrameVerticalBorderWidth"); resources[136] = ("SystemParameters", "SmallIconWidthKey", "SmallIconWidth"); resources[137] = ("SystemParameters", "SmallIconHeightKey", "SmallIconHeight"); resources[138] = ("SystemParameters", "SmallWindowCaptionButtonWidthKey", "SmallWindowCaptionButtonWidth"); resources[139] = ("SystemParameters", "SmallWindowCaptionButtonHeightKey", "SmallWindowCaptionButtonHeight"); resources[140] = ("SystemParameters", "VirtualScreenWidthKey", "VirtualScreenWidth"); resources[141] = ("SystemParameters", "VirtualScreenHeightKey", "VirtualScreenHeight"); resources[142] = ("SystemParameters", "VerticalScrollBarWidthKey", "VerticalScrollBarWidth"); resources[143] = ("SystemParameters", "VerticalScrollBarButtonHeightKey", "VerticalScrollBarButtonHeight"); resources[144] = ("SystemParameters", "WindowCaptionHeightKey", "WindowCaptionHeight"); resources[145] = ("SystemParameters", "KanjiWindowHeightKey", "KanjiWindowHeight"); resources[146] = ("SystemParameters", "MenuBarHeightKey", "MenuBarHeight"); resources[147] = ("SystemParameters", "SmallCaptionHeightKey", "SmallCaptionHeight"); resources[148] = ("SystemParameters", "VerticalScrollBarThumbHeightKey", "VerticalScrollBarThumbHeight"); resources[149] = ("SystemParameters", "IsImmEnabledKey", "IsImmEnabled"); resources[150] = ("SystemParameters", "IsMediaCenterKey", "IsMediaCenter"); resources[151] = ("SystemParameters", "IsMenuDropRightAlignedKey", "IsMenuDropRightAligned"); resources[152] = ("SystemParameters", "IsMiddleEastEnabledKey", "IsMiddleEastEnabled"); resources[153] = ("SystemParameters", "IsMousePresentKey", "IsMousePresent"); resources[154] = ("SystemParameters", "IsMouseWheelPresentKey", "IsMouseWheelPresent"); resources[155] = ("SystemParameters", "IsPenWindowsKey", "IsPenWindows"); resources[156] = ("SystemParameters", "IsRemotelyControlledKey", "IsRemotelyControlled"); resources[157] = ("SystemParameters", "IsRemoteSessionKey", "IsRemoteSession"); resources[158] = ("SystemParameters", "ShowSoundsKey", "ShowSounds"); resources[159] = ("SystemParameters", "IsSlowMachineKey", "IsSlowMachine"); resources[160] = ("SystemParameters", "SwapButtonsKey", "SwapButtons"); resources[161] = ("SystemParameters", "IsTabletPCKey", "IsTabletPC"); resources[162] = ("SystemParameters", "VirtualScreenLeftKey", "VirtualScreenLeft"); resources[163] = ("SystemParameters", "VirtualScreenTopKey", "VirtualScreenTop"); resources[164] = ("SystemParameters", "FocusBorderWidthKey", "FocusBorderWidth"); resources[165] = ("SystemParameters", "FocusBorderHeightKey", "FocusBorderHeight"); resources[166] = ("SystemParameters", "HighContrastKey", "HighContrast"); resources[167] = ("SystemParameters", "DropShadowKey", "DropShadow"); resources[168] = ("SystemParameters", "FlatMenuKey", "FlatMenu"); resources[169] = ("SystemParameters", "WorkAreaKey", "WorkArea"); resources[170] = ("SystemParameters", "IconHorizontalSpacingKey", "IconHorizontalSpacing"); resources[171] = ("SystemParameters", "IconVerticalSpacingKey", "IconVerticalSpacing"); resources[172] = ("SystemParameters", "IconTitleWrapKey", "IconTitleWrap"); resources[173] = ("SystemParameters", "KeyboardCuesKey", "KeyboardCues"); resources[174] = ("SystemParameters", "KeyboardDelayKey", "KeyboardDelay"); resources[175] = ("SystemParameters", "KeyboardPreferenceKey", "KeyboardPreference"); resources[176] = ("SystemParameters", "KeyboardSpeedKey", "KeyboardSpeed"); resources[177] = ("SystemParameters", "SnapToDefaultButtonKey", "SnapToDefaultButton"); resources[178] = ("SystemParameters", "WheelScrollLinesKey", "WheelScrollLines"); resources[179] = ("SystemParameters", "MouseHoverTimeKey", "MouseHoverTime"); resources[180] = ("SystemParameters", "MouseHoverHeightKey", "MouseHoverHeight"); resources[181] = ("SystemParameters", "MouseHoverWidthKey", "MouseHoverWidth"); resources[182] = ("SystemParameters", "MenuDropAlignmentKey", "MenuDropAlignment"); resources[183] = ("SystemParameters", "MenuFadeKey", "MenuFade"); resources[184] = ("SystemParameters", "MenuShowDelayKey", "MenuShowDelay"); resources[185] = ("SystemParameters", "ComboBoxAnimationKey", "ComboBoxAnimation"); resources[186] = ("SystemParameters", "ClientAreaAnimationKey", "ClientAreaAnimation"); resources[187] = ("SystemParameters", "CursorShadowKey", "CursorShadow"); resources[188] = ("SystemParameters", "GradientCaptionsKey", "GradientCaptions"); resources[189] = ("SystemParameters", "HotTrackingKey", "HotTracking"); resources[190] = ("SystemParameters", "ListBoxSmoothScrollingKey", "ListBoxSmoothScrolling"); resources[191] = ("SystemParameters", "MenuAnimationKey", "MenuAnimation"); resources[192] = ("SystemParameters", "SelectionFadeKey", "SelectionFade"); resources[193] = ("SystemParameters", "StylusHotTrackingKey", "StylusHotTracking"); resources[194] = ("SystemParameters", "ToolTipAnimationKey", "ToolTipAnimation"); resources[195] = ("SystemParameters", "ToolTipFadeKey", "ToolTipFade"); resources[196] = ("SystemParameters", "UIEffectsKey", "UIEffects"); resources[197] = ("SystemParameters", "MinimizeAnimationKey", "MinimizeAnimation"); resources[198] = ("SystemParameters", "BorderKey", "Border"); resources[199] = ("SystemParameters", "CaretWidthKey", "CaretWidth"); resources[200] = ("SystemParameters", "ForegroundFlashCountKey", "ForegroundFlashCount"); resources[201] = ("SystemParameters", "DragFullWindowsKey", "DragFullWindows"); resources[202] = ("SystemParameters", "BorderWidthKey", "BorderWidth"); resources[203] = ("SystemParameters", "ScrollWidthKey", "ScrollWidth"); resources[204] = ("SystemParameters", "ScrollHeightKey", "ScrollHeight"); resources[205] = ("SystemParameters", "CaptionWidthKey", "CaptionWidth"); resources[206] = ("SystemParameters", "CaptionHeightKey", "CaptionHeight"); resources[207] = ("SystemParameters", "SmallCaptionWidthKey", "SmallCaptionWidth"); resources[208] = ("SystemParameters", "MenuWidthKey", "MenuWidth"); resources[209] = ("SystemParameters", "MenuHeightKey", "MenuHeight"); resources[210] = ("SystemParameters", "ComboBoxPopupAnimationKey", "ComboBoxPopupAnimation"); resources[211] = ("SystemParameters", "MenuPopupAnimationKey", "MenuPopupAnimation"); resources[212] = ("SystemParameters", "ToolTipPopupAnimationKey", "ToolTipPopupAnimation"); resources[213] = ("SystemParameters", "PowerLineStatusKey", "PowerLineStatus"); resources[215] = ("SystemParameters", "FocusVisualStyleKey", "FocusVisualStyle"); resources[216] = ("SystemParameters", "NavigationChromeDownLevelStyleKey", "NavigationChromeDownLevelStyle"); resources[217] = ("SystemParameters", "NavigationChromeStyleKey", "NavigationChromeStyle"); resources[219] = ("MenuItem", "SeparatorStyleKey", "MenuItemSeparatorStyle"); resources[220] = ("GridView", "GridViewScrollViewerStyleKey", "GridViewScrollViewerStyle"); resources[221] = ("GridView", "GridViewStyleKey", "GridViewStyle"); resources[222] = ("GridView", "GridViewItemContainerStyleKey", "GridViewItemContainerStyle"); resources[223] = ("StatusBar", "SeparatorStyleKey", "StatusBarSeparatorStyle"); resources[224] = ("ToolBar", "ButtonStyleKey", "ToolBarButtonStyle"); resources[225] = ("ToolBar", "ToggleButtonStyleKey", "ToolBarToggleButtonStyle"); resources[226] = ("ToolBar", "SeparatorStyleKey", "ToolBarSeparatorStyle"); resources[227] = ("ToolBar", "CheckBoxStyleKey", "ToolBarCheckBoxStyle"); resources[228] = ("ToolBar", "RadioButtonStyleKey", "ToolBarRadioButtonStyle"); resources[229] = ("ToolBar", "ComboBoxStyleKey", "ToolBarComboBoxStyle"); resources[230] = ("ToolBar", "TextBoxStyleKey", "ToolBarTextBoxStyle"); resources[231] = ("ToolBar", "MenuStyleKey", "ToolBarMenuStyle"); resources[234] = ("SystemColors", "InactiveSelectionHighlightBrushKey", "InactiveSelectionHighlightBrush"); resources[235] = ("SystemColors", "InactiveSelectionHighlightTextBrushKey", "InactiveSelectionHighlightTextBrush"); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/KnownThings.gen.cs ================================================ /* Copyright (c) 2015 Ki 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.Reflection; using System.Runtime.CompilerServices; using System.Text; namespace Test { internal class Program { static T GetMember(Func func, string name) => func(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); static Assembly GetDeclAssembly(Type type) { if (type.IsDefined(typeof(TypeForwardedFromAttribute), false)) { var attr = (TypeForwardedFromAttribute)type.GetCustomAttributes(typeof(TypeForwardedFromAttribute), false)[0]; return Assembly.Load(attr.AssemblyFullName); } return type.Assembly; } static void Main(string[] args) { var asmName = "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; var assembly = Assembly.Load(asmName); var ctx = assembly.GetType("System.Windows.Baml2006.WpfSharedBamlSchemaContext"); var instance = Activator.CreateInstance(ctx); var assemblies = ExtractAssemblies(ctx, instance); var types = ExtractTypes(ctx, instance); var members = ExtractMembers(ctx, instance); var strings = ExtractStrings(ctx, instance); var resources = ExtractResources(assembly); var code = new StringBuilder(); foreach (var type in types) { if (type == null) continue; if (!assemblies.Contains(GetDeclAssembly(type))) assemblies.Add(GetDeclAssembly(type)); } foreach (var member in members) { if (member == null) continue; if (!assemblies.Contains(GetDeclAssembly(member.Item3))) assemblies.Add(GetDeclAssembly(member.Item3)); } code.AppendLine("\tinternal enum KnownTypes : short {"); code.AppendLine("\t\tUnknown = 0,"); for (int i = 1; i < types.Count; i++) { if (types[i] == null) { code.AppendLine(); continue; } var line = "\t\t{0} = {1},"; code.AppendLine(string.Format(line, types[i].Name, i)); } code.AppendLine("\t}").AppendLine(); code.AppendLine("\tinternal enum KnownMembers : short {"); code.AppendLine("\t\tUnknown = 0,"); for (int i = 1; i < members.Count; i++) { if (members[i] == null) { code.AppendLine(); continue; } var line = "\t\t{0}_{1} = {2},"; code.AppendLine(string.Format(line, members[i].Item1.Name, members[i].Item2, i)); } code.AppendLine("\t}").AppendLine(); code.AppendLine("\t\tvoid InitAssemblies() {"); for (int i = 0; i < assemblies.Count; i++) { var line = "\t\t\tassemblies[{0}] = ResolveAssembly(\"{1}\");"; code.AppendLine(string.Format(line, i, assemblies[i])); } code.AppendLine("\t\t}").AppendLine(); code.AppendLine("\t\tvoid InitTypes() {"); for (int i = 0; i < types.Count; i++) { var type = types[i]; if (type == null) { code.AppendLine(); continue; } var line = "\t\t\ttypes[KnownTypes.{0}] = InitType(assemblies[{1}], \"{2}\", \"{3}\");"; code.AppendLine(string.Format(line, new object[] { type.Name, assemblies.IndexOf(GetDeclAssembly(type)), type.Namespace, type.Name })); } code.AppendLine("\t\t}").AppendLine(); code.AppendLine("\t\tvoid InitMembers() {"); for (int i = 0; i < members.Count; i++) { var member = members[i]; if (member == null) { code.AppendLine(); continue; } var line = "\t\t\tmembers[KnownMembers.{0}_{1}] = InitMember(KnownTypes.{0}, \"{1}\", InitType(assemblies[{2}], \"{3}\", \"{4}\"));"; code.AppendLine(string.Format(line, new object[] { member.Item1.Name, member.Item2, assemblies.IndexOf(GetDeclAssembly(member.Item3)), member.Item3.Namespace, member.Item3.Name })); } code.AppendLine("\t\t}").AppendLine(); code.AppendLine("\t\tvoid InitStrings() {"); for (int i = 0; i < strings.Count; i++) { if (strings[i] == null) continue; var line = "\t\t\tstrings[{0}] = \"{1}\";"; code.AppendLine(string.Format(line, i, strings[i])); } code.AppendLine("\t\t}").AppendLine(); code.AppendLine("\t\tvoid InitResources() {"); for (int i = 0; i < resources.Count; i++) { if (resources[i] == null) { code.AppendLine(); continue; } var res = resources[i]; var line = "\t\t\tresources[{0}] = (\"{1}\", \"{2}\", \"{3}\");"; code.AppendLine(string.Format(line, i, res.Item1.Name, res.Item2, res.Item3)); } code.AppendLine("\t\t}").AppendLine(); Console.WriteLine(code); } static List ExtractAssemblies(Type ctx, object instance) { var getAssembly = GetMember(ctx.GetMethod, "GetKnownBamlAssembly"); var assemblies = (Array)GetMember(ctx.GetField, "_knownBamlAssemblies").GetValue(instance); var bamlAssembly = ctx.Assembly.GetType("System.Windows.Baml2006.Baml6Assembly"); var property = GetMember(bamlAssembly.GetProperty, "Assembly"); var extract = new List(); for (int i = 0; i < assemblies.Length; i++) { try { var asm = getAssembly.Invoke(instance, new object[] { (short)(-i) }); var asmValue = (Assembly)property.GetValue(asm, null); extract.Add(asmValue); } catch { extract.Add(null); } } return extract; } static List ExtractTypes(Type ctx, object instance) { var getType = GetMember(ctx.GetMethod, "GetKnownBamlType"); var types = (Array)GetMember(ctx.GetField, "_knownBamlTypes").GetValue(instance); var bamlType = ctx.Assembly.GetType("System.Windows.Baml2006.WpfKnownType"); var field = GetMember(bamlType.GetField, "_underlyingType"); var extract = new List() { null }; for (int i = 1; i < types.Length; i++) { try { var type = getType.Invoke(instance, new object[] { (short)(-i) }); var typeValue = (Type)field.GetValue(type); extract.Add(typeValue); } catch { extract.Add(null); } } return extract; } static List> ExtractMembers(Type ctx, object instance) { var getMember = GetMember(ctx.GetMethod, "GetKnownBamlMember"); var members = (Array)GetMember(ctx.GetField, "_knownBamlMembers").GetValue(instance); var bamlMember = ctx.Assembly.GetType("System.Windows.Baml2006.WpfKnownMember"); var propertyName = GetMember(bamlMember.GetProperty, "Name"); var propertyType = GetMember(bamlMember.GetProperty, "Type"); var bamlType = ctx.Assembly.GetType("System.Windows.Baml2006.WpfKnownType"); var propertyUnderlyType = GetMember(bamlType.GetProperty, "UnderlyingType"); var mapTable = ctx.Assembly.GetType("System.Windows.Markup.BamlMapTable"); var getAttrRecord = mapTable.GetMethod("GetAttributeInfoFromId", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(short) }, null); var getAttrOwner = GetMember(mapTable.GetMethod, "GetAttributeOwnerType"); var tbl = mapTable.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0].Invoke(new object[] { null }); var extract = new List>() { null }; for (int i = 1; i < members.Length; i++) { try { var member = getMember.Invoke(instance, new object[] { (short)(-i) }); var name = (string)propertyName.GetValue(member, null); var type = (Type)propertyUnderlyType.GetValue(propertyType.GetValue(member, null), null); var declType = (Type)getAttrOwner.Invoke(tbl, new object[] { getAttrRecord.Invoke(tbl, new object[] { (short)(-i) }) }); extract.Add(Tuple.Create(declType, name, type)); } catch { extract.Add(null); } } return extract; } static List ExtractStrings(Type ctx, object instance) { var getString = GetMember(ctx.GetMethod, "GetKnownBamlString"); var extract = new List(); for (int i = 0; i < 10; i++) { try { var str = (string)getString.Invoke(instance, new object[] { (short)(-i) }); extract.Add(str); } catch { extract.Add(null); } } return extract; } static List> ExtractResources(Assembly asm) { var resIdType = asm.GetType("System.Windows.SystemResourceKeyID"); var cvrtType = asm.GetType("System.Windows.Markup.SystemKeyConverter"); var getSysType = GetMember(cvrtType.GetMethod, "GetSystemClassType"); var getSysKeyName = GetMember(cvrtType.GetMethod, "GetSystemKeyName"); var getSysPropertyName = GetMember(cvrtType.GetMethod, "GetSystemPropertyName"); var values = Enum.GetValues(resIdType); var extract = new List>(); for (int i = 0; i < values.Length; i++) { var value = values.GetValue(i); if (Enum.GetName(resIdType, value).StartsWith("Internal")) { extract.Add(null); continue; } var type = (Type)getSysType.Invoke(null, new object[] { value }); var keyName = (string)getSysKeyName.Invoke(null, new object[] { value }); var propName = (string)getSysPropertyName.Invoke(null, new object[] { value }); extract.Add(Tuple.Create(type, keyName, propName)); } return extract; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Baml/KnownTypes.cs ================================================ /* Copyright (c) 2015 Ki 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. */ namespace ICSharpCode.BamlDecompiler.Baml { // Auto generated. Do not modify. internal enum KnownTypes : short { Unknown = 0, AccessText = 1, AdornedElementPlaceholder = 2, Adorner = 3, AdornerDecorator = 4, AdornerLayer = 5, AffineTransform3D = 6, AmbientLight = 7, AnchoredBlock = 8, Animatable = 9, AnimationClock = 10, AnimationTimeline = 11, Application = 12, ArcSegment = 13, ArrayExtension = 14, AxisAngleRotation3D = 15, BaseIListConverter = 16, BeginStoryboard = 17, BevelBitmapEffect = 18, BezierSegment = 19, Binding = 20, BindingBase = 21, BindingExpression = 22, BindingExpressionBase = 23, BindingListCollectionView = 24, BitmapDecoder = 25, BitmapEffect = 26, BitmapEffectCollection = 27, BitmapEffectGroup = 28, BitmapEffectInput = 29, BitmapEncoder = 30, BitmapFrame = 31, BitmapImage = 32, BitmapMetadata = 33, BitmapPalette = 34, BitmapSource = 35, Block = 36, BlockUIContainer = 37, BlurBitmapEffect = 38, BmpBitmapDecoder = 39, BmpBitmapEncoder = 40, Bold = 41, BoolIListConverter = 42, Boolean = 43, BooleanAnimationBase = 44, BooleanAnimationUsingKeyFrames = 45, BooleanConverter = 46, BooleanKeyFrame = 47, BooleanKeyFrameCollection = 48, BooleanToVisibilityConverter = 49, Border = 50, BorderGapMaskConverter = 51, Brush = 52, BrushConverter = 53, BulletDecorator = 54, Button = 55, ButtonBase = 56, Byte = 57, ByteAnimation = 58, ByteAnimationBase = 59, ByteAnimationUsingKeyFrames = 60, ByteConverter = 61, ByteKeyFrame = 62, ByteKeyFrameCollection = 63, CachedBitmap = 64, Camera = 65, Canvas = 66, Char = 67, CharAnimationBase = 68, CharAnimationUsingKeyFrames = 69, CharConverter = 70, CharIListConverter = 71, CharKeyFrame = 72, CharKeyFrameCollection = 73, CheckBox = 74, Clock = 75, ClockController = 76, ClockGroup = 77, CollectionContainer = 78, CollectionView = 79, CollectionViewSource = 80, Color = 81, ColorAnimation = 82, ColorAnimationBase = 83, ColorAnimationUsingKeyFrames = 84, ColorConvertedBitmap = 85, ColorConvertedBitmapExtension = 86, ColorConverter = 87, ColorKeyFrame = 88, ColorKeyFrameCollection = 89, ColumnDefinition = 90, CombinedGeometry = 91, ComboBox = 92, ComboBoxItem = 93, CommandConverter = 94, ComponentResourceKey = 95, ComponentResourceKeyConverter = 96, CompositionTarget = 97, Condition = 98, ContainerVisual = 99, ContentControl = 100, ContentElement = 101, ContentPresenter = 102, ContentPropertyAttribute = 103, ContentWrapperAttribute = 104, ContextMenu = 105, ContextMenuService = 106, Control = 107, ControlTemplate = 108, ControllableStoryboardAction = 109, CornerRadius = 110, CornerRadiusConverter = 111, CroppedBitmap = 112, CultureInfo = 113, CultureInfoConverter = 114, CultureInfoIetfLanguageTagConverter = 115, Cursor = 116, CursorConverter = 117, DashStyle = 118, DataChangedEventManager = 119, DataTemplate = 120, DataTemplateKey = 121, DataTrigger = 122, DateTime = 123, DateTimeConverter = 124, DateTimeConverter2 = 125, Decimal = 126, DecimalAnimation = 127, DecimalAnimationBase = 128, DecimalAnimationUsingKeyFrames = 129, DecimalConverter = 130, DecimalKeyFrame = 131, DecimalKeyFrameCollection = 132, Decorator = 133, DefinitionBase = 134, DependencyObject = 135, DependencyProperty = 136, DependencyPropertyConverter = 137, DialogResultConverter = 138, DiffuseMaterial = 139, DirectionalLight = 140, DiscreteBooleanKeyFrame = 141, DiscreteByteKeyFrame = 142, DiscreteCharKeyFrame = 143, DiscreteColorKeyFrame = 144, DiscreteDecimalKeyFrame = 145, DiscreteDoubleKeyFrame = 146, DiscreteInt16KeyFrame = 147, DiscreteInt32KeyFrame = 148, DiscreteInt64KeyFrame = 149, DiscreteMatrixKeyFrame = 150, DiscreteObjectKeyFrame = 151, DiscretePoint3DKeyFrame = 152, DiscretePointKeyFrame = 153, DiscreteQuaternionKeyFrame = 154, DiscreteRectKeyFrame = 155, DiscreteRotation3DKeyFrame = 156, DiscreteSingleKeyFrame = 157, DiscreteSizeKeyFrame = 158, DiscreteStringKeyFrame = 159, DiscreteThicknessKeyFrame = 160, DiscreteVector3DKeyFrame = 161, DiscreteVectorKeyFrame = 162, DockPanel = 163, DocumentPageView = 164, DocumentReference = 165, DocumentViewer = 166, DocumentViewerBase = 167, Double = 168, DoubleAnimation = 169, DoubleAnimationBase = 170, DoubleAnimationUsingKeyFrames = 171, DoubleAnimationUsingPath = 172, DoubleCollection = 173, DoubleCollectionConverter = 174, DoubleConverter = 175, DoubleIListConverter = 176, DoubleKeyFrame = 177, DoubleKeyFrameCollection = 178, Drawing = 179, DrawingBrush = 180, DrawingCollection = 181, DrawingContext = 182, DrawingGroup = 183, DrawingImage = 184, DrawingVisual = 185, DropShadowBitmapEffect = 186, Duration = 187, DurationConverter = 188, DynamicResourceExtension = 189, DynamicResourceExtensionConverter = 190, Ellipse = 191, EllipseGeometry = 192, EmbossBitmapEffect = 193, EmissiveMaterial = 194, EnumConverter = 195, EventManager = 196, EventSetter = 197, EventTrigger = 198, Expander = 199, Expression = 200, ExpressionConverter = 201, Figure = 202, FigureLength = 203, FigureLengthConverter = 204, FixedDocument = 205, FixedDocumentSequence = 206, FixedPage = 207, Floater = 208, FlowDocument = 209, FlowDocumentPageViewer = 210, FlowDocumentReader = 211, FlowDocumentScrollViewer = 212, FocusManager = 213, FontFamily = 214, FontFamilyConverter = 215, FontSizeConverter = 216, FontStretch = 217, FontStretchConverter = 218, FontStyle = 219, FontStyleConverter = 220, FontWeight = 221, FontWeightConverter = 222, FormatConvertedBitmap = 223, Frame = 224, FrameworkContentElement = 225, FrameworkElement = 226, FrameworkElementFactory = 227, FrameworkPropertyMetadata = 228, FrameworkPropertyMetadataOptions = 229, FrameworkRichTextComposition = 230, FrameworkTemplate = 231, FrameworkTextComposition = 232, Freezable = 233, GeneralTransform = 234, GeneralTransformCollection = 235, GeneralTransformGroup = 236, Geometry = 237, Geometry3D = 238, GeometryCollection = 239, GeometryConverter = 240, GeometryDrawing = 241, GeometryGroup = 242, GeometryModel3D = 243, GestureRecognizer = 244, GifBitmapDecoder = 245, GifBitmapEncoder = 246, GlyphRun = 247, GlyphRunDrawing = 248, GlyphTypeface = 249, Glyphs = 250, GradientBrush = 251, GradientStop = 252, GradientStopCollection = 253, Grid = 254, GridLength = 255, GridLengthConverter = 256, GridSplitter = 257, GridView = 258, GridViewColumn = 259, GridViewColumnHeader = 260, GridViewHeaderRowPresenter = 261, GridViewRowPresenter = 262, GridViewRowPresenterBase = 263, GroupBox = 264, GroupItem = 265, Guid = 266, GuidConverter = 267, GuidelineSet = 268, HeaderedContentControl = 269, HeaderedItemsControl = 270, HierarchicalDataTemplate = 271, HostVisual = 272, Hyperlink = 273, IAddChild = 274, IAddChildInternal = 275, ICommand = 276, IComponentConnector = 277, INameScope = 278, IStyleConnector = 279, IconBitmapDecoder = 280, Image = 281, ImageBrush = 282, ImageDrawing = 283, ImageMetadata = 284, ImageSource = 285, ImageSourceConverter = 286, InPlaceBitmapMetadataWriter = 287, InkCanvas = 288, InkPresenter = 289, Inline = 290, InlineCollection = 291, InlineUIContainer = 292, InputBinding = 293, InputDevice = 294, InputLanguageManager = 295, InputManager = 296, InputMethod = 297, InputScope = 298, InputScopeConverter = 299, InputScopeName = 300, InputScopeNameConverter = 301, Int16 = 302, Int16Animation = 303, Int16AnimationBase = 304, Int16AnimationUsingKeyFrames = 305, Int16Converter = 306, Int16KeyFrame = 307, Int16KeyFrameCollection = 308, Int32 = 309, Int32Animation = 310, Int32AnimationBase = 311, Int32AnimationUsingKeyFrames = 312, Int32Collection = 313, Int32CollectionConverter = 314, Int32Converter = 315, Int32KeyFrame = 316, Int32KeyFrameCollection = 317, Int32Rect = 318, Int32RectConverter = 319, Int64 = 320, Int64Animation = 321, Int64AnimationBase = 322, Int64AnimationUsingKeyFrames = 323, Int64Converter = 324, Int64KeyFrame = 325, Int64KeyFrameCollection = 326, Italic = 327, ItemCollection = 328, ItemsControl = 329, ItemsPanelTemplate = 330, ItemsPresenter = 331, JournalEntry = 332, JournalEntryListConverter = 333, JournalEntryUnifiedViewConverter = 334, JpegBitmapDecoder = 335, JpegBitmapEncoder = 336, KeyBinding = 337, KeyConverter = 338, KeyGesture = 339, KeyGestureConverter = 340, KeySpline = 341, KeySplineConverter = 342, KeyTime = 343, KeyTimeConverter = 344, KeyboardDevice = 345, Label = 346, LateBoundBitmapDecoder = 347, LengthConverter = 348, Light = 349, Line = 350, LineBreak = 351, LineGeometry = 352, LineSegment = 353, LinearByteKeyFrame = 354, LinearColorKeyFrame = 355, LinearDecimalKeyFrame = 356, LinearDoubleKeyFrame = 357, LinearGradientBrush = 358, LinearInt16KeyFrame = 359, LinearInt32KeyFrame = 360, LinearInt64KeyFrame = 361, LinearPoint3DKeyFrame = 362, LinearPointKeyFrame = 363, LinearQuaternionKeyFrame = 364, LinearRectKeyFrame = 365, LinearRotation3DKeyFrame = 366, LinearSingleKeyFrame = 367, LinearSizeKeyFrame = 368, LinearThicknessKeyFrame = 369, LinearVector3DKeyFrame = 370, LinearVectorKeyFrame = 371, List = 372, ListBox = 373, ListBoxItem = 374, ListCollectionView = 375, ListItem = 376, ListView = 377, ListViewItem = 378, Localization = 379, LostFocusEventManager = 380, MarkupExtension = 381, Material = 382, MaterialCollection = 383, MaterialGroup = 384, Matrix = 385, Matrix3D = 386, Matrix3DConverter = 387, MatrixAnimationBase = 388, MatrixAnimationUsingKeyFrames = 389, MatrixAnimationUsingPath = 390, MatrixCamera = 391, MatrixConverter = 392, MatrixKeyFrame = 393, MatrixKeyFrameCollection = 394, MatrixTransform = 395, MatrixTransform3D = 396, MediaClock = 397, MediaElement = 398, MediaPlayer = 399, MediaTimeline = 400, Menu = 401, MenuBase = 402, MenuItem = 403, MenuScrollingVisibilityConverter = 404, MeshGeometry3D = 405, Model3D = 406, Model3DCollection = 407, Model3DGroup = 408, ModelVisual3D = 409, ModifierKeysConverter = 410, MouseActionConverter = 411, MouseBinding = 412, MouseDevice = 413, MouseGesture = 414, MouseGestureConverter = 415, MultiBinding = 416, MultiBindingExpression = 417, MultiDataTrigger = 418, MultiTrigger = 419, NameScope = 420, NavigationWindow = 421, NullExtension = 422, NullableBoolConverter = 423, NullableConverter = 424, NumberSubstitution = 425, Object = 426, ObjectAnimationBase = 427, ObjectAnimationUsingKeyFrames = 428, ObjectDataProvider = 429, ObjectKeyFrame = 430, ObjectKeyFrameCollection = 431, OrthographicCamera = 432, OuterGlowBitmapEffect = 433, Page = 434, PageContent = 435, PageFunctionBase = 436, Panel = 437, Paragraph = 438, ParallelTimeline = 439, ParserContext = 440, PasswordBox = 441, Path = 442, PathFigure = 443, PathFigureCollection = 444, PathFigureCollectionConverter = 445, PathGeometry = 446, PathSegment = 447, PathSegmentCollection = 448, PauseStoryboard = 449, Pen = 450, PerspectiveCamera = 451, PixelFormat = 452, PixelFormatConverter = 453, PngBitmapDecoder = 454, PngBitmapEncoder = 455, Point = 456, Point3D = 457, Point3DAnimation = 458, Point3DAnimationBase = 459, Point3DAnimationUsingKeyFrames = 460, Point3DCollection = 461, Point3DCollectionConverter = 462, Point3DConverter = 463, Point3DKeyFrame = 464, Point3DKeyFrameCollection = 465, Point4D = 466, Point4DConverter = 467, PointAnimation = 468, PointAnimationBase = 469, PointAnimationUsingKeyFrames = 470, PointAnimationUsingPath = 471, PointCollection = 472, PointCollectionConverter = 473, PointConverter = 474, PointIListConverter = 475, PointKeyFrame = 476, PointKeyFrameCollection = 477, PointLight = 478, PointLightBase = 479, PolyBezierSegment = 480, PolyLineSegment = 481, PolyQuadraticBezierSegment = 482, Polygon = 483, Polyline = 484, Popup = 485, PresentationSource = 486, PriorityBinding = 487, PriorityBindingExpression = 488, ProgressBar = 489, ProjectionCamera = 490, PropertyPath = 491, PropertyPathConverter = 492, QuadraticBezierSegment = 493, Quaternion = 494, QuaternionAnimation = 495, QuaternionAnimationBase = 496, QuaternionAnimationUsingKeyFrames = 497, QuaternionConverter = 498, QuaternionKeyFrame = 499, QuaternionKeyFrameCollection = 500, QuaternionRotation3D = 501, RadialGradientBrush = 502, RadioButton = 503, RangeBase = 504, Rect = 505, Rect3D = 506, Rect3DConverter = 507, RectAnimation = 508, RectAnimationBase = 509, RectAnimationUsingKeyFrames = 510, RectConverter = 511, RectKeyFrame = 512, RectKeyFrameCollection = 513, Rectangle = 514, RectangleGeometry = 515, RelativeSource = 516, RemoveStoryboard = 517, RenderOptions = 518, RenderTargetBitmap = 519, RepeatBehavior = 520, RepeatBehaviorConverter = 521, RepeatButton = 522, ResizeGrip = 523, ResourceDictionary = 524, ResourceKey = 525, ResumeStoryboard = 526, RichTextBox = 527, RotateTransform = 528, RotateTransform3D = 529, Rotation3D = 530, Rotation3DAnimation = 531, Rotation3DAnimationBase = 532, Rotation3DAnimationUsingKeyFrames = 533, Rotation3DKeyFrame = 534, Rotation3DKeyFrameCollection = 535, RoutedCommand = 536, RoutedEvent = 537, RoutedEventConverter = 538, RoutedUICommand = 539, RoutingStrategy = 540, RowDefinition = 541, Run = 542, RuntimeNamePropertyAttribute = 543, SByte = 544, SByteConverter = 545, ScaleTransform = 546, ScaleTransform3D = 547, ScrollBar = 548, ScrollContentPresenter = 549, ScrollViewer = 550, Section = 551, SeekStoryboard = 552, Selector = 553, Separator = 554, SetStoryboardSpeedRatio = 555, Setter = 556, SetterBase = 557, Shape = 558, Single = 559, SingleAnimation = 560, SingleAnimationBase = 561, SingleAnimationUsingKeyFrames = 562, SingleConverter = 563, SingleKeyFrame = 564, SingleKeyFrameCollection = 565, Size = 566, Size3D = 567, Size3DConverter = 568, SizeAnimation = 569, SizeAnimationBase = 570, SizeAnimationUsingKeyFrames = 571, SizeConverter = 572, SizeKeyFrame = 573, SizeKeyFrameCollection = 574, SkewTransform = 575, SkipStoryboardToFill = 576, Slider = 577, SolidColorBrush = 578, SoundPlayerAction = 579, Span = 580, SpecularMaterial = 581, SpellCheck = 582, SplineByteKeyFrame = 583, SplineColorKeyFrame = 584, SplineDecimalKeyFrame = 585, SplineDoubleKeyFrame = 586, SplineInt16KeyFrame = 587, SplineInt32KeyFrame = 588, SplineInt64KeyFrame = 589, SplinePoint3DKeyFrame = 590, SplinePointKeyFrame = 591, SplineQuaternionKeyFrame = 592, SplineRectKeyFrame = 593, SplineRotation3DKeyFrame = 594, SplineSingleKeyFrame = 595, SplineSizeKeyFrame = 596, SplineThicknessKeyFrame = 597, SplineVector3DKeyFrame = 598, SplineVectorKeyFrame = 599, SpotLight = 600, StackPanel = 601, StaticExtension = 602, StaticResourceExtension = 603, StatusBar = 604, StatusBarItem = 605, StickyNoteControl = 606, StopStoryboard = 607, Storyboard = 608, StreamGeometry = 609, StreamGeometryContext = 610, StreamResourceInfo = 611, String = 612, StringAnimationBase = 613, StringAnimationUsingKeyFrames = 614, StringConverter = 615, StringKeyFrame = 616, StringKeyFrameCollection = 617, StrokeCollection = 618, StrokeCollectionConverter = 619, Style = 620, Stylus = 621, StylusDevice = 622, TabControl = 623, TabItem = 624, TabPanel = 625, Table = 626, TableCell = 627, TableColumn = 628, TableRow = 629, TableRowGroup = 630, TabletDevice = 631, TemplateBindingExpression = 632, TemplateBindingExpressionConverter = 633, TemplateBindingExtension = 634, TemplateBindingExtensionConverter = 635, TemplateKey = 636, TemplateKeyConverter = 637, TextBlock = 638, TextBox = 639, TextBoxBase = 640, TextComposition = 641, TextCompositionManager = 642, TextDecoration = 643, TextDecorationCollection = 644, TextDecorationCollectionConverter = 645, TextEffect = 646, TextEffectCollection = 647, TextElement = 648, TextSearch = 649, ThemeDictionaryExtension = 650, Thickness = 651, ThicknessAnimation = 652, ThicknessAnimationBase = 653, ThicknessAnimationUsingKeyFrames = 654, ThicknessConverter = 655, ThicknessKeyFrame = 656, ThicknessKeyFrameCollection = 657, Thumb = 658, TickBar = 659, TiffBitmapDecoder = 660, TiffBitmapEncoder = 661, TileBrush = 662, TimeSpan = 663, TimeSpanConverter = 664, Timeline = 665, TimelineCollection = 666, TimelineGroup = 667, ToggleButton = 668, ToolBar = 669, ToolBarOverflowPanel = 670, ToolBarPanel = 671, ToolBarTray = 672, ToolTip = 673, ToolTipService = 674, Track = 675, Transform = 676, Transform3D = 677, Transform3DCollection = 678, Transform3DGroup = 679, TransformCollection = 680, TransformConverter = 681, TransformGroup = 682, TransformedBitmap = 683, TranslateTransform = 684, TranslateTransform3D = 685, TreeView = 686, TreeViewItem = 687, Trigger = 688, TriggerAction = 689, TriggerBase = 690, TypeExtension = 691, TypeTypeConverter = 692, Typography = 693, UIElement = 694, UInt16 = 695, UInt16Converter = 696, UInt32 = 697, UInt32Converter = 698, UInt64 = 699, UInt64Converter = 700, UShortIListConverter = 701, Underline = 702, UniformGrid = 703, Uri = 704, UriTypeConverter = 705, UserControl = 706, Validation = 707, Vector = 708, Vector3D = 709, Vector3DAnimation = 710, Vector3DAnimationBase = 711, Vector3DAnimationUsingKeyFrames = 712, Vector3DCollection = 713, Vector3DCollectionConverter = 714, Vector3DConverter = 715, Vector3DKeyFrame = 716, Vector3DKeyFrameCollection = 717, VectorAnimation = 718, VectorAnimationBase = 719, VectorAnimationUsingKeyFrames = 720, VectorCollection = 721, VectorCollectionConverter = 722, VectorConverter = 723, VectorKeyFrame = 724, VectorKeyFrameCollection = 725, VideoDrawing = 726, ViewBase = 727, Viewbox = 728, Viewport3D = 729, Viewport3DVisual = 730, VirtualizingPanel = 731, VirtualizingStackPanel = 732, Visual = 733, Visual3D = 734, VisualBrush = 735, VisualTarget = 736, WeakEventManager = 737, WhitespaceSignificantCollectionAttribute = 738, Window = 739, WmpBitmapDecoder = 740, WmpBitmapEncoder = 741, WrapPanel = 742, WriteableBitmap = 743, XamlBrushSerializer = 744, XamlInt32CollectionSerializer = 745, XamlPathDataSerializer = 746, XamlPoint3DCollectionSerializer = 747, XamlPointCollectionSerializer = 748, XamlReader = 749, XamlStyleSerializer = 750, XamlTemplateSerializer = 751, XamlVector3DCollectionSerializer = 752, XamlWriter = 753, XmlDataProvider = 754, XmlLangPropertyAttribute = 755, XmlLanguage = 756, XmlLanguageConverter = 757, XmlNamespaceMapping = 758, ZoomPercentageConverter = 759, } } ================================================ FILE: ICSharpCode.BamlDecompiler/BamlConnectionId.cs ================================================ /* 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 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler { /// /// Represents a field assignment of a XAML code-behind class. /// internal sealed class FieldAssignment { public IField Field; } /// /// Represents an event registration of a XAML code-behind class. /// internal sealed class EventRegistration { public string EventName, MethodName; } internal class BamlConnectionId { public uint Id { get; } public BamlConnectionId(uint id) => Id = id; } } ================================================ FILE: ICSharpCode.BamlDecompiler/BamlDecompilationResult.cs ================================================ // Copyright (c) 2021 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.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler { public class BamlDecompilationResult { public XDocument Xaml { get; } public List AssemblyReferences { get; } public FullTypeName? TypeName { get; } public List GeneratedMembers { get; } public BamlDecompilationResult(XDocument xaml, FullTypeName? typeName, IEnumerable assemblyReferences, IEnumerable generatedMembers) { this.Xaml = xaml; this.TypeName = typeName; this.AssemblyReferences = assemblyReferences.ToList(); this.GeneratedMembers = generatedMembers.ToList(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/BamlDecompilerSettings.cs ================================================ // Copyright (c) 2021 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.ComponentModel; using System.Runtime.CompilerServices; namespace ICSharpCode.BamlDecompiler { public class BamlDecompilerSettings : INotifyPropertyChanged { bool throwOnAssemblyResolveErrors = true; [Browsable(false)] public bool ThrowOnAssemblyResolveErrors { get { return throwOnAssemblyResolveErrors; } set { if (throwOnAssemblyResolveErrors != value) { throwOnAssemblyResolveErrors = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } } ================================================ FILE: ICSharpCode.BamlDecompiler/BamlDecompilerTypeSystem.cs ================================================ // Copyright (c) 2021 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; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.BamlDecompiler { public class BamlDecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem { string[] defaultBamlReferences = new[] { "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationUI, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" }; public BamlDecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver) { if (mainModule == null) throw new ArgumentNullException(nameof(mainModule)); if (assemblyResolver == null) throw new ArgumentNullException(nameof(assemblyResolver)); // Load referenced assemblies and type-forwarder references. // This is necessary to make .NET Core/PCL binaries work better. var referencedAssemblies = new List(); var assemblyReferenceQueue = new Queue<(bool IsAssembly, MetadataFile MainModule, object Reference)>(); var mainMetadata = mainModule.Metadata; foreach (var h in mainMetadata.GetModuleReferences()) { var moduleRef = mainMetadata.GetModuleReference(h); var moduleName = mainMetadata.GetString(moduleRef.Name); foreach (var fileHandle in mainMetadata.AssemblyFiles) { var file = mainMetadata.GetAssemblyFile(fileHandle); if (mainMetadata.StringComparer.Equals(file.Name, moduleName) && file.ContainsMetadata) { assemblyReferenceQueue.Enqueue((false, mainModule, moduleName)); break; } } } foreach (var refs in mainModule.AssemblyReferences) { assemblyReferenceQueue.Enqueue((true, mainModule, refs)); } foreach (var bamlReference in defaultBamlReferences) { assemblyReferenceQueue.Enqueue((true, mainModule, AssemblyNameReference.Parse(bamlReference))); } var comparer = KeyComparer.Create(((bool IsAssembly, MetadataFile MainModule, object Reference) reference) => reference.IsAssembly ? "A:" + ((IAssemblyReference)reference.Reference).FullName : "M:" + reference.Reference); var processedAssemblyReferences = new HashSet<(bool IsAssembly, MetadataFile Parent, object Reference)>(comparer); while (assemblyReferenceQueue.Count > 0) { var asmRef = assemblyReferenceQueue.Dequeue(); if (!processedAssemblyReferences.Add(asmRef)) continue; MetadataFile asm; if (asmRef.IsAssembly) { asm = assemblyResolver.Resolve((IAssemblyReference)asmRef.Reference); } else { asm = assemblyResolver.ResolveModule(asmRef.MainModule, (string)asmRef.Reference); } if (asm != null) { referencedAssemblies.Add(asm); var metadata = asm.Metadata; foreach (var h in metadata.ExportedTypes) { var exportedType = metadata.GetExportedType(h); switch (exportedType.Implementation.Kind) { case HandleKind.AssemblyReference: assemblyReferenceQueue.Enqueue((true, asm, new ICSharpCode.Decompiler.Metadata.AssemblyReference(asm, (AssemblyReferenceHandle)exportedType.Implementation))); break; case HandleKind.AssemblyFile: var file = metadata.GetAssemblyFile((AssemblyFileHandle)exportedType.Implementation); assemblyReferenceQueue.Enqueue((false, asm, metadata.GetString(file.Name))); break; } } } } var mainModuleWithOptions = mainModule.WithOptions(TypeSystemOptions.Default); var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(TypeSystemOptions.Default)); // Primitive types are necessary to avoid assertions in ILReader. // Fallback to MinimalCorlib to provide the primitive types. if (!HasType(KnownTypeCode.Void) || !HasType(KnownTypeCode.Int32)) { Init(mainModule.WithOptions(TypeSystemOptions.Default), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.Instance })); } else { Init(mainModuleWithOptions, referencedAssembliesWithOptions); } this.MainModule = (MetadataModule)base.MainModule; bool HasType(KnownTypeCode code) { TopLevelTypeName name = KnownTypeReference.Get(code).TypeName; if (!mainModule.GetTypeDefinition(name).IsNil) return true; foreach (var file in referencedAssemblies) { if (!file.GetTypeDefinition(name).IsNil) return true; } return false; } } public new MetadataModule MainModule { get; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/BamlElement.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler { internal readonly struct XamlNode { XamlNode(XElement value) { Element = value; String = null; } XamlNode(string value) { Element = null; String = value; } public readonly XElement Element; public readonly string String; public static implicit operator XamlNode(XElement value) => new XamlNode(value); public static implicit operator XamlNode(string value) => new XamlNode(value); public static implicit operator XElement(XamlNode node) => node.Element; public static implicit operator string(XamlNode node) => node.String; } internal class BamlElement { public BamlNode Node { get; } public XamlNode Xaml { get; set; } public BamlElement Parent { get; set; } public IList Children { get; } public BamlElement(BamlNode node) { Node = node; Children = new List(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/ConstructorParametersHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class ConstructorParametersStartHandler : IHandler { public BamlRecordType Type => BamlRecordType.ConstructorParametersStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var doc = new BamlElement(node); doc.Xaml = new XElement(ctx.GetPseudoName("Ctor")); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/DocumentHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class DocumentHandler : IHandler { public BamlRecordType Type => BamlRecordType.DocumentStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var doc = new BamlElement(node); doc.Xaml = new XElement(ctx.GetPseudoName("Document")); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/ElementHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class ElementHandler : IHandler { public BamlRecordType Type => BamlRecordType.ElementStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ElementStartRecord)((BamlBlockNode)node).Header; var doc = new BamlElement(node); var elemType = ctx.ResolveType(record.TypeId); doc.Xaml = new XElement(elemType.ToXName(ctx)); doc.Xaml.Element.AddAnnotation(elemType); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); if (node.Annotation is XamlResourceKey key && key.KeyNode.Record != node.Record) { var handler = (IDeferHandler)HandlerMap.LookupHandler(key.KeyNode.Record.Type); var keyElem = handler.TranslateDefer(ctx, key.KeyNode, doc); doc.Children.Add(keyElem); keyElem.Parent = doc; } elemType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemType.ToXName(ctx); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class KeyElementStartHandler : ElementHandler, IHandler, IDeferHandler { BamlRecordType IHandler.Type => BamlRecordType.KeyElementStart; BamlElement IHandler.Translate(XamlContext ctx, BamlNode node, BamlElement parent) { XamlResourceKey.Create(node); return null; } public BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (KeyElementStartRecord)((BamlBlockNode)node).Header; var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); key.KeyElement = bamlElem; base.Translate(ctx, node, bamlElem); return bamlElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/PropertyArrayHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyArrayHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyArrayStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyArrayStartRecord)((BamlBlockNode)node).Header; var doc = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); doc.Xaml = new XElement(elemAttr.ToXName(ctx, null)); doc.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); elemAttr.DeclaringType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/PropertyComplexHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyComplexHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyComplexStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyComplexStartRecord)((BamlBlockNode)node).Header; var doc = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); doc.Xaml = new XElement(elemAttr.ToXName(ctx, null)); doc.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); elemAttr.DeclaringType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/PropertyDictionaryHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyDictionaryHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyDictionaryStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyDictionaryStartRecord)((BamlBlockNode)node).Header; var doc = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); doc.Xaml = new XElement(elemAttr.ToXName(ctx, null)); doc.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); elemAttr.DeclaringType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Blocks/PropertyListHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyListHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyListStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyListStartRecord)((BamlBlockNode)node).Header; var doc = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); doc.Xaml = new XElement(elemAttr.ToXName(ctx, null)); doc.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); elemAttr.DeclaringType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/AssemblyInfoHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class AssemblyInfoHandler : IHandler { public BamlRecordType Type => BamlRecordType.AssemblyInfo; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/AttributeInfoHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class AttributeInfoHandler : IHandler { public BamlRecordType Type => BamlRecordType.AttributeInfo; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/ConnectionIdHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class ConnectionIdHandler : IHandler { public BamlRecordType Type => BamlRecordType.ConnectionId; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ConnectionIdRecord)((BamlRecordNode)node).Record; parent.Xaml.Element.AddAnnotation(new BamlConnectionId(record.ConnectionId)); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class ConstructorParameterTypeHandler : IHandler { public BamlRecordType Type => BamlRecordType.ConstructorParameterType; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ConstructorParameterTypeRecord)((BamlRecordNode)node).Record; var elem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); elem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension var bamlElem = new BamlElement(node); bamlElem.Xaml = elem; parent.Xaml.Element.Add(elem); var type = ctx.ResolveType(record.TypeId); var typeName = ctx.ToString(parent.Xaml, type); elem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); return bamlElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/ContentPropertyHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class ContentPropertyHandler : IHandler { public BamlRecordType Type => BamlRecordType.ContentProperty; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ContentPropertyRecord)((BamlRecordNode)node).Record; // TODO: What to do here? return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class DefAttributeHandler : IHandler { public BamlRecordType Type => BamlRecordType.DefAttribute; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (DefAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); parent.Xaml.Element.Add(new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_Xaml), record.Value)); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class DefAttributeStringHandler : IHandler, IDeferHandler { public BamlRecordType Type => BamlRecordType.DefAttributeKeyString; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { XamlResourceKey.Create(node); return null; } public BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (DefAttributeKeyStringRecord)((BamlRecordNode)node).Record; var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); bamlElem.Xaml.Element.Value = ctx.ResolveString(record.ValueId); key.KeyElement = bamlElem; return bamlElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class DefAttributeTypeHandler : IHandler, IDeferHandler { public BamlRecordType Type => BamlRecordType.DefAttributeKeyType; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { XamlResourceKey.Create(node); return null; } public BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (DefAttributeKeyTypeRecord)((BamlRecordNode)node).Record; var type = ctx.ResolveType(record.TypeId); var typeName = ctx.ToString(parent.Xaml, type); var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); bamlElem.Xaml.Element.Add(typeElem); key.KeyElement = bamlElem; return bamlElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/DeferableContentStartHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Diagnostics; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class DeferableContentStartHandler : IHandler { public BamlRecordType Type => BamlRecordType.DeferableContentStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (DeferableContentStartRecord)((BamlRecordNode)node).Record; Debug.Assert(record.Record == ((BamlBlockNode)parent.Node).Footer); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/LineNumberAndPositionHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class LineNumberAndPositionHandler : IHandler { public BamlRecordType Type => BamlRecordType.LineNumberAndPosition; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/LinePositionHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class LinePositionHandler : IHandler { public BamlRecordType Type => BamlRecordType.LinePosition; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class LiteralContentHandler : IHandler { public BamlRecordType Type => BamlRecordType.LiteralContent; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (LiteralContentRecord)((BamlRecordNode)node).Record; var elem = new XElement(ctx.GetKnownNamespace("XData", XamlContext.KnownNamespace_Xaml, parent.Xaml)); var content = XElement.Parse(record.Value); elem.Add(content); parent.Xaml.Element.Add(elem); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class OptimizedStaticResourceHandler : IHandler, IDeferHandler { public BamlRecordType Type => BamlRecordType.OptimizedStaticResource; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (OptimizedStaticResourceRecord)((BamlRecordNode)node).Record; var key = XamlResourceKey.FindKeyInSiblings(node); key.StaticResources.Add(node); return null; } public BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (OptimizedStaticResourceRecord)((BamlRecordNode)node).Record; var bamlElem = new BamlElement(node); object key; if (record.IsType) { var value = ctx.ResolveType(record.ValueId); var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), ctx.ToString(parent.Xaml, value))); key = typeElem; } else if (record.IsStatic) { string attrName; if (record.ValueId > 0x7fff) { bool isKey = true; short bamlId = unchecked((short)-record.ValueId); if (bamlId > 232 && bamlId < 464) { bamlId -= 232; isKey = false; } else if (bamlId > 464 && bamlId < 467) { bamlId -= 231; } else if (bamlId > 467 && bamlId < 470) { bamlId -= 234; isKey = false; } var res = ctx.Baml.KnownThings.Resources(bamlId); string name; if (isKey) name = res.Item1 + "." + res.Item2; else name = res.Item1 + "." + res.Item3; var xmlns = ctx.GetXmlNamespace(XamlContext.KnownNamespace_Presentation); attrName = ctx.ToString(parent.Xaml, xmlns.GetName(name)); } else { var value = ctx.ResolveProperty(record.ValueId); value.DeclaringType.ResolveNamespace(parent.Xaml, ctx); var xName = value.ToXName(ctx, parent.Xaml); attrName = ctx.ToString(parent.Xaml, xName); } var staticElem = new XElement(ctx.GetKnownNamespace("StaticExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); staticElem.AddAnnotation(ctx.ResolveType(0xfda6)); // Known type - StaticExtension staticElem.Add(new XElement(ctx.GetPseudoName("Ctor"), attrName)); key = staticElem; } else key = ctx.ResolveString(record.ValueId); var extType = ctx.ResolveType(0xfda5); var resElem = new XElement(extType.ToXName(ctx)); resElem.AddAnnotation(extType); // Known type - StaticResourceExtension bamlElem.Xaml = resElem; parent.Xaml.Element.Add(resElem); var attrElem = new XElement(ctx.GetPseudoName("Ctor")); attrElem.Add(key); resElem.Add(attrElem); return bamlElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PIMappingHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PIMappingHandler : IHandler { public BamlRecordType Type => BamlRecordType.PIMapping; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PresentationOptionsAttributeHandler : IHandler { public BamlRecordType Type => BamlRecordType.PresentationOptionsAttribute; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PresentationOptionsAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); var attr = new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_PresentationOptions, parent.Xaml), record.Value); parent.Xaml.Element.Add(attr); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyCustomHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyCustom; enum IntegerCollectionType : byte { Unknown, Consecutive, U1, U2, I4 } string Deserialize(XamlContext ctx, XElement elem, KnownTypes ser, byte[] value) { using (BinaryReader reader = new BinaryReader(new MemoryStream(value))) { switch (ser) { case KnownTypes.DependencyPropertyConverter: { if (value.Length == 2) { var property = ctx.ResolveProperty(reader.ReadUInt16()); return ctx.ToString(elem, property.ToXName(ctx, elem, NeedsFullName(property, ctx, elem))); } else { var type = ctx.ResolveType(reader.ReadUInt16()); var name = reader.ReadString(); var typeName = ctx.ToString(elem, type); return typeName + "." + name; } } case KnownTypes.EnumConverter: { uint enumVal = reader.ReadUInt32(); // TODO: Convert to enum names return enumVal.ToString("D", CultureInfo.InvariantCulture); } case KnownTypes.BooleanConverter: { Debug.Assert(value.Length == 1); return (reader.ReadByte() == 1).ToString(CultureInfo.InvariantCulture); } case KnownTypes.XamlBrushSerializer: { switch (reader.ReadByte()) { case 1: // KnownSolidColor return string.Format(CultureInfo.InvariantCulture, "#{0:X8}", reader.ReadUInt32()); case 2: // OtherColor return reader.ReadString(); } break; } case KnownTypes.XamlPathDataSerializer: return XamlPathDeserializer.Deserialize(reader); case KnownTypes.XamlPoint3DCollectionSerializer: case KnownTypes.XamlVector3DCollectionSerializer: { var sb = new StringBuilder(); var count = reader.ReadUInt32(); for (uint i = 0; i < count; i++) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0:R},{1:R},{2:R} ", reader.ReadXamlDouble(), reader.ReadXamlDouble(), reader.ReadXamlDouble()); } return sb.ToString().Trim(); } case KnownTypes.XamlPointCollectionSerializer: { var sb = new StringBuilder(); var count = reader.ReadUInt32(); for (uint i = 0; i < count; i++) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0:R},{1:R} ", reader.ReadXamlDouble(), reader.ReadXamlDouble()); } return sb.ToString().Trim(); } case KnownTypes.XamlInt32CollectionSerializer: { var sb = new StringBuilder(); var type = (IntegerCollectionType)reader.ReadByte(); var count = reader.ReadInt32(); switch (type) { case IntegerCollectionType.Consecutive: { var start = reader.ReadInt32(); for (int i = 0; i < count; i++) sb.AppendFormat(CultureInfo.InvariantCulture, "{0:D}", start + i); } break; case IntegerCollectionType.U1: { for (int i = 0; i < count; i++) sb.AppendFormat(CultureInfo.InvariantCulture, "{0:D}", reader.ReadByte()); } break; case IntegerCollectionType.U2: { for (int i = 0; i < count; i++) sb.AppendFormat(CultureInfo.InvariantCulture, "{0:D}", reader.ReadUInt16()); } break; case IntegerCollectionType.I4: { for (int i = 0; i < count; i++) sb.AppendFormat(CultureInfo.InvariantCulture, "{0:D}", reader.ReadInt32()); } break; default: throw new NotSupportedException(type.ToString()); } return sb.ToString().Trim(); } } } throw new NotSupportedException(ser.ToString()); } private bool NeedsFullName(XamlProperty property, XamlContext ctx, XElement elem) { XElement p = elem.Parent; while (p != null && p.Annotation()?.ResolvedType.FullName != "System.Windows.Style") { p = p.Parent; } var type = p?.Annotation()?.Type; if (type == null) return true; return property.IsAttachedTo(type); } public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyCustomRecord)((BamlRecordNode)node).Record; var serTypeId = ((short)record.SerializerTypeId & 0xfff); bool valueType = ((short)record.SerializerTypeId & 0x4000) == 0x4000; var elemType = parent.Xaml.Element.Annotation(); var xamlProp = ctx.ResolveProperty(record.AttributeId); string value = Deserialize(ctx, parent.Xaml, (KnownTypes)serTypeId, record.Data); var attr = new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, xamlProp.IsAttachedTo(elemType)), value); parent.Xaml.Element.Add(attr); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyHandler : IHandler { public virtual BamlRecordType Type => BamlRecordType.Property; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyRecord)((BamlRecordNode)node).Record; var elemType = parent.Xaml.Element.Annotation(); var xamlProp = ctx.ResolveProperty(record.AttributeId); var value = XamlUtils.Escape(record.Value); xamlProp.DeclaringType.ResolveNamespace(parent.Xaml, ctx); parent.Xaml.Element.Add(ConstructXAttribute()); return null; XAttribute ConstructXAttribute() { if (xamlProp.IsAttachedTo(elemType)) return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, true), value); if (xamlProp.PropertyName == "Name" && elemType.ResolvedType.GetDefinition()?.ParentModule.IsMainModule == true) return new XAttribute(ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml), value); return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, false), value); } } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyTypeReferenceHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyTypeReference; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyTypeReferenceRecord)((BamlRecordNode)node).Record; var attr = ctx.ResolveProperty(record.AttributeId); var type = ctx.ResolveType(record.TypeId); var typeName = ctx.ToString(parent.Xaml, type); var elem = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); elem.Xaml = new XElement(elemAttr.ToXName(ctx, null)); if (attr.ResolvedMember?.FullNameIs("System.Windows.Style", "TargetType") == true) { parent.Xaml.Element.AddAnnotation(new TargetTypeAnnotation(type)); } elem.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(elem.Xaml.Element); var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); elem.Xaml.Element.Add(typeElem); elemAttr.DeclaringType.ResolveNamespace(elem.Xaml, ctx); elem.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return elem; } } internal class TargetTypeAnnotation { public XamlType Type { get; } public TargetTypeAnnotation(XamlType type) { this.Type = type; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyWithConverterHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyWithConverterHandler : PropertyHandler, IHandler { BamlRecordType IHandler.Type => BamlRecordType.PropertyWithConverter; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyWithExtensionHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyWithExtensionHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyWithExtension; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyWithExtensionRecord)((BamlRecordNode)node).Record; var extTypeId = ((short)record.Flags & 0xfff); bool valTypeExt = ((short)record.Flags & 0x4000) == 0x4000; bool valStaticExt = ((short)record.Flags & 0x2000) == 0x2000; var elemType = parent.Xaml.Element.Annotation(); var xamlProp = ctx.ResolveProperty(record.AttributeId); var extType = ctx.ResolveType(unchecked((ushort)-extTypeId)); extType.ResolveNamespace(parent.Xaml, ctx); var ext = new XamlExtension(extType); if (valTypeExt || extTypeId == (short)KnownTypes.TypeExtension) { var value = ctx.ResolveType(record.ValueId); object[] initializer = new object[] { ctx.ToString(parent.Xaml, value) }; if (valTypeExt) initializer = new object[] { new XamlExtension(ctx.ResolveType(0xfd4d)) { Initializer = initializer } }; // Known type - TypeExtension ext.Initializer = initializer; } else if (extTypeId == (short)KnownTypes.TemplateBindingExtension) { var value = ctx.ResolveProperty(record.ValueId); value.DeclaringType.ResolveNamespace(parent.Xaml, ctx); var xName = value.ToXName(ctx, parent.Xaml, true); ext.Initializer = new object[] { ctx.ToString(parent.Xaml, xName) }; } else if (valStaticExt || extTypeId == (short)KnownTypes.StaticExtension) { string attrName; if (record.ValueId > 0x7fff) { bool isKey = true; short bamlId = unchecked((short)-record.ValueId); if (bamlId > 232 && bamlId < 464) { bamlId -= 232; isKey = false; } else if (bamlId > 464 && bamlId < 467) { bamlId -= 231; } else if (bamlId > 467 && bamlId < 470) { bamlId -= 234; isKey = false; } var res = ctx.Baml.KnownThings.Resources(bamlId); string name; if (isKey) name = res.Item1 + "." + res.Item2; else name = res.Item1 + "." + res.Item3; var xmlns = ctx.GetXmlNamespace(XamlContext.KnownNamespace_Presentation); attrName = ctx.ToString(parent.Xaml, xmlns.GetName(name)); } else { var value = ctx.ResolveProperty(record.ValueId); value.DeclaringType.ResolveNamespace(parent.Xaml, ctx); var xName = value.ToXName(ctx, parent.Xaml); attrName = ctx.ToString(parent.Xaml, xName); } object[] initializer = new object[] { attrName }; if (valStaticExt) initializer = new object[] { new XamlExtension(ctx.ResolveType(0xfda6)) { Initializer = initializer } }; // Known type - StaticExtension ext.Initializer = initializer; } else { ext.Initializer = new object[] { XamlUtils.Escape(ctx.ResolveString(record.ValueId)) }; } var extValue = ext.ToString(ctx, parent.Xaml); var attr = new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, xamlProp.IsAttachedTo(elemType)), extValue); parent.Xaml.Element.Add(attr); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/PropertyWithStaticResourceIdHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class PropertyWithStaticResourceIdHandler : IHandler { public BamlRecordType Type => BamlRecordType.PropertyWithStaticResourceId; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyWithStaticResourceIdRecord)((BamlRecordNode)node).Record; var doc = new BamlElement(node); var elemAttr = ctx.ResolveProperty(record.AttributeId); doc.Xaml = new XElement(elemAttr.ToXName(ctx, null)); doc.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(doc.Xaml.Element); BamlNode found = node; XamlResourceKey key; do { key = XamlResourceKey.FindKeyInAncestors(found.Parent, out found); } while (key != null && record.StaticResourceId >= key.StaticResources.Count); if (key == null) throw new Exception("Cannot find StaticResource @" + node.Record.Position); var resNode = key.StaticResources[record.StaticResourceId]; var handler = (IDeferHandler)HandlerMap.LookupHandler(resNode.Type); var resElem = handler.TranslateDefer(ctx, resNode, doc); doc.Children.Add(resElem); resElem.Parent = doc; elemAttr.DeclaringType.ResolveNamespace(doc.Xaml, ctx); doc.Xaml.Element.Name = elemAttr.ToXName(ctx, null); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/StaticResourceIdHandler.cs ================================================ // 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 // 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 ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { class StaticResourceIdHandler : IHandler { public BamlRecordType Type => BamlRecordType.StaticResourceId; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (StaticResourceIdRecord)((BamlRecordNode)node).Record; BamlNode found = node; XamlResourceKey key; do { key = XamlResourceKey.FindKeyInAncestors(found.Parent, out found); } while (key != null && record.StaticResourceId >= key.StaticResources.Count); if (key == null) throw new Exception("Cannot find StaticResource @" + node.Record.Position); var resNode = key.StaticResources[record.StaticResourceId]; var handler = (IDeferHandler)HandlerMap.LookupHandler(resNode.Type); var resElem = handler.TranslateDefer(ctx, resNode, parent); parent.Children.Add(resElem); resElem.Parent = parent; return resElem; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/StaticResourceStartHandler.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { class StaticResourceStartHandler : IHandler, IDeferHandler { public BamlRecordType Type => BamlRecordType.StaticResourceStart; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (StaticResourceStartRecord)((BamlBlockNode)node).Record; var key = XamlResourceKey.FindKeyInSiblings(node); key.StaticResources.Add(node); return null; } public BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (StaticResourceStartRecord)((BamlBlockNode)node).Record; var doc = new BamlElement(node); var elemType = ctx.ResolveType(record.TypeId); doc.Xaml = new XElement(elemType.ToXName(ctx)); doc.Xaml.Element.AddAnnotation(elemType); parent.Xaml.Element.Add(doc.Xaml.Element); HandlerMap.ProcessChildren(ctx, (BamlBlockNode)node, doc); return doc; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/TextHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class TextHandler : IHandler { public BamlRecordType Type => BamlRecordType.Text; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (TextRecord)((BamlRecordNode)node).Record; parent.Xaml.Element.Add(record.Value); return null; } } internal class TextWithIdHandler : IHandler { public BamlRecordType Type => BamlRecordType.TextWithId; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (TextWithIdRecord)((BamlRecordNode)node).Record; parent.Xaml.Element.Add(ctx.ResolveString(record.ValueId)); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/TextWithConverterHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class TextWithConverterHandler : TextHandler, IHandler { BamlRecordType IHandler.Type => BamlRecordType.TextWithConverter; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/TypeInfoHandler.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class TypeInfoHandler : IHandler { public BamlRecordType Type => BamlRecordType.TypeInfo; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } internal class TypeSerializerInfoHandler : IHandler { public BamlRecordType Type => BamlRecordType.TypeSerializerInfo; public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) => null; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Handlers/Records/XmlnsPropertyHandler.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Xml; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Handlers { internal class XmlnsPropertyHandler : IHandler { public BamlRecordType Type => BamlRecordType.XmlnsProperty; IEnumerable ResolveCLRNamespaces(IModule assembly, string ns) { foreach (var attr in assembly.GetAssemblyAttributes().Where(a => a.AttributeType.FullName == "System.Windows.Markup.XmlnsDefinitionAttribute")) { Debug.Assert(attr.FixedArguments.Length == 2); var xmlNs = attr.FixedArguments[0].Value; var clrNs = attr.FixedArguments[1].Value; Debug.Assert(xmlNs is string && clrNs is string); if ((string)xmlNs == ns) yield return (string)clrNs; } } public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (XmlnsPropertyRecord)((BamlRecordNode)node).Record; foreach (var asmId in record.AssemblyIds) { var assembly = ctx.Baml.ResolveAssembly(asmId); ctx.XmlNs.Add(new NamespaceMap(record.Prefix, assembly.FullAssemblyName, record.XmlNamespace)); if (assembly.Assembly?.IsMainModule == true) { foreach (var clrNs in ResolveCLRNamespaces(assembly.Assembly, record.XmlNamespace)) ctx.XmlNs.Add(new NamespaceMap(record.Prefix, assembly.FullAssemblyName, record.XmlNamespace, clrNs)); } } XName xmlnsDef; if (string.IsNullOrEmpty(record.Prefix)) xmlnsDef = "xmlns"; else xmlnsDef = XNamespace.Xmlns + XmlConvert.EncodeLocalName(record.Prefix); parent.Xaml.Element.Add(new XAttribute(xmlnsDef, ctx.GetXmlNamespace(record.XmlNamespace))); return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/ICSharpCode.BamlDecompiler.csproj ================================================  net10.0 True ..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk en-US False False False ICSharpCode.BamlDecompiler 10.0.0.0-noversion ILSpy BAML Decompiler ILSpy Contributors MIT https://github.com/icsharpcode/ILSpy/ Cross-Platform library for decompiling BAML. PackageReadme.md ic#code BamlDecompiler git https://github.com/icsharpcode/ILSpy.git ../ICSharpCode.Decompiler/DecompilerNuGetPackageIcon.png false Copyright 2024-$([System.DateTime]::Now.Year) AlphaSierraPapa C# Decompiler ILSpy true embedded true true true true true true ILSpyUpdateAssemblyInfo; $(GetPackageVersionDependsOn) all runtime; build; native; contentfiles; analyzers; buildtransitive ================================================ FILE: ICSharpCode.BamlDecompiler/IHandlers.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler { internal interface IHandler { BamlRecordType Type { get; } BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent); } internal interface IDeferHandler { BamlElement TranslateDefer(XamlContext ctx, BamlNode node, BamlElement parent); } internal static class HandlerMap { static readonly Dictionary handlers; static HandlerMap() { handlers = new Dictionary(); foreach (var type in typeof(IHandler).Assembly.GetTypes()) { if (typeof(IHandler).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { var handler = (IHandler)Activator.CreateInstance(type); handlers.Add(handler.Type, handler); } } } public static IHandler LookupHandler(BamlRecordType type) { #if DEBUG switch (type) { case BamlRecordType.AssemblyInfo: case BamlRecordType.TypeInfo: case BamlRecordType.AttributeInfo: case BamlRecordType.StringInfo: break; default: if (!handlers.ContainsKey(type)) throw new NotSupportedException(type.ToString()); break; } #endif return handlers.ContainsKey(type) ? handlers[type] : null; } public static void ProcessChildren(XamlContext ctx, BamlBlockNode node, BamlElement nodeElem) { ctx.XmlNs.PushScope(nodeElem); if (nodeElem.Xaml.Element != null) nodeElem.Xaml.Element.AddAnnotation(ctx.XmlNs.CurrentScope); foreach (var child in node.Children) { var handler = LookupHandler(child.Type); if (handler == null) { Debug.WriteLine("BAML Handler {0} not implemented.", child.Type); continue; } var elem = handler.Translate(ctx, (BamlNode)child, nodeElem); if (elem != null) { nodeElem.Children.Add(elem); elem.Parent = nodeElem; } ctx.CancellationToken.ThrowIfCancellationRequested(); } ctx.XmlNs.PopScope(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/IRewritePass.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml.Linq; namespace ICSharpCode.BamlDecompiler { internal interface IRewritePass { void Run(XamlContext ctx, XDocument document); } } ================================================ FILE: ICSharpCode.BamlDecompiler/PackageReadme.md ================================================ ## About ICSharpCode.BamlDecompiler is the library used by the BAML Addin in ILSpy to decompile BAML to XAML. ================================================ FILE: ICSharpCode.BamlDecompiler/Properties/AssemblyInfo.cs ================================================ #region Using directives using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; #endregion [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] [assembly: AssemblyVersion(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision)] [assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithCommitHash)] [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] ================================================ FILE: ICSharpCode.BamlDecompiler/Rewrite/AttributeRewritePass.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Rewrite { internal class AttributeRewritePass : IRewritePass { XName key; public void Run(XamlContext ctx, XDocument document) { key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); bool doWork; do { doWork = false; foreach (var elem in document.Elements()) { doWork |= ProcessElement(ctx, elem); } } while (doWork); } bool ProcessElement(XamlContext ctx, XElement elem) { bool doWork = false; foreach (var child in elem.Elements()) { doWork |= RewriteElement(ctx, elem, child); doWork |= ProcessElement(ctx, child); } return doWork; } bool RewriteElement(XamlContext ctx, XElement parent, XElement elem) { var property = elem.Annotation(); if (property == null && elem.Name != key) return false; if (elem.HasAttributes || elem.HasElements) return false; ctx.CancellationToken.ThrowIfCancellationRequested(); var value = elem.Value; var attrName = elem.Name; if (attrName != key) attrName = property.ToXName(ctx, parent, property.IsAttachedTo(parent.Annotation())); var attr = new XAttribute(attrName, value); var list = new List(parent.Attributes()); if (attrName == key) list.Insert(0, attr); else list.Add(attr); parent.RemoveAttributes(); parent.ReplaceAttributes(list); elem.Remove(); return true; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs ================================================ // 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 // 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; using System.Reflection.Metadata; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Xaml; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.BamlDecompiler.Rewrite { using ICSharpCode.Decompiler.TypeSystem; internal class ConnectionIdRewritePass : IRewritePass { static readonly TopLevelTypeName componentConnectorTypeName = new TopLevelTypeName("System.Windows.Markup", "IComponentConnector"); static readonly TopLevelTypeName styleConnectorTypeName = new TopLevelTypeName("System.Windows.Markup", "IStyleConnector"); public void Run(XamlContext ctx, XDocument document) { var connections = DecompileConnections(ctx, document); ProcessConnectionIds(ctx, document.Root, connections); } static void ProcessConnectionIds(XamlContext ctx, XElement element, (List<(LongSet key, FieldAssignment value)> fieldAssignments, List<(LongSet key, EventRegistration[] value)> eventMappings) connections) { foreach (var child in element.Elements()) ProcessConnectionIds(ctx, child, connections); var fieldAssignments = connections.fieldAssignments; var eventMappings = connections.eventMappings; foreach (var annotation in element.Annotations()) { int index; bool found = false; if ((index = fieldAssignments.FindIndex(item => item.key.Contains(annotation.Id))) > -1) { var xName = ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml, element); FieldAssignment fieldAssignment = fieldAssignments[index].value; if (element.Attribute("Name") is null && element.Attribute(xName) is null) { element.Add(new XAttribute(xName, fieldAssignment.Field.Name)); } // x:FieldModifier can only be "public" or "internal" (in C#), where "internal" is the default and thus omitted if (fieldAssignment.Field.Accessibility is Accessibility.Public) { element.Add(new XAttribute(ctx.GetKnownNamespace("FieldModifier", XamlContext.KnownNamespace_Xaml, element), "public")); } ctx.GeneratedMembers.Add(fieldAssignment.Field.MetadataToken); found = true; } if ((index = eventMappings.FindIndex(item => item.key.Contains(annotation.Id))) > -1) { foreach (var entry in eventMappings[index].value) { string xmlns = ""; // TODO : implement xmlns resolver! var type = element.Annotation(); if (type?.TypeNamespace + "." + type?.TypeName == "System.Windows.Style") { element.Add(new XElement(type.Namespace + "EventSetter", new XAttribute("Event", entry.EventName), new XAttribute("Handler", entry.MethodName))); } else { element.Add(new XAttribute(xmlns + entry.EventName, entry.MethodName)); } } found = true; } if (!found) { element.Add(new XComment($"Unknown connection ID: {annotation.Id}")); } } } (List<(LongSet, FieldAssignment)>, List<(LongSet, EventRegistration[])>) DecompileConnections (XamlContext ctx, XDocument document) { var fieldAssignments = new List<(LongSet key, FieldAssignment value)>(); var eventMappings = new List<(LongSet, EventRegistration[])>(); var xClass = document.Root .Elements().First() .Attribute(ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml)); if (xClass == null) return (fieldAssignments, eventMappings); var type = ctx.TypeSystem.FindType(new FullTypeName(xClass.Value)).GetDefinition(); if (type == null) return (fieldAssignments, eventMappings); DecompileConnections(ctx, fieldAssignments, eventMappings, componentConnectorTypeName, type); DecompileConnections(ctx, fieldAssignments, eventMappings, styleConnectorTypeName, type); return (fieldAssignments, eventMappings); } void DecompileConnections(XamlContext ctx, List<(LongSet, FieldAssignment)> fieldAssignments, List<(LongSet, EventRegistration[])> eventMappings, FullTypeName connectorTypeName, ITypeDefinition type) { var connectorInterface = ctx.TypeSystem.FindType(connectorTypeName).GetDefinition(); if (connectorInterface == null) return; var connect = connectorInterface.GetMethods(m => m.Name == "Connect").SingleOrDefault(); IMethod connectMethod = null; MethodDefinition connectMetadataEntry = default; var module = ctx.TypeSystem.MainModule.MetadataFile; foreach (IMethod m in type.Methods) { if (connectMethod == null && m.ExplicitlyImplementedInterfaceMembers.Any(md => md.MemberDefinition.Equals(connect))) { connectMethod = m; connectMetadataEntry = module.Metadata .GetMethodDefinition((MethodDefinitionHandle)connectMethod.MetadataToken); } else if (m.Parameters.Count == 0 && m.ReturnType.Kind == TypeKind.Void && !m.IsStatic && m.Accessibility == Accessibility.Public && m.Name == "InitializeComponent" && m.GetAttributes().Any(a => a.AttributeType.ReflectionName == "System.CodeDom.Compiler.GeneratedCodeAttribute")) { ctx.GeneratedMembers.Add(m.MetadataToken); } else if (m.Parameters.Count == 0 && m.ReturnType.Kind == TypeKind.Void && m.IsStatic && m.Accessibility == Accessibility.Public && m.Name == "Main" && m.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.ReflectionName == "System.Windows.Application") && m.GetAttributes().Any(a => a.AttributeType.ReflectionName == "System.CodeDom.Compiler.GeneratedCodeAttribute")) { ctx.GeneratedMembers.Add(m.MetadataToken); } } if (type.Fields.FirstOrDefault(f => f.Name == "_contentLoaded" && f.Type.IsKnownType(KnownTypeCode.Boolean)) is { Accessibility: Accessibility.Private, IsStatic: false } contentLoadedField) { ctx.GeneratedMembers.Add(contentLoadedField.MetadataToken); } if (connectMethod == null || connectMetadataEntry.RelativeVirtualAddress <= 0) return; ctx.GeneratedMembers.Add(connectMethod.MetadataToken); var body = module.GetMethodBody(connectMetadataEntry.RelativeVirtualAddress); var genericContext = new GenericContext( classTypeParameters: connectMethod.DeclaringType?.TypeParameters, methodTypeParameters: connectMethod.TypeParameters); // decompile method and optimize the switch var ilReader = new ILReader(ctx.TypeSystem.MainModule); var function = ilReader.ReadIL((MethodDefinitionHandle)connectMethod.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken); var context = new ILTransformContext(function, ctx.TypeSystem, null) { CancellationToken = ctx.CancellationToken }; function.RunTransforms(CSharpDecompiler.GetILTransforms(), context); var block = function.Body.Children.OfType().First(); var ilSwitch = block.Descendants.OfType().FirstOrDefault(); var events = new List(); if (ilSwitch != null) { foreach (var section in ilSwitch.Sections) { Add(section.Labels, section.Body); } } else { foreach (var ifInst in function.Descendants.OfType()) { if (!(ifInst.Condition is Comp comp)) continue; if (comp.Kind != ComparisonKind.Inequality && comp.Kind != ComparisonKind.Equality) continue; if (!comp.Right.MatchLdcI4(out int id)) continue; var inst = comp.Kind == ComparisonKind.Inequality ? ifInst.FalseInst : ifInst.TrueInst; Add(new LongSet(id), inst); } } void Add(LongSet ids, ILInstruction inst) { var field = FindField(inst); if (!(field is null)) { fieldAssignments.Add((ids, field)); } events.Clear(); FindEvents(inst, events); if (events.Count > 0) { eventMappings.Add((ids, events.ToArray())); } } } FieldAssignment FindField(ILInstruction inst) { switch (inst) { case Block b: var t = b.Instructions.FirstOrDefault(); if (!(t is null) && MatchFieldAssignment(t, out var field)) return field; return null; case Branch br: return FindField(br.TargetBlock); default: if (MatchFieldAssignment(inst, out field)) return field; return null; } } bool MatchFieldAssignment(ILInstruction inst, out FieldAssignment field) { field = null; if (!inst.MatchStFld(out _, out var fld, out var value) || !value.MatchCastClass(out var arg, out _) || !(arg.MatchLdLoc(out var t) && t.Kind == VariableKind.Parameter && t.Index == 1)) return false; field = new FieldAssignment { Field = fld }; return true; } void FindEvents(ILInstruction inst, List events) { EventRegistration @event; switch (inst) { case Block b: for (int i = 0; i < b.Instructions.Count;) { if (MatchEventSetterCreation(b, ref i, out @event)) events.Add(@event); else i++; } foreach (var node in b.Instructions) { if (MatchSimpleEventRegistration(node, out @event)) events.Add(@event); } break; case Branch br: FindEvents(br.TargetBlock, events); break; default: if (MatchSimpleEventRegistration(inst, out @event)) events.Add(@event); break; } } // stloc v(newobj EventSetter..ctor()) // callvirt set_Event(ldloc v, ldsfld eventName) // callvirt set_Handler(ldloc v, newobj RoutedEventHandler..ctor(ldloc this, ldftn eventHandler)) // callvirt Add(callvirt get_Setters(castclass System.Windows.Style(ldloc target)), ldloc v) bool MatchEventSetterCreation(Block b, ref int pos, out EventRegistration @event) { @event = null; if (!b.FinalInstruction.MatchNop()) { pos = b.Instructions.Count; return false; } var instr = b.Instructions; // stloc v(newobj EventSetter..ctor()) if (!instr[pos + 0].MatchStLoc(out var v, out var initializer)) return false; if (!(initializer is NewObj newObj && newObj.Method.DeclaringType.FullName == "System.Windows.EventSetter" && newObj.Arguments.Count == 0)) { return false; } //callvirt set_Event(ldloc v, ldsfld eventName) if (!(instr[pos + 1] is CallVirt setEventCall && setEventCall.Arguments.Count == 2)) return false; if (!setEventCall.Method.IsAccessor) return false; if (!setEventCall.Arguments[0].MatchLdLoc(v)) return false; if (setEventCall.Method.Name != "set_Event") return false; if (!setEventCall.Arguments[1].MatchLdsFld(out var eventField)) return false; string eventName = eventField.Name; if (eventName.EndsWith("Event")) { eventName = eventName.Remove(eventName.Length - "Event".Length); } // callvirt set_Handler(ldloc v, newobj RoutedEventHandler..ctor(ldloc this, ldftn eventHandler)) if (!(instr[pos + 2] is CallVirt setHandlerCall && setHandlerCall.Arguments.Count == 2)) return false; if (!setHandlerCall.Method.IsAccessor) return false; if (!setHandlerCall.Arguments[0].MatchLdLoc(v)) return false; if (setHandlerCall.Method.Name != "set_Handler") return false; if (!MatchEventHandlerCreation(setHandlerCall.Arguments[1], out string handlerName)) return false; @event = new EventRegistration { EventName = eventName, MethodName = handlerName }; // callvirt Add(callvirt get_Setters(castclass System.Windows.Style(ldloc target)), ldloc v) if (!(instr[pos + 3] is CallVirt addCall && addCall.Arguments.Count == 2)) return false; if (addCall.Method.Name != "Add") return false; if (!(addCall.Arguments[0] is CallVirt getSettersCall && getSettersCall.Arguments.Count == 1)) return false; if (!getSettersCall.Method.IsAccessor) return false; if (getSettersCall.Method.Name != "get_Setters") return false; if (!getSettersCall.Arguments[0].MatchCastClass(out var arg, out var type)) return false; if (type.FullName != "System.Windows.Style") return false; if (!(arg.MatchLdLoc(out var t) && t.Kind == VariableKind.Parameter && t.Index == 1)) return false; if (!addCall.Arguments[1].MatchLdLoc(v)) return false; pos += 4; return true; } bool MatchSimpleEventRegistration(ILInstruction inst, out EventRegistration @event) { @event = null; if (!(inst is CallInstruction call) || call.OpCode == OpCode.NewObj) return false; if (!IsAddEvent(call, out string eventName, out string handlerName) && !IsAddAttachedEvent(call, out eventName, out handlerName)) { return false; } @event = new EventRegistration { EventName = eventName, MethodName = handlerName }; return true; } bool IsAddAttachedEvent(CallInstruction call, out string eventName, out string handlerName) { eventName = ""; handlerName = ""; if (call.Arguments.Count == 3) { var addMethod = call.Method; if (addMethod.Name != "AddHandler" || addMethod.Parameters.Count != 2) return false; if (!call.Arguments[1].MatchLdsFld(out IField field)) return false; eventName = field.DeclaringType.Name + "." + field.Name; if (eventName.EndsWith("Event", StringComparison.Ordinal) && eventName.Length > "Event".Length) { eventName = eventName.Remove(eventName.Length - "Event".Length); } return MatchEventHandlerCreation(call.Arguments[2], out handlerName); } return false; } bool IsAddEvent(CallInstruction call, out string eventName, out string handlerName) { eventName = ""; handlerName = ""; if (call.Arguments.Count == 2) { var addMethod = call.Method; if (!addMethod.Name.StartsWith("add_", StringComparison.Ordinal) || addMethod.Parameters.Count != 1) { return false; } eventName = addMethod.Name.Substring("add_".Length); return MatchEventHandlerCreation(call.Arguments[1], out handlerName); } return false; } bool MatchEventHandlerCreation(ILInstruction inst, out string handlerName) { handlerName = ""; if (!(inst is NewObj newObj) || newObj.Arguments.Count != 2) return false; var ldftn = newObj.Arguments[1]; if (ldftn.OpCode != OpCode.LdFtn && ldftn.OpCode != OpCode.LdVirtFtn) return false; handlerName = ((IInstructionWithMethodOperand)ldftn).Method.Name; handlerName = XamlUtils.EscapeName(handlerName); return true; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Rewrite/DocumentRewritePass.cs ================================================ /* Copyright (c) 2015 Ki 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.Diagnostics; using System.Linq; using System.Xml.Linq; namespace ICSharpCode.BamlDecompiler.Rewrite { internal class DocumentRewritePass : IRewritePass { public void Run(XamlContext ctx, XDocument document) { foreach (var elem in document.Elements(ctx.GetPseudoName("Document")).ToList()) { if (elem.Elements().Count() != 1) continue; var docElem = elem.Elements().Single(); foreach (var attr in elem.Attributes()) { Debug.Assert(attr.IsNamespaceDeclaration); attr.Remove(); docElem.Add(attr); } elem.ReplaceWith(docElem); } } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Linq; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Rewrite { internal class MarkupExtensionRewritePass : IRewritePass { XName key; XName ctor; public void Run(XamlContext ctx, XDocument document) { key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); ctor = ctx.GetPseudoName("Ctor"); bool doWork; do { doWork = false; foreach (var elem in document.Elements()) { doWork |= ProcessElement(ctx, elem); } } while (doWork); } bool ProcessElement(XamlContext ctx, XElement elem) { bool doWork = false; foreach (var child in elem.Elements()) { doWork |= RewriteElement(ctx, elem, child); doWork |= ProcessElement(ctx, child); } return doWork; } bool RewriteElement(XamlContext ctx, XElement parent, XElement elem) { var type = parent.Annotation(); var property = elem.Annotation(); if (elem.Name != key) { if (property == null || type == null) return false; if (property.ResolvedMember is IProperty { CanSet: false }) return false; } if (elem.Elements().Count() != 1 || elem.Attributes().Any(t => t.Name.Namespace != XNamespace.Xmlns)) return false; var value = elem.Elements().Single(); if (!CanInlineExt(ctx, value)) return false; var ext = InlineExtension(ctx, value); if (ext == null) return false; ctx.CancellationToken.ThrowIfCancellationRequested(); var extValue = ext.ToString(ctx, parent); var attrName = elem.Name; if (attrName != key) attrName = property.ToXName(ctx, parent, property.IsAttachedTo(type)); if (!parent.Attributes(attrName).Any()) { var attr = new XAttribute(attrName, extValue); var list = new List(parent.Attributes()); if (attrName == key) list.Insert(0, attr); else list.Add(attr); parent.RemoveAttributes(); parent.ReplaceAttributes(list); } elem.Remove(); return true; } bool CanInlineExt(XamlContext ctx, XElement ctxElement) { var type = ctxElement.Annotation(); if (type != null && type.ResolvedType != null) { var typeDef = type.ResolvedType.GetDefinition()?.DirectBaseTypes.FirstOrDefault(); bool isExt = false; while (typeDef != null) { if (typeDef.FullName == "System.Windows.Markup.MarkupExtension") { isExt = true; break; } typeDef = typeDef.DirectBaseTypes.FirstOrDefault(); } if (!isExt) return false; } else if (ctxElement.Annotation() == null && ctxElement.Name != ctor) return false; foreach (var child in ctxElement.Elements()) { if (!CanInlineExt(ctx, child)) return false; } return true; } object InlineObject(XamlContext ctx, XNode obj) { if (obj is XText) return ((XText)obj).Value; else if (obj is XElement) return InlineExtension(ctx, (XElement)obj); else return null; } object[] InlineCtor(XamlContext ctx, XElement ctor) { if (ctor.HasAttributes) return null; var args = new List(); foreach (var child in ctor.Nodes()) { var arg = InlineObject(ctx, child); if (arg == null) return null; args.Add(arg); } return args.ToArray(); } XamlExtension InlineExtension(XamlContext ctx, XElement ctxElement) { var type = ctxElement.Annotation(); if (type == null) return null; var ext = new XamlExtension(type); foreach (var attr in ctxElement.Attributes().Where(attr => attr.Name.Namespace != XNamespace.Xmlns)) ext.NamedArguments[attr.Name.LocalName] = attr.Value; foreach (var child in ctxElement.Nodes()) { var elem = child as XElement; if (elem == null) return null; if (elem.Name == ctor) { if (ext.Initializer != null) return null; var args = InlineCtor(ctx, elem); if (args == null) return null; ext.Initializer = args; continue; } var property = elem.Annotation(); if (property == null || elem.Nodes().Count() != 1 || elem.Attributes().Any(attr => attr.Name.Namespace != XNamespace.Xmlns)) return null; var name = property.PropertyName; var value = InlineObject(ctx, elem.Nodes().Single()); ext.NamedArguments[name] = value; } return ext; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Rewrite/XClassRewritePass.cs ================================================ /* Copyright (c) 2015 Ki 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.Linq; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler.Rewrite { internal class XClassRewritePass : IRewritePass { public void Run(XamlContext ctx, XDocument document) { foreach (var elem in document.Elements(ctx.GetPseudoName("Document")).Elements()) RewriteClass(ctx, elem); } void RewriteClass(XamlContext ctx, XElement elem) { var type = elem.Annotation(); if (type == null || type.ResolvedType == null) return; var typeDef = type.ResolvedType.GetDefinition(); if (typeDef == null || !typeDef.ParentModule.IsMainModule) return; var newType = typeDef.DirectBaseTypes.First().GetDefinition(); if (newType == null) return; var xamlType = new XamlType(newType.ParentModule, newType.ParentModule.FullAssemblyName, newType.Namespace, newType.Name); xamlType.ResolveNamespace(elem, ctx); elem.Name = xamlType.ToXName(ctx); var attrName = ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml, elem); var attrs = elem.Attributes().ToList(); if (typeDef.Accessibility != ICSharpCode.Decompiler.TypeSystem.Accessibility.Public) { var classModifierName = ctx.GetKnownNamespace("ClassModifier", XamlContext.KnownNamespace_Xaml, elem); attrs.Insert(0, new XAttribute(classModifierName, "internal")); } attrs.Insert(0, new XAttribute(attrName, type.ResolvedType.FullName)); ctx.XClassNames.Add(type.ResolvedType.FullName); elem.ReplaceAttributes(attrs); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/NamespaceMap.cs ================================================ /* Copyright (c) 2015 Ki 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 ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler.Xaml { internal class NamespaceMap { public string XmlnsPrefix { get; set; } public string FullAssemblyName { get; set; } public string XMLNamespace { get; set; } public string CLRNamespace { get; set; } public NamespaceMap(string prefix, string fullAssemblyName, string xmlNs) : this(prefix, fullAssemblyName, xmlNs, null) { } public NamespaceMap(string prefix, string fullAssemblyName, string xmlNs, string clrNs) { XmlnsPrefix = prefix; FullAssemblyName = fullAssemblyName; XMLNamespace = xmlNs; CLRNamespace = clrNs; } public override string ToString() => $"{XmlnsPrefix}:[{FullAssemblyName}|{CLRNamespace ?? XMLNamespace}]"; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlExtension.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Text; using System.Xml.Linq; namespace ICSharpCode.BamlDecompiler.Xaml { internal class XamlExtension { public XamlType ExtensionType { get; } public object[] Initializer { get; set; } public IDictionary NamedArguments { get; } public XamlExtension(XamlType type) { ExtensionType = type; NamedArguments = new Dictionary(); } static void WriteObject(StringBuilder sb, XamlContext ctx, XElement ctxElement, object value) { if (value is XamlExtension) sb.Append(((XamlExtension)value).ToString(ctx, ctxElement)); else sb.Append(value.ToString()); } public string ToString(XamlContext ctx, XElement ctxElement) { var sb = new StringBuilder(); sb.Append('{'); var typeName = ctx.ToString(ctxElement, ExtensionType); if (typeName.EndsWith("Extension")) sb.Append(typeName.Substring(0, typeName.Length - 9)); else sb.Append(typeName); bool comma = false; if (Initializer != null && Initializer.Length > 0) { sb.Append(' '); for (int i = 0; i < Initializer.Length; i++) { if (comma) sb.Append(", "); WriteObject(sb, ctx, ctxElement, Initializer[i]); comma = true; } } if (NamedArguments.Count > 0) { foreach (var kvp in NamedArguments) { if (comma) sb.Append(", "); else { sb.Append(' '); comma = true; } sb.AppendFormat("{0}=", kvp.Key); WriteObject(sb, ctx, ctxElement, kvp.Value); } } sb.Append('}'); return sb.ToString(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlPathDeserializer.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Globalization; using System.IO; using System.Text; namespace ICSharpCode.BamlDecompiler.Xaml { class XamlPathDeserializer { enum PathOpCodes { BeginFigure, LineTo, QuadraticBezierTo, BezierTo, PolyLineTo, PolyQuadraticBezierTo, PolyBezierTo, ArcTo, Closed, FillRule } readonly struct Point { public readonly double X; public readonly double Y; public Point(double x, double y) { X = x; Y = y; } public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{0:R},{1:R}", X, Y); } static void UnpackBools(byte b, out bool b1, out bool b2, out bool b3, out bool b4) { b1 = (b & 0x10) != 0; b2 = (b & 0x20) != 0; b3 = (b & 0x40) != 0; b4 = (b & 0x80) != 0; } static Point ReadPoint(BinaryReader reader) => new Point(reader.ReadXamlDouble(), reader.ReadXamlDouble()); static void ReadPointBoolBool(BinaryReader reader, byte b, out Point pt, out bool b1, out bool b2) { UnpackBools(b, out b1, out b2, out bool sx, out bool sy); pt = new Point(reader.ReadXamlDouble(sx), reader.ReadXamlDouble(sy)); } static IList ReadPointsBoolBool(BinaryReader reader, byte b, out bool b1, out bool b2) { UnpackBools(b, out b1, out b2, out bool b3, out bool b4); var count = reader.ReadInt32(); var pts = new List(); for (int i = 0; i < count; i++) pts.Add(ReadPoint(reader)); return pts; } public static string Deserialize(BinaryReader reader) { bool end = false; var sb = new StringBuilder(); Point pt1, pt2, pt3; IList pts; while (!end) { var b = reader.ReadByte(); switch ((PathOpCodes)(b & 0xf)) { case PathOpCodes.BeginFigure: { ReadPointBoolBool(reader, b, out pt1, out bool filled, out bool closed); sb.AppendFormat("M{0} ", pt1); break; } case PathOpCodes.LineTo: { ReadPointBoolBool(reader, b, out pt1, out bool stroked, out bool smoothJoin); sb.AppendFormat("L{0} ", pt1); break; } case PathOpCodes.QuadraticBezierTo: { ReadPointBoolBool(reader, b, out pt1, out bool stroked, out bool smoothJoin); pt2 = ReadPoint(reader); sb.AppendFormat("Q{0} {1} ", pt1, pt2); break; } case PathOpCodes.BezierTo: { ReadPointBoolBool(reader, b, out pt1, out bool stroked, out bool smoothJoin); pt2 = ReadPoint(reader); pt3 = ReadPoint(reader); sb.AppendFormat("C{0} {1} {2} ", pt1, pt2, pt3); break; } case PathOpCodes.PolyLineTo: { pts = ReadPointsBoolBool(reader, b, out bool stroked, out bool smoothJoin); sb.Append('L'); foreach (var pt in pts) sb.AppendFormat("{0} ", pt); break; } case PathOpCodes.PolyQuadraticBezierTo: { pts = ReadPointsBoolBool(reader, b, out bool stroked, out bool smoothJoin); sb.Append('Q'); foreach (var pt in pts) sb.AppendFormat("{0} ", pt); break; } case PathOpCodes.PolyBezierTo: { pts = ReadPointsBoolBool(reader, b, out bool stroked, out bool smoothJoin); sb.Append('C'); foreach (var pt in pts) sb.AppendFormat("{0} ", pt); break; } case PathOpCodes.ArcTo: { ReadPointBoolBool(reader, b, out pt1, out bool stroked, out bool smoothJoin); byte b2 = reader.ReadByte(); bool largeArc = (b2 & 0x0f) != 0; bool sweepDirection = (b2 & 0xf0) != 0; var size = ReadPoint(reader); double angle = reader.ReadXamlDouble(); sb.AppendFormat(CultureInfo.InvariantCulture, "A{0} {1:R} {2} {3} {4}", size, angle, largeArc ? '1' : '0', sweepDirection ? '1' : '0', pt1); break; } case PathOpCodes.Closed: end = true; break; case PathOpCodes.FillRule: { UnpackBools(b, out bool fillRule, out bool b2, out bool b3, out bool b4); if (fillRule) sb.Insert(0, "F1 "); break; } } } return sb.ToString().Trim(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlProperty.cs ================================================ /* Copyright (c) 2015 Ki 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.Linq; using System.Xml; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler.Xaml { internal class XamlProperty { public XamlType DeclaringType { get; } public string PropertyName { get; } public IMember ResolvedMember { get; set; } public XamlProperty(XamlType type, string name) { DeclaringType = type; PropertyName = name; } public void TryResolve() { if (ResolvedMember != null) return; var typeDef = DeclaringType.ResolvedType.GetDefinition(); if (typeDef == null) return; ResolvedMember = typeDef.GetProperties(p => p.Name == PropertyName).FirstOrDefault(); if (ResolvedMember != null) return; ResolvedMember = typeDef.GetFields(f => f.Name == PropertyName + "Property").FirstOrDefault(); if (ResolvedMember != null) return; ResolvedMember = typeDef.GetEvents(e => e.Name == PropertyName).FirstOrDefault(); if (ResolvedMember != null) return; ResolvedMember = typeDef.GetFields(f => f.Name == PropertyName + "Event").FirstOrDefault(); } public bool IsAttachedTo(XamlType type) { if (type == null || ResolvedMember == null || type.ResolvedType == null) return true; var declType = ResolvedMember.DeclaringType; var t = type.ResolvedType; do { if (t.FullName == declType.FullName && t.TypeParameterCount == declType.TypeParameterCount) return false; t = t.DirectBaseTypes.FirstOrDefault(); } while (t != null); return true; } public XName ToXName(XamlContext ctx, XElement parent, bool isFullName = true) { var typeName = DeclaringType.ToXName(ctx); XName name; if (!isFullName) name = XmlConvert.EncodeLocalName(PropertyName); else { name = typeName.LocalName + "." + XmlConvert.EncodeLocalName(PropertyName); if (parent == null || parent.GetDefaultNamespace() != typeName.Namespace) name = typeName.Namespace + name.LocalName; } return name; } public override string ToString() => PropertyName; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlResourceKey.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Diagnostics; using ICSharpCode.BamlDecompiler.Baml; namespace ICSharpCode.BamlDecompiler.Xaml { internal class XamlResourceKey { XamlResourceKey(BamlNode node) { KeyNode = node; StaticResources = new List(); IBamlDeferRecord keyRecord; if (node is BamlBlockNode) keyRecord = (IBamlDeferRecord)((BamlBlockNode)node).Header; else keyRecord = (IBamlDeferRecord)((BamlRecordNode)node).Record; if (keyRecord.Record.Type == BamlRecordType.ElementEnd) { Debug.Assert(node.Parent.Footer == keyRecord.Record); node.Parent.Annotation = this; node.Annotation = this; return; } if (keyRecord.Record.Type != BamlRecordType.ElementStart && node.Parent.Type == BamlRecordType.ElementStart) { node.Parent.Annotation = this; node.Annotation = this; return; } if (keyRecord.Record.Type != BamlRecordType.ElementStart) { Debug.WriteLine($"Key record @{keyRecord.Position} must be attached to ElementStart (actual {keyRecord.Record.Type})"); } foreach (var child in node.Parent.Children) { if (child.Record != keyRecord.Record) continue; child.Annotation = this; node.Annotation = this; return; } Debug.WriteLine("Cannot find corresponding value element of key record @" + keyRecord.Position); } public static XamlResourceKey Create(BamlNode node) => new XamlResourceKey(node); public BamlNode KeyNode { get; set; } public BamlElement KeyElement { get; set; } public IList StaticResources { get; } public static XamlResourceKey FindKeyInSiblings(BamlNode node) { var children = node.Parent.Children; var index = children.IndexOf(node); for (int i = index; i >= 0; i--) { if (children[i].Annotation is XamlResourceKey) return (XamlResourceKey)children[i].Annotation; } return null; } public static XamlResourceKey FindKeyInAncestors(BamlNode node) => FindKeyInAncestors(node, out var found); public static XamlResourceKey FindKeyInAncestors(BamlNode node, out BamlNode found) { BamlNode n = node; do { if (n.Annotation is XamlResourceKey) { found = n; return (XamlResourceKey)n.Annotation; } n = n.Parent; } while (n != null); found = null; return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlType.cs ================================================ /* Copyright (c) 2015 Ki 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.Xml; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler.Xaml { internal class XamlType { /// /// Assembly that contains the type defintion. Can be null. /// public IModule Assembly { get; } public string FullAssemblyName { get; } public string TypeNamespace { get; } public string TypeName { get; } public XNamespace Namespace { get; private set; } public IType ResolvedType { get; set; } public XamlType(IModule assembly, string fullAssemblyName, string ns, string name) : this(assembly, fullAssemblyName, ns, name, null) { } public XamlType(IModule assembly, string fullAssemblyName, string ns, string name, XNamespace xmlns) { Assembly = assembly; FullAssemblyName = fullAssemblyName; TypeNamespace = ns; TypeName = name; Namespace = xmlns; } public void ResolveNamespace(XElement elem, XamlContext ctx) { if (Namespace != null) return; // Since XmlnsProperty records are inside the element, // the namespace is resolved after processing the element body. string xmlNs = null; if (elem.Annotation() != null) xmlNs = elem.Annotation().LookupXmlns(FullAssemblyName, TypeNamespace); if (xmlNs == null) xmlNs = ctx.XmlNs.LookupXmlns(FullAssemblyName, TypeNamespace); // Sometimes there's no reference to System.Xaml even if x:Type is used if (xmlNs == null) xmlNs = ctx.TryGetXmlNamespace(Assembly, TypeNamespace); if (xmlNs == null) { if (FullAssemblyName == ctx.TypeSystem.MainModule.FullAssemblyName) xmlNs = $"clr-namespace:{TypeNamespace}"; else { var name = ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(FullAssemblyName); xmlNs = $"clr-namespace:{TypeNamespace};assembly={name.Name}"; } var nsSeg = TypeNamespace.Split('.'); var prefix = nsSeg[nsSeg.Length - 1].ToLowerInvariant(); if (string.IsNullOrEmpty(prefix)) { if (string.IsNullOrEmpty(TypeNamespace)) prefix = "global"; else prefix = "empty"; } int count = 0; var truePrefix = prefix; XNamespace prefixNs, ns = ctx.GetXmlNamespace(xmlNs); while ((prefixNs = elem.GetNamespaceOfPrefix(truePrefix)) != null && prefixNs != ns) { count++; truePrefix = prefix + count; } if (prefixNs == null) { elem.Add(new XAttribute(XNamespace.Xmlns + XmlConvert.EncodeLocalName(truePrefix), ns)); if (string.IsNullOrEmpty(TypeNamespace)) elem.AddBeforeSelf(new XComment(string.Format("'{0}' is prefix for the global namespace", truePrefix))); } } Namespace = ctx.GetXmlNamespace(xmlNs); } public XName ToXName(XamlContext ctx) { if (Namespace == null) return XmlConvert.EncodeLocalName(TypeName); return Namespace + XmlConvert.EncodeLocalName(TypeName); } public override string ToString() => TypeName; } } ================================================ FILE: ICSharpCode.BamlDecompiler/Xaml/XamlUtils.cs ================================================ /* Copyright (c) 2015 Ki 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.IO; using System.Text; using System.Xml.Linq; namespace ICSharpCode.BamlDecompiler.Xaml { internal static class XamlUtils { public static string Escape(string value) { if (value.Length == 0) return value; if (value[0] == '{') return "{}" + value; return value; } public static string ToString(this XamlContext ctx, XElement elem, XamlType type) { type.ResolveNamespace(elem, ctx); return ctx.ToString(elem, type.ToXName(ctx)); } public static string ToString(this XamlContext ctx, XElement elem, XName name) { var sb = new StringBuilder(); if (name.Namespace != elem.GetDefaultNamespace()) { var prefix = elem.GetPrefixOfNamespace(name.Namespace); if (!string.IsNullOrEmpty(prefix)) { sb.Append(prefix); sb.Append(':'); } } sb.Append(name.LocalName); return sb.ToString(); } public static double ReadXamlDouble(this BinaryReader reader, bool scaledInt = false) { if (!scaledInt) { switch (reader.ReadByte()) { case 1: return 0; case 2: return 1; case 3: return -1; case 4: break; case 5: return reader.ReadDouble(); default: throw new InvalidDataException("Unknown double type."); } } // Dividing by 1000000.0 is important to get back the original numbers, we can't // multiply by the inverse of it (0.000001). // (11700684 * 0.000001) != (11700684 / 1000000.0) => 11.700683999999999 != 11.700684 return reader.ReadInt32() / 1000000.0; } /// /// Escape characters that cannot be used in XML. /// public static StringBuilder EscapeName(StringBuilder sb, string name) { foreach (char ch in name) { if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch)) sb.AppendFormat("\\u{0:x4}", (int)ch); else sb.Append(ch); } return sb; } /// /// Escape characters that cannot be displayed in the UI. /// public static string EscapeName(string name) { return EscapeName(new StringBuilder(name.Length), name).ToString(); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/XamlContext.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Threading; using System.Xml.Linq; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Xaml; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.BamlDecompiler { internal class XamlContext { XamlContext(IDecompilerTypeSystem typeSystem) { TypeSystem = typeSystem; NodeMap = new Dictionary(); XmlNs = new XmlnsDictionary(); GeneratedMembers = new List(); XClassNames = new List(); } Dictionary typeMap = new Dictionary(); Dictionary propertyMap = new Dictionary(); Dictionary xmlnsMap = new Dictionary(); public IDecompilerTypeSystem TypeSystem { get; } public CancellationToken CancellationToken { get; private set; } public BamlDecompilerSettings Settings { get; private set; } public BamlContext Baml { get; private set; } public BamlNode RootNode { get; private set; } public IDictionary NodeMap { get; } public List XClassNames { get; } public List GeneratedMembers { get; } public XmlnsDictionary XmlNs { get; } public static XamlContext Construct(IDecompilerTypeSystem typeSystem, BamlDocument document, CancellationToken token, BamlDecompilerSettings bamlDecompilerOptions) { var ctx = new XamlContext(typeSystem); ctx.CancellationToken = token; ctx.Settings = bamlDecompilerOptions ?? new BamlDecompilerSettings(); ctx.Baml = BamlContext.ConstructContext(typeSystem, document, token); ctx.RootNode = BamlNode.Parse(document, token); ctx.BuildPIMappings(document); ctx.BuildNodeMap(ctx.RootNode as BamlBlockNode); return ctx; } void BuildNodeMap(BamlBlockNode node) { if (node == null) return; NodeMap[node.Header] = node; foreach (var child in node.Children) { if (child is BamlBlockNode childBlock) BuildNodeMap(childBlock); } } void BuildPIMappings(BamlDocument document) { foreach (var record in document) { var piMap = record as PIMappingRecord; if (piMap == null) continue; XmlNs.SetPIMapping(piMap.XmlNamespace, piMap.ClrNamespace, Baml.ResolveAssembly(piMap.AssemblyId).FullAssemblyName); } } public XamlType ResolveType(ushort id) { if (typeMap.TryGetValue(id, out var xamlType)) return xamlType; IType type; IModule assembly; string fullAssemblyName; if (id > 0x7fff) { type = Baml.KnownThings.Types((KnownTypes)(short)-unchecked((short)id)); assembly = type.GetDefinition().ParentModule; fullAssemblyName = assembly.FullAssemblyName; } else { var typeRec = Baml.TypeIdMap[id]; (fullAssemblyName, assembly) = Baml.ResolveAssembly(typeRec.AssemblyId); type = ReflectionHelper.ParseReflectionName(typeRec.TypeFullName, new SimpleTypeResolveContext(TypeSystem)); } var clrNs = type.Namespace; var xmlNs = XmlNs.LookupXmlns(fullAssemblyName, clrNs); typeMap[id] = xamlType = new XamlType(assembly, fullAssemblyName, clrNs, type.Name, GetXmlNamespace(xmlNs)) { ResolvedType = type }; return xamlType; } public XamlProperty ResolveProperty(ushort id) { if (propertyMap.TryGetValue(id, out var xamlProp)) return xamlProp; XamlType type; string name; IMember member; if (id > 0x7fff) { var knownProp = Baml.KnownThings.Members((KnownMembers)unchecked((short)-(short)id)); type = ResolveType(unchecked((ushort)(short)-(short)knownProp.Parent)); name = knownProp.Name; member = knownProp.Property; } else { var attrRec = Baml.AttributeIdMap[id]; type = ResolveType(attrRec.OwnerTypeId); name = attrRec.Name; member = null; } propertyMap[id] = xamlProp = new XamlProperty(type, name) { ResolvedMember = member }; xamlProp.TryResolve(); return xamlProp; } public string ResolveString(ushort id) { if (id > 0x7fff) return Baml.KnownThings.Strings(unchecked((short)-id)); else if (Baml.StringIdMap.ContainsKey(id)) return Baml.StringIdMap[id].Value; return null; } public XNamespace GetXmlNamespace(string xmlns) { if (xmlns == null) return null; if (!xmlnsMap.TryGetValue(xmlns, out var ns)) xmlnsMap[xmlns] = ns = XNamespace.Get(xmlns); return ns; } public const string KnownNamespace_Xaml = "http://schemas.microsoft.com/winfx/2006/xaml"; public const string KnownNamespace_Presentation = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; public const string KnownNamespace_PresentationOptions = "http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"; public string TryGetXmlNamespace(IModule assembly, string typeNamespace) { if (assembly == null) return null; HashSet possibleXmlNs = new HashSet(); foreach (var attr in assembly.GetAssemblyAttributes().Where(a => a.AttributeType.FullName == "System.Windows.Markup.XmlnsDefinitionAttribute")) { Debug.Assert(attr.FixedArguments.Length == 2); if (attr.FixedArguments.Length != 2) continue; var xmlNs = attr.FixedArguments[0].Value as string; var typeNs = attr.FixedArguments[1].Value as string; Debug.Assert((object)xmlNs != null && (object)typeNs != null); if ((object)xmlNs == null || (object)typeNs == null) continue; if (typeNamespace == typeNs) possibleXmlNs.Add(xmlNs); } if (possibleXmlNs.Contains(KnownNamespace_Presentation)) return KnownNamespace_Presentation; return possibleXmlNs.FirstOrDefault(); } public XName GetKnownNamespace(string name, string xmlNamespace, XElement context = null) { var xNs = GetXmlNamespace(xmlNamespace); XName xName; if (context != null && xNs == context.GetDefaultNamespace()) xName = name; else xName = xNs + name; return xName; } public XName GetPseudoName(string name) => XNamespace.Get("https://github.com/icsharpcode/ILSpy").GetName(name); } } ================================================ FILE: ICSharpCode.BamlDecompiler/XamlDecompiler.cs ================================================ /* Copyright (c) 2015 Ki 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.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading; using System.Xml.Linq; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.BamlDecompiler.Baml; using ICSharpCode.BamlDecompiler.Rewrite; namespace ICSharpCode.BamlDecompiler { public class XamlDecompiler { static readonly IRewritePass[] rewritePasses = new IRewritePass[] { new XClassRewritePass(), new MarkupExtensionRewritePass(), new AttributeRewritePass(), new ConnectionIdRewritePass(), new DocumentRewritePass(), }; private BamlDecompilerTypeSystem typeSystem; private BamlDecompilerSettings settings; private MetadataModule module; public BamlDecompilerSettings Settings { get { return settings; } set { settings = value; } } public CancellationToken CancellationToken { get; set; } public XamlDecompiler(string fileName, BamlDecompilerSettings settings) : this(CreateTypeSystemFromFile(fileName, settings), settings) { } public XamlDecompiler(string fileName, IAssemblyResolver assemblyResolver, BamlDecompilerSettings settings) : this(LoadPEFile(fileName, settings), assemblyResolver, settings) { } public XamlDecompiler(PEFile module, IAssemblyResolver assemblyResolver, BamlDecompilerSettings settings) : this(new BamlDecompilerTypeSystem(module, assemblyResolver), settings) { } public XamlDecompiler(BamlDecompilerTypeSystem typeSystem, BamlDecompilerSettings settings) { this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.settings = settings; this.module = typeSystem.MainModule; if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.Uncached)) throw new ArgumentException("Cannot use an uncached type system in the decompiler."); } static PEFile LoadPEFile(string fileName, BamlDecompilerSettings settings) { return new PEFile( fileName, new FileStream(fileName, FileMode.Open, FileAccess.Read), streamOptions: PEStreamOptions.PrefetchEntireImage, metadataOptions: MetadataReaderOptions.None ); } static BamlDecompilerTypeSystem CreateTypeSystemFromFile(string fileName, BamlDecompilerSettings settings) { var file = LoadPEFile(fileName, settings); var resolver = new UniversalAssemblyResolver(fileName, settings.ThrowOnAssemblyResolveErrors, file.DetectTargetFrameworkId(), file.DetectRuntimePack(), PEStreamOptions.PrefetchMetadata, MetadataReaderOptions.None); return new BamlDecompilerTypeSystem(file, resolver); } public BamlDecompilationResult Decompile(Stream stream) { var ct = CancellationToken; var document = BamlReader.ReadDocument(stream, ct); var ctx = XamlContext.Construct(typeSystem, document, ct, settings); var handler = HandlerMap.LookupHandler(ctx.RootNode.Type); var elem = handler.Translate(ctx, ctx.RootNode, null); var xaml = new XDocument(); xaml.Add(elem.Xaml.Element); foreach (var pass in rewritePasses) { ct.ThrowIfCancellationRequested(); pass.Run(ctx, xaml); } var assemblyReferences = ctx.Baml.AssemblyIdMap.Select(a => a.Value.AssemblyFullName); var typeName = ctx.XClassNames.FirstOrDefault() is string s ? (FullTypeName?)new FullTypeName(s) : null; return new BamlDecompilationResult(xaml, typeName, assemblyReferences, ctx.GeneratedMembers); } } } ================================================ FILE: ICSharpCode.BamlDecompiler/XmlnsDictionary.cs ================================================ /* Copyright (c) 2015 Ki 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.Collections.Generic; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.BamlDecompiler.Xaml; namespace ICSharpCode.BamlDecompiler { internal class XmlnsScope : List { public BamlElement Element { get; } public XmlnsScope PreviousScope { get; } public XmlnsScope(XmlnsScope prev, BamlElement elem) { PreviousScope = prev; Element = elem; } public string LookupXmlns(string fullAssemblyName, string clrNs) { foreach (var ns in this) { if (fullAssemblyName == ns.FullAssemblyName && ns.CLRNamespace == clrNs) return ns.XMLNamespace; } return null; } } internal class XmlnsDictionary { Dictionary piMappings = new Dictionary(); public XmlnsDictionary() => CurrentScope = null; public XmlnsScope CurrentScope { get; set; } public void PushScope(BamlElement element) => CurrentScope = new XmlnsScope(CurrentScope, element); public void PopScope() => CurrentScope = CurrentScope.PreviousScope; public void Add(NamespaceMap map) => CurrentScope.Add(map); public void SetPIMapping(string xmlNs, string clrNs, string fullAssemblyName) { if (!piMappings.ContainsKey(xmlNs)) { var map = new NamespaceMap(null, fullAssemblyName, xmlNs, clrNs); piMappings[xmlNs] = map; } } NamespaceMap PIFixup(NamespaceMap map) { if (piMappings.TryGetValue(map.XMLNamespace, out var piMap)) { map.FullAssemblyName = piMap.FullAssemblyName; map.CLRNamespace = piMap.CLRNamespace; } return map; } public NamespaceMap LookupNamespaceFromPrefix(string prefix) { var scope = CurrentScope; while (scope != null) { foreach (var ns in scope) { if (ns.XmlnsPrefix == prefix) return PIFixup(ns); } scope = scope.PreviousScope; } return null; } public NamespaceMap LookupNamespaceFromXmlns(string xmlNs) { var scope = CurrentScope; while (scope != null) { foreach (var ns in scope) { if (ns.XMLNamespace == xmlNs) return ns; } scope = scope.PreviousScope; } return null; } public string LookupXmlns(string fullAssemblyName, string clrNs) { foreach (var map in piMappings) { if (fullAssemblyName == map.Value.FullAssemblyName && map.Value.CLRNamespace == clrNs) return map.Key; } var scope = CurrentScope; while (scope != null) { foreach (var ns in scope) { if (fullAssemblyName == ns.FullAssemblyName && ns.CLRNamespace == clrNs) return ns.XMLNamespace; } scope = scope.PreviousScope; } return null; } } } ================================================ FILE: ICSharpCode.BamlDecompiler/packages.lock.json ================================================ { "version": 2, "dependencies": { "net10.0": { "Microsoft.Sbom.Targets": { "type": "Direct", "requested": "[4.1.5, )", "resolved": "4.1.5", "contentHash": "i5z+cNu/cOcdO0AgFB8aXk8w6In2H+haaDfSgd9ImvQIK+rSHavHZIogVoAZLL8jLwYx4bAcs5b7EyuMMG4mQQ==" }, "TomsToolbox.Composition.Analyzer": { "type": "Direct", "requested": "[2.22.2, )", "resolved": "2.22.2", "contentHash": "7gYo8ZR2eq3XkrilvUpLbTypeZy6IlD5FB8jah0YPhMOmDGhya4jJ3kfDMTTRt5m258Ou78P69mHMkG6DKZXsg==" }, "icsharpcode.decompiler": { "type": "Project", "dependencies": { "System.Collections.Immutable": "[9.0.0, )", "System.Reflection.Metadata": "[9.0.0, )" } }, "System.Collections.Immutable": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "9.0.0", "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" }, "System.Reflection.Metadata": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "9.0.0", "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==" } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Annotations.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp { // Annotations: // * AstNodes should be annotated with the corresponding ILInstruction // * AstNodes referring to other entities should be annotated with the IEntity // * Expression type information is currently only available in the ExpressionBuilder, but we might // change 'WithTypeInfo()' to use an annotation in the future // * IntroduceUnsafeModifier.PointerArithmeticAnnotation is placed on arithmetic operators that operate // on pointers. // TODO: actually, we could use the type info instead? // * AddCheckedBlocks.CheckedAnnotation / AddCheckedBlocks.UnCheckedAnnotation is used on // checked/unchecked integer arithmetic // TODO: here the info is also redundant, we could peek at the BinaryNumericInstruction instead // but on the other hand, some unchecked casts are not backed by any BinaryNumericInstruction /// /// Currently unused; we'll probably use the LdToken ILInstruction as annotation instead when LdToken /// support gets reimplemented. /// public class LdTokenAnnotation { } public static class AnnotationExtensions { internal static ExpressionWithILInstruction WithILInstruction(this Expression expression, ILInstruction instruction) { expression.AddAnnotation(instruction); return new ExpressionWithILInstruction(expression); } internal static ExpressionWithILInstruction WithILInstruction(this Expression expression, IEnumerable instructions) { foreach (var inst in instructions) expression.AddAnnotation(inst); return new ExpressionWithILInstruction(expression); } internal static ExpressionWithILInstruction WithoutILInstruction(this Expression expression) { return new ExpressionWithILInstruction(expression); } internal static TranslatedStatement WithILInstruction(this Statement statement, ILInstruction instruction) { statement.AddAnnotation(instruction); return new TranslatedStatement(statement); } internal static TranslatedStatement WithILInstruction(this Statement statement, IEnumerable instructions) { foreach (var inst in instructions) statement.AddAnnotation(inst); return new TranslatedStatement(statement); } internal static TranslatedStatement WithoutILInstruction(this Statement statement) { return new TranslatedStatement(statement); } internal static TranslatedExpression WithILInstruction(this ExpressionWithResolveResult expression, ILInstruction instruction) { expression.Expression.AddAnnotation(instruction); return new TranslatedExpression(expression.Expression, expression.ResolveResult); } internal static TranslatedExpression WithILInstruction(this ExpressionWithResolveResult expression, IEnumerable instructions) { foreach (var inst in instructions) expression.Expression.AddAnnotation(inst); return new TranslatedExpression(expression.Expression, expression.ResolveResult); } internal static TranslatedExpression WithILInstruction(this TranslatedExpression expression, ILInstruction instruction) { expression.Expression.AddAnnotation(instruction); return expression; } internal static TranslatedExpression WithoutILInstruction(this ExpressionWithResolveResult expression) { return new TranslatedExpression(expression.Expression, expression.ResolveResult); } internal static ExpressionWithResolveResult WithRR(this Expression expression, ResolveResult resolveResult) { expression.AddAnnotation(resolveResult); return new ExpressionWithResolveResult(expression, resolveResult); } internal static TranslatedExpression WithRR(this ExpressionWithILInstruction expression, ResolveResult resolveResult) { expression.Expression.AddAnnotation(resolveResult); return new TranslatedExpression(expression, resolveResult); } /// /// Retrieves the associated with this AstNode, or null if no symbol /// is associated with the node. /// public static ISymbol GetSymbol(this AstNode node) { var rr = node.Annotation(); if (rr is MethodGroupResolveResult mgrr) { return mgrr.ChosenMethod; } return rr?.GetSymbol(); } /// /// Retrieves the associated with this , /// or if no resolve result is associated with the node. /// public static ResolveResult GetResolveResult(this AstNode node) { return node.Annotation() ?? ErrorResolveResult.UnknownError; } /// /// Retrieves the associated with this , /// or null if no variable is associated with this identifier. /// public static ILVariable GetILVariable(this IdentifierExpression expr) { if (expr.Annotation() is ILVariableResolveResult rr) return rr.Variable; else return null; } /// /// Retrieves the associated with this , /// or null if no variable is associated with this initializer. /// public static ILVariable GetILVariable(this VariableInitializer vi) { if (vi.Annotation() is ILVariableResolveResult rr) return rr.Variable; else return null; } /// /// Retrieves the associated with this , /// or null if no variable is associated with this foreach statement. /// public static ILVariable GetILVariable(this ForeachStatement loop) { if (loop.Annotation() is ILVariableResolveResult rr) return rr.Variable; else return null; } /// /// Adds an to this initializer. /// public static VariableInitializer WithILVariable(this VariableInitializer vi, ILVariable v) { vi.AddAnnotation(new ILVariableResolveResult(v, v.Type)); return vi; } /// /// Adds an to this foreach statement. /// public static ForeachStatement WithILVariable(this ForeachStatement loop, ILVariable v) { loop.AddAnnotation(new ILVariableResolveResult(v, v.Type)); return loop; } /// /// Copies all annotations from to . /// public static T CopyAnnotationsFrom(this T node, AstNode other) where T : AstNode { foreach (object annotation in other.Annotations) { node.AddAnnotation(annotation); } return node; } /// /// Copies all annotations from /// to . /// public static T CopyInstructionsFrom(this T node, AstNode other) where T : AstNode { foreach (object annotation in other.Annotations.OfType()) { node.AddAnnotation(annotation); } return node; } } /// /// Represents a reference to a local variable. /// public class ILVariableResolveResult : ResolveResult { public readonly ILVariable Variable; public ILVariableResolveResult(ILVariable v) : base(v.Type) { this.Variable = v; } public ILVariableResolveResult(ILVariable v, IType type) : base(type) { this.Variable = v ?? throw new ArgumentNullException(nameof(v)); } } /// /// Annotates a with the instructions for the GetEnumerator, MoveNext /// and get_Current calls. /// public class ForeachAnnotation { public readonly ILInstruction GetEnumeratorCall; public readonly ILInstruction MoveNextCall; public readonly ILInstruction GetCurrentCall; public ForeachAnnotation(ILInstruction getEnumeratorCall, ILInstruction moveNextCall, ILInstruction getCurrentCall) { GetEnumeratorCall = getEnumeratorCall; MoveNextCall = moveNextCall; GetCurrentCall = getCurrentCall; } } /// /// Annotates the top-level block statement of a function /// with the implicitly executed return/yield break. /// public class ImplicitReturnAnnotation { public readonly Leave Leave; public ImplicitReturnAnnotation(Leave leave) { this.Leave = leave; } } /// /// Annotates an expression when an implicit user-defined conversion was omitted. /// public class ImplicitConversionAnnotation { public readonly ConversionResolveResult ConversionResolveResult; public IType TargetType => ConversionResolveResult.Type; public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult) { this.ConversionResolveResult = conversionResolveResult; } } /// /// Annotates a QueryGroupClause with the ILFunctions of each (implicit lambda) expression. /// public class QueryGroupClauseAnnotation { public readonly ILFunction KeyLambda; public readonly ILFunction ProjectionLambda; public QueryGroupClauseAnnotation(ILFunction key, ILFunction projection) { this.KeyLambda = key; this.ProjectionLambda = projection; } } /// /// Annotates a QueryJoinClause with the ILFunctions of each (implicit lambda) expression. /// public class QueryJoinClauseAnnotation { public readonly ILFunction OnLambda; public readonly ILFunction EqualsLambda; public QueryJoinClauseAnnotation(ILFunction on, ILFunction equals) { this.OnLambda = on; this.EqualsLambda = equals; } } /// /// Annotates an out DirectionExpression if the out variable can be declared implicitly typed. /// public class UseImplicitlyTypedOutAnnotation { public static readonly UseImplicitlyTypedOutAnnotation Instance = new UseImplicitlyTypedOutAnnotation(); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.CSharp { /// /// Main class of the C# decompiler engine. /// /// /// Instances of this class are not thread-safe. Use separate instances to decompile multiple members in parallel. /// (in particular, the transform instances are not thread-safe) /// public class CSharpDecompiler { readonly IDecompilerTypeSystem typeSystem; readonly MetadataModule module; readonly MetadataReader metadata; readonly DecompilerSettings settings; SyntaxTree syntaxTree; List ilTransforms = GetILTransforms(); /// /// Pre-yield/await transforms. /// internal static List EarlyILTransforms(bool aggressivelyDuplicateReturnBlocks = false) { return new List { new ControlFlowSimplification { aggressivelyDuplicateReturnBlocks = aggressivelyDuplicateReturnBlocks }, new SplitVariables(), new ILInlining(), }; } /// /// Returns all built-in transforms of the ILAst pipeline. /// public static List GetILTransforms() { return new List { new ControlFlowSimplification(), // Run SplitVariables only after ControlFlowSimplification duplicates return blocks, // so that the return variable is split and can be inlined. new SplitVariables(), new ILInlining(), new InlineReturnTransform(), // must run before DetectPinnedRegions new RemoveInfeasiblePathTransform(), new DetectPinnedRegions(), // must run after inlining but before non-critical control flow transforms new YieldReturnDecompiler(), // must run after inlining but before loop detection new AsyncAwaitDecompiler(), // must run after inlining but before loop detection new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection new DetectExitPoints(), new LdLocaDupInitObjTransform(), new EarlyExpressionTransforms(), new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements // RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...) // is already collapsed into stloc(V, ...). new RemoveDeadVariableInit(), new ControlFlowSimplification(), //split variables may enable new branch to leave inlining new DynamicCallSiteTransform(), new SwitchDetection(), new SwitchOnStringTransform(), new SwitchOnNullableTransform(), new SplitVariables(), // split variables once again, because SwitchOnNullableTransform eliminates ldloca new IntroduceRefReadOnlyModifierOnLocals(), new BlockILTransform { // per-block transforms PostOrderTransforms = { // Even though it's a post-order block-transform as most other transforms, // let's keep LoopDetection separate for now until there's a compelling // reason to combine it with the other block transforms. // If we ran loop detection after some if structures are already detected, // we might make our life introducing good exit points more difficult. new LoopDetection() } }, // re-run DetectExitPoints after loop detection new DetectExitPoints(), new PatternMatchingTransform(), // must run after LoopDetection and before ConditionDetection new BlockILTransform { // per-block transforms PostOrderTransforms = { new ConditionDetection(), new LockTransform(), new UsingTransform(), // CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform // and must run before NullCoalescingTransform new CachedDelegateInitialization(), new StatementTransform( // per-block transforms that depend on each other, and thus need to // run interleaved (statement by statement). // Pretty much all transforms that open up new expression inlining // opportunities belong in this category. new ILInlining() { options = InliningOptions.AllowInliningOfLdloca }, // Inlining must be first, because it doesn't trigger re-runs. // Any other transform that opens up new inlining opportunities should call RequestRerun(). new ExpressionTransforms(), new DynamicIsEventAssignmentTransform(), new TransformAssignment(), // inline and compound assignments new NullCoalescingTransform(), new NullableLiftingStatementTransform(), new NullPropagationStatementTransform(), new TransformArrayInitializers(), new TransformCollectionAndObjectInitializers(), new TransformExpressionTrees(), new IndexRangeTransform(), new DeconstructionTransform(), new NamedArgumentTransform(), new RemoveUnconstrainedGenericReferenceTypeCheck(), new UserDefinedLogicTransform(), new InterpolatedStringTransform() ), } }, new ProxyCallReplacer(), new FixRemainingIncrements(), new CopyPropagation(), new DelegateConstruction(), new LocalFunctionDecompiler(), new TransformDisplayClassUsage(), new HighLevelLoopTransform(), new ReduceNestingTransform(), new RemoveRedundantReturn(), new IntroduceDynamicTypeOnLocals(), new IntroduceNativeIntTypeOnLocals(), new AssignVariableNames(), }; } List astTransforms = GetAstTransforms(); /// /// Returns all built-in transforms of the C# AST pipeline. /// public static List GetAstTransforms() { return new List { new PatternStatementTransform(), new ReplaceMethodCallsWithOperators(), // must run before DeclareVariables.EnsureExpressionStatementsAreValid new IntroduceUnsafeModifier(), new AddCheckedBlocks(), new DeclareVariables(), // should run after most transforms that modify statements new TransformFieldAndConstructorInitializers(), // must run after DeclareVariables new PrettifyAssignments(), // must run after DeclareVariables new IntroduceUsingDeclarations(), new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods new CombineQueryExpressions(), new NormalizeBlockStatements(), new FlattenSwitchBlocks(), new FixNameCollisions(), new AddXmlDocumentationTransform(), }; } /// /// Token to check for requested cancellation of the decompilation. /// public CancellationToken CancellationToken { get; set; } /// /// The type system created from the main module and referenced modules. /// public IDecompilerTypeSystem TypeSystem => typeSystem; /// /// Gets or sets the optional provider for debug info. /// public IDebugInfoProvider DebugInfoProvider { get; set; } /// /// Gets or sets the optional provider for XML documentation strings. /// public IDocumentationProvider DocumentationProvider { get; set; } /// /// IL transforms. /// public IList ILTransforms { get { return ilTransforms; } } /// /// C# AST transforms. /// public IList AstTransforms { get { return astTransforms; } } /// /// Creates a new instance from the given using the given . /// public CSharpDecompiler(string fileName, DecompilerSettings settings) : this(CreateTypeSystemFromFile(fileName, settings), settings) { } /// /// Creates a new instance from the given using the given and . /// public CSharpDecompiler(string fileName, IAssemblyResolver assemblyResolver, DecompilerSettings settings) : this(LoadPEFile(fileName, settings), assemblyResolver, settings) { } /// /// Creates a new instance from the given using the given and . /// public CSharpDecompiler(MetadataFile module, IAssemblyResolver assemblyResolver, DecompilerSettings settings) : this(new DecompilerTypeSystem(module, assemblyResolver, settings), settings) { } /// /// Creates a new instance from the given and the given . /// public CSharpDecompiler(IDecompilerTypeSystem typeSystem, DecompilerSettings settings) { this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.settings = settings; this.module = typeSystem.MainModule; this.metadata = module.MetadataFile.Metadata; if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.Uncached)) throw new ArgumentException("Cannot use an uncached type system in the decompiler."); } #region MemberIsHidden /// /// Determines whether a should be hidden from the decompiled code. This is used to exclude compiler-generated code that is handled by transforms from the output. /// /// The module containing the member. /// The metadata token/handle of the member. Can be a TypeDef, MethodDef or FieldDef. /// THe settings used to determine whether code should be hidden. E.g. if async methods are not transformed, async state machines are included in the decompiled code. public static bool MemberIsHidden(MetadataFile module, EntityHandle member, DecompilerSettings settings) { if (module == null || member.IsNil) return false; var metadata = module.Metadata; string name; switch (member.Kind) { case HandleKind.MethodDefinition: var methodHandle = (MethodDefinitionHandle)member; var method = metadata.GetMethodDefinition(methodHandle); var methodSemantics = module.MethodSemanticsLookup.GetSemantics(methodHandle).Item2; if (methodSemantics != 0 && methodSemantics != System.Reflection.MethodSemanticsAttributes.Other) return true; name = metadata.GetString(method.Name); if (name == ".ctor" && method.RelativeVirtualAddress == 0 && metadata.GetTypeDefinition(method.GetDeclaringType()).Attributes.HasFlag(System.Reflection.TypeAttributes.Import)) return true; if (settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionMethod(module, methodHandle)) return true; if (settings.AnonymousMethods && methodHandle.HasGeneratedName(metadata) && methodHandle.IsCompilerGenerated(metadata)) return true; if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedMainMethod(module, methodHandle)) return true; return false; case HandleKind.TypeDefinition: var typeHandle = (TypeDefinitionHandle)member; var type = metadata.GetTypeDefinition(typeHandle); name = metadata.GetString(type.Name); if (!type.GetDeclaringType().IsNil) { if (settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionDisplayClass(module, typeHandle)) return true; if (settings.AnonymousMethods && IsClosureType(type, metadata)) return true; if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(typeHandle, metadata)) return true; if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(typeHandle, metadata)) return true; if (settings.AsyncEnumerator && AsyncAwaitDecompiler.IsCompilerGeneratorAsyncEnumerator(typeHandle, metadata)) return true; if (settings.FixedBuffers && name.StartsWith("<", StringComparison.Ordinal) && name.Contains("__FixedBuffer")) return true; if (settings.InlineArrays && name.StartsWith("<>y__InlineArray", StringComparison.Ordinal) && name.EndsWith("`1", StringComparison.Ordinal)) return true; if (settings.ExtensionMembers && name.StartsWith("<>E__", StringComparison.Ordinal)) return true; } else if (type.IsCompilerGenerated(metadata)) { if (settings.ArrayInitializers && name.StartsWith("", StringComparison.Ordinal)) return true; if (settings.AnonymousTypes && type.IsAnonymousType(metadata)) return true; if (settings.Dynamic && type.IsDelegate(metadata) && (name.StartsWith("<>A", StringComparison.Ordinal) || name.StartsWith("<>F", StringComparison.Ordinal))) return true; } if (settings.ArrayInitializers && settings.SwitchStatementOnString && name.StartsWith("", StringComparison.Ordinal)) return true; return false; case HandleKind.FieldDefinition: var fieldHandle = (FieldDefinitionHandle)member; var field = metadata.GetFieldDefinition(fieldHandle); name = metadata.GetString(field.Name); if (field.IsCompilerGenerated(metadata)) { if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field, metadata)) return true; if (settings.UsePrimaryConstructorSyntaxForNonRecordTypes && IsPrimaryConstructorParameterBackingField(field, metadata)) return true; if (settings.AutomaticProperties && module.PropertyAndEventBackingFieldLookup.IsPropertyBackingField(fieldHandle, out var propertyHandle)) { if (!settings.GetterOnlyAutomaticProperties) { PropertyAccessors accessors = metadata.GetPropertyDefinition(propertyHandle).GetAccessors(); if (!accessors.Getter.IsNil && accessors.Setter.IsNil) return false; } return true; } if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field, metadata)) return true; } // event-fields are not [CompilerGenerated] if (settings.AutomaticEvents && module.PropertyAndEventBackingFieldLookup.IsEventBackingField(fieldHandle, out _)) { return true; } if (settings.ArrayInitializers && metadata.GetString(metadata.GetTypeDefinition(field.GetDeclaringType()).Name).StartsWith("", StringComparison.Ordinal)) { // only hide fields starting with '__StaticArrayInit' if (name.StartsWith("__StaticArrayInit", StringComparison.Ordinal)) return true; // hide fields starting with '$$method' if (name.StartsWith("$$method", StringComparison.Ordinal)) return true; if (field.DecodeSignature(new Metadata.FullTypeNameSignatureDecoder(metadata), default).ToString().StartsWith("__StaticArrayInit", StringComparison.Ordinal)) return true; } return false; } return false; } static bool IsPrimaryConstructorParameterBackingField(SRM.FieldDefinition field, MetadataReader metadata) { var name = metadata.GetString(field.Name); return name.StartsWith("<", StringComparison.Ordinal) && name.EndsWith(">P", StringComparison.Ordinal); } static bool IsSwitchOnStringCache(SRM.FieldDefinition field, MetadataReader metadata) { return metadata.GetString(field.Name).StartsWith("<>f__switch", StringComparison.Ordinal); } internal static bool IsEventBackingFieldName(string fieldName, string eventName, out int suffixLength) { suffixLength = 0; if (fieldName == eventName) return true; var vbSuffixLength = "Event".Length; if (fieldName.Length == eventName.Length + vbSuffixLength && fieldName.StartsWith(eventName, StringComparison.Ordinal) && fieldName.EndsWith("Event", StringComparison.Ordinal)) { suffixLength = vbSuffixLength; return true; } return false; } static bool IsAnonymousMethodCacheField(SRM.FieldDefinition field, MetadataReader metadata) { var name = metadata.GetString(field.Name); return name.StartsWith("CS$<>", StringComparison.Ordinal) || name.StartsWith("<>f__am", StringComparison.Ordinal) || name.StartsWith("<>f__mg", StringComparison.Ordinal); } static bool IsClosureType(SRM.TypeDefinition type, MetadataReader metadata) { var name = metadata.GetString(type.Name); if (!type.Name.IsGeneratedName(metadata) || !type.IsCompilerGenerated(metadata)) return false; if (name.Contains("DisplayClass") || name.Contains("AnonStorey") || name.Contains("Closure$")) return true; return type.BaseType.IsKnownType(metadata, KnownTypeCode.Object) && !type.GetInterfaceImplementations().Any(); } internal static bool IsTransparentIdentifier(string identifier) { return identifier.StartsWith("<>", StringComparison.Ordinal) && (identifier.Contains("TransparentIdentifier") || identifier.Contains("TranspIdent")); } #endregion #region NativeOrdering /// /// Determines whether a given type requires that its methods be ordered precisely as they were originally defined. /// /// The type whose members may need native ordering. internal bool RequiresNativeOrdering(ITypeDefinition typeDef) { // The main scenario for requiring the native method ordering is COM interop, where the V-table is fixed by the ABI return ComHelper.IsComImport(typeDef); } /// /// Compare handles with the method definition ordering intact by using the underlying method's MetadataToken, /// which is defined as the index into a given metadata table. This should equate to the original order that /// methods and properties were defined by the author. /// /// The type whose members to order using their method's MetadataToken /// A sequence of all members ordered by MetadataToken internal IEnumerable GetMembersWithNativeOrdering(ITypeDefinition typeDef) { EntityHandle GetOrderingHandle(IMember member) { // Note! Technically COM interfaces could define property getters and setters out of order or interleaved with other // methods, but C# doesn't support this so we can't define it that way. if (member is IMethod) return member.MetadataToken; else if (member is IProperty property) return property.Getter?.MetadataToken ?? property.Setter?.MetadataToken ?? property.MetadataToken; else if (member is IEvent @event) return @event.AddAccessor?.MetadataToken ?? @event.RemoveAccessor?.MetadataToken ?? @event.InvokeAccessor?.MetadataToken ?? @event.MetadataToken; else return member.MetadataToken; } return typeDef.Fields.Concat(typeDef.Properties).Concat(typeDef.Methods).Concat(typeDef.Events).OrderBy((member) => GetOrderingHandle(member), HandleComparer.Default); } #endregion static PEFile LoadPEFile(string fileName, DecompilerSettings settings) { settings.LoadInMemory = true; return new PEFile( fileName, new FileStream(fileName, FileMode.Open, FileAccess.Read), streamOptions: PEStreamOptions.PrefetchEntireImage, metadataOptions: settings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None ); } static DecompilerTypeSystem CreateTypeSystemFromFile(string fileName, DecompilerSettings settings) { settings.LoadInMemory = true; var file = LoadPEFile(fileName, settings); var resolver = new UniversalAssemblyResolver(fileName, settings.ThrowOnAssemblyResolveErrors, file.DetectTargetFrameworkId(), file.DetectRuntimePack(), settings.LoadInMemory ? PEStreamOptions.PrefetchMetadata : PEStreamOptions.Default, settings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None); return new DecompilerTypeSystem(file, resolver, settings); } static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings) { var typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.ShowAttributes = true; typeSystemAstBuilder.UsePrivateProtectedAccessibility = settings.IntroducePrivateProtectedAccessibility; typeSystemAstBuilder.SortAttributes = settings.SortCustomAttributes; typeSystemAstBuilder.AlwaysUseShortTypeNames = true; typeSystemAstBuilder.AddResolveResultAnnotations = true; typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors; typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses; typeSystemAstBuilder.SupportRecordStructs = settings.RecordStructs; typeSystemAstBuilder.SupportUnsignedRightShift = settings.UnsignedRightShift; typeSystemAstBuilder.SupportOperatorChecked = settings.CheckedOperators; typeSystemAstBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal; return typeSystemAstBuilder; } IDocumentationProvider CreateDefaultDocumentationProvider() { try { return XmlDocLoader.LoadDocumentation(module.MetadataFile); } catch (System.Xml.XmlException) { return null; } } DecompileRun CreateDecompileRun(HashSet namespaces) { List resolvedNamespaces = new List(); foreach (var ns in namespaces) { var resolvedNamespace = typeSystem.GetNamespaceByFullName(ns); if (resolvedNamespace != null) { resolvedNamespaces.Add(resolvedNamespace); } } UsingScope usingScope = new UsingScope( new CSharpTypeResolveContext(typeSystem.MainModule), typeSystem.RootNamespace, resolvedNamespaces.ToImmutableArray() ); return new DecompileRun(settings, usingScope) { DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), CancellationToken = CancellationToken, Namespaces = namespaces }; } void RunTransforms(AstNode rootNode, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var context = new TransformContext(typeSystem, decompileRun, decompilationContext, typeSystemAstBuilder); foreach (var transform in astTransforms) { CancellationToken.ThrowIfCancellationRequested(); transform.Run(rootNode, context); } CancellationToken.ThrowIfCancellationRequested(); rootNode.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); CancellationToken.ThrowIfCancellationRequested(); GenericGrammarAmbiguityVisitor.ResolveAmbiguities(rootNode); } string SyntaxTreeToString(SyntaxTree syntaxTree) { StringWriter w = new StringWriter(); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions)); return w.ToString(); } /// /// Decompile assembly and module attributes. /// public SyntaxTree DecompileModuleAndAssemblyAttributes() { var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); var namespaces = new HashSet(); syntaxTree = new SyntaxTree(); RequiredNamespaceCollector.CollectAttributeNamespaces(module, namespaces); DecompileRun decompileRun = CreateDecompileRun(namespaces); DoDecompileModuleAndAssemblyAttributes(decompileRun, decompilationContext, syntaxTree); RunTransforms(syntaxTree, decompileRun, decompilationContext); return syntaxTree; } /// /// Decompile assembly and module attributes. /// public string DecompileModuleAndAssemblyAttributesToString() { return SyntaxTreeToString(DecompileModuleAndAssemblyAttributes()); } void DoDecompileModuleAndAssemblyAttributes(DecompileRun decompileRun, ITypeResolveContext decompilationContext, SyntaxTree syntaxTree) { try { foreach (var a in typeSystem.MainModule.GetAssemblyAttributes()) { var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "assembly"; syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); } foreach (var a in typeSystem.MainModule.GetModuleAttributes()) { var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "module"; syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); } } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, null, innerException, "Error decompiling module and assembly attributes of " + module.AssemblyName); } } void DoDecompileTypes(IEnumerable types, DecompileRun decompileRun, ITypeResolveContext decompilationContext, SyntaxTree syntaxTree) { string currentNamespace = null; AstNode groupNode = null; foreach (var typeDefHandle in types) { var typeDef = module.GetDefinition(typeDefHandle); if (typeDef.Name == "" && typeDef.Members.Count == 0) continue; if (MemberIsHidden(module.MetadataFile, typeDefHandle, settings)) continue; if (string.IsNullOrEmpty(typeDef.Namespace)) { groupNode = syntaxTree; } else { if (currentNamespace != typeDef.Namespace) { groupNode = new NamespaceDeclaration(typeDef.Namespace); syntaxTree.AddChild(groupNode, SyntaxTree.MemberRole); } } currentNamespace = typeDef.Namespace; var typeDecl = DoDecompile(typeDef, decompileRun, decompilationContext.WithCurrentTypeDefinition(typeDef)); groupNode.AddChild(typeDecl, SyntaxTree.MemberRole); } } /// /// Decompiles the whole module into a single syntax tree. /// public SyntaxTree DecompileWholeModuleAsSingleFile() { return DecompileWholeModuleAsSingleFile(false); } /// /// Decompiles the whole module into a single syntax tree. /// /// If true, top-level-types are emitted sorted by namespace/name. /// If false, types are emitted in metadata order. public SyntaxTree DecompileWholeModuleAsSingleFile(bool sortTypes) { var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); syntaxTree = new SyntaxTree(); var namespaces = new HashSet(); RequiredNamespaceCollector.CollectNamespaces(module, namespaces); var decompileRun = CreateDecompileRun(namespaces); DoDecompileModuleAndAssemblyAttributes(decompileRun, decompilationContext, syntaxTree); var typeDefs = metadata.GetTopLevelTypeDefinitions(); if (sortTypes) { typeDefs = typeDefs.OrderBy(td => { var typeDef = module.metadata.GetTypeDefinition(td); return (module.metadata.GetString(typeDef.Namespace), module.metadata.GetString(typeDef.Name)); }); } DoDecompileTypes(typeDefs, decompileRun, decompilationContext, syntaxTree); RunTransforms(syntaxTree, decompileRun, decompilationContext); return syntaxTree; } /// /// Creates an for the given . /// public ILTransformContext CreateILTransformContext(ILFunction function) { var namespaces = new HashSet(); RequiredNamespaceCollector.CollectNamespaces(function.Method, module, namespaces); var decompileRun = CreateDecompileRun(namespaces); return new ILTransformContext(function, typeSystem, DebugInfoProvider, settings) { CancellationToken = CancellationToken, DecompileRun = decompileRun }; } /// /// Determines the "code-mappings" for a given TypeDef or MethodDef. See for more information. /// public static CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member) { var declaringType = (TypeDefinitionHandle)member.GetDeclaringType(module.Metadata); if (declaringType.IsNil && member.Kind == HandleKind.TypeDefinition) { declaringType = (TypeDefinitionHandle)member; } var info = new CodeMappingInfo(module, declaringType); var td = module.Metadata.GetTypeDefinition(declaringType); foreach (var method in td.GetMethods()) { var parent = method; var part = method; var connectedMethods = new Queue(); var processedMethods = new HashSet(); var processedNestedTypes = new HashSet(); connectedMethods.Enqueue(part); while (connectedMethods.Count > 0) { part = connectedMethods.Dequeue(); if (!processedMethods.Add(part)) continue; try { ReadCodeMappingInfo(module, info, parent, part, connectedMethods, processedNestedTypes); } catch (BadImageFormatException) { // ignore invalid IL } } } return info; } private static void ReadCodeMappingInfo(MetadataFile module, CodeMappingInfo info, MethodDefinitionHandle parent, MethodDefinitionHandle part, Queue connectedMethods, HashSet processedNestedTypes) { var md = module.Metadata.GetMethodDefinition(part); if (!md.HasBody()) { info.AddMapping(parent, part); return; } var declaringType = md.GetDeclaringType(); var blob = module.GetMethodBody(md.RelativeVirtualAddress).GetILReader(); while (blob.RemainingBytes > 0) { var code = blob.DecodeOpCode(); switch (code) { case ILOpCode.Newobj: case ILOpCode.Stfld: // async and yield fsms: var token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (token.IsNil) continue; TypeDefinitionHandle fsmTypeDef; switch (token.Kind) { case HandleKind.MethodDefinition: var fsmMethod = module.Metadata.GetMethodDefinition((MethodDefinitionHandle)token); fsmTypeDef = fsmMethod.GetDeclaringType(); break; case HandleKind.FieldDefinition: var fsmField = module.Metadata.GetFieldDefinition((FieldDefinitionHandle)token); fsmTypeDef = fsmField.GetDeclaringType(); break; case HandleKind.MemberReference: var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); fsmTypeDef = ExtractDeclaringType(memberRef); break; default: continue; } if (!fsmTypeDef.IsNil) { var fsmType = module.Metadata.GetTypeDefinition(fsmTypeDef); // Must be a nested type of the containing type. if (fsmType.GetDeclaringType() != declaringType) break; if (YieldReturnDecompiler.IsCompilerGeneratorEnumerator(fsmTypeDef, module.Metadata) || AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(fsmTypeDef, module.Metadata)) { if (!processedNestedTypes.Add(fsmTypeDef)) break; foreach (var h in fsmType.GetMethods()) { if (module.MethodSemanticsLookup.GetSemantics(h).Item2 != 0) continue; var otherMethod = module.Metadata.GetMethodDefinition(h); if (!otherMethod.GetCustomAttributes().HasKnownAttribute(module.Metadata, KnownAttribute.DebuggerHidden)) { connectedMethods.Enqueue(h); } } } } break; case ILOpCode.Ldftn: // deal with ldftn instructions, i.e., lambdas token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (token.IsNil) continue; TypeDefinitionHandle closureTypeHandle; switch (token.Kind) { case HandleKind.MethodDefinition: if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) { connectedMethods.Enqueue((MethodDefinitionHandle)token); } continue; case HandleKind.MemberReference: var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); if (memberRef.GetKind() != MemberReferenceKind.Method) continue; closureTypeHandle = ExtractDeclaringType(memberRef); if (!closureTypeHandle.IsNil) { var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle); if (closureTypeHandle != declaringType) { // Must be a nested type of the containing type. if (closureType.GetDeclaringType() != declaringType) break; if (!processedNestedTypes.Add(closureTypeHandle)) break; foreach (var m in closureType.GetMethods()) { connectedMethods.Enqueue(m); } } else { // Delegate body is declared in the same type foreach (var m in closureType.GetMethods()) { var methodDef = module.Metadata.GetMethodDefinition(m); if (methodDef.Name == memberRef.Name && m.IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) connectedMethods.Enqueue(m); } } break; } break; default: continue; } break; case ILOpCode.Call: case ILOpCode.Callvirt: // deal with call/callvirt instructions, i.e., local function invocations token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (token.IsNil) continue; switch (token.Kind) { case HandleKind.MethodDefinition: break; case HandleKind.MethodSpecification: var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token); if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition) continue; token = methodSpec.Method; break; default: continue; } if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) { connectedMethods.Enqueue((MethodDefinitionHandle)token); } break; default: blob.SkipOperand(code); break; } } info.AddMapping(parent, part); TypeDefinitionHandle ExtractDeclaringType(MemberReference memberRef) { switch (memberRef.Parent.Kind) { case HandleKind.TypeReference: // This should never happen in normal code, because we are looking at nested types // If it's not a nested type, it can't be a reference to the state machine or lambda anyway, and // those should be either TypeDef or TypeSpec. return default; case HandleKind.TypeDefinition: return (TypeDefinitionHandle)memberRef.Parent; case HandleKind.TypeSpecification: var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent); // Only read the generic type, ignore the type arguments var genericType = ts.GetGenericType(module.Metadata); // Again, we assume this is a type def, because we are only looking at nested types if (genericType.Kind != HandleKind.TypeDefinition) return default; return (TypeDefinitionHandle)genericType; } return default; } } /// /// Decompiles the whole module into a single string. /// public string DecompileWholeModuleAsString() { return SyntaxTreeToString(DecompileWholeModuleAsSingleFile()); } /// /// Decompile the given types. /// /// /// Unlike Decompile(IMemberDefinition[]), this method will add namespace declarations around the type definitions. /// public SyntaxTree DecompileTypes(IEnumerable types) { if (types == null) throw new ArgumentNullException(nameof(types)); var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); syntaxTree = new SyntaxTree(); var namespaces = new HashSet(); foreach (var type in types) { CancellationToken.ThrowIfCancellationRequested(); if (type.IsNil) throw new ArgumentException("types contains null element"); RequiredNamespaceCollector.CollectNamespaces(type, module, namespaces); } var decompileRun = CreateDecompileRun(namespaces); DoDecompileTypes(types, decompileRun, decompilationContext, syntaxTree); RunTransforms(syntaxTree, decompileRun, decompilationContext); return syntaxTree; } /// /// Decompile the given types. /// /// /// Unlike Decompile(IMemberDefinition[]), this method will add namespace declarations around the type definitions. /// public string DecompileTypesAsString(IEnumerable types) { return SyntaxTreeToString(DecompileTypes(types)); } /// /// Decompile the given type. /// /// /// Unlike Decompile(IMemberDefinition[]), this method will add namespace declarations around the type definition. /// Note that decompiling types from modules other than the main module is not supported. /// public SyntaxTree DecompileType(FullTypeName fullTypeName) { var type = typeSystem.FindType(fullTypeName.TopLevelTypeName).GetDefinition(); if (type == null) throw new InvalidOperationException($"Could not find type definition {fullTypeName} in type system."); if (type.ParentModule != typeSystem.MainModule) throw new NotSupportedException($"Type {fullTypeName} was not found in the module being decompiled, but only in {type.ParentModule.Name}"); var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); var namespaces = new HashSet(); syntaxTree = new SyntaxTree(); RequiredNamespaceCollector.CollectNamespaces(type.MetadataToken, module, namespaces); var decompileRun = CreateDecompileRun(namespaces); DoDecompileTypes(new[] { (TypeDefinitionHandle)type.MetadataToken }, decompileRun, decompilationContext, syntaxTree); RunTransforms(syntaxTree, decompileRun, decompilationContext); return syntaxTree; } /// /// Decompile the given type. /// /// /// Unlike Decompile(IMemberDefinition[]), this method will add namespace declarations around the type definition. /// public string DecompileTypeAsString(FullTypeName fullTypeName) { return SyntaxTreeToString(DecompileType(fullTypeName)); } /// /// Decompile the specified types and/or members. /// public SyntaxTree Decompile(params EntityHandle[] definitions) { return Decompile((IEnumerable)definitions); } /// /// Decompile the specified types and/or members. /// public SyntaxTree Decompile(IEnumerable definitions) { if (definitions == null) throw new ArgumentNullException(nameof(definitions)); syntaxTree = new SyntaxTree(); var namespaces = new HashSet(); foreach (var entity in definitions) { if (entity.IsNil) throw new ArgumentException("definitions contains null element"); RequiredNamespaceCollector.CollectNamespaces(entity, module, namespaces); } var decompileRun = CreateDecompileRun(namespaces); bool first = true; ITypeDefinition parentTypeDef = null; foreach (var entity in definitions) { switch (entity.Kind) { case HandleKind.TypeDefinition: ITypeDefinition typeDef = module.GetDefinition((TypeDefinitionHandle)entity); syntaxTree.Members.Add(DoDecompile(typeDef, decompileRun, new SimpleTypeResolveContext(typeDef))); if (first) { parentTypeDef = typeDef.DeclaringTypeDefinition; } else if (parentTypeDef != null) { parentTypeDef = FindCommonDeclaringTypeDefinition(parentTypeDef, typeDef.DeclaringTypeDefinition); } break; case HandleKind.MethodDefinition: IMethod method = module.GetDefinition((MethodDefinitionHandle)entity); syntaxTree.Members.Add(DoDecompile(method, decompileRun, new SimpleTypeResolveContext(method), null)); if (first) { parentTypeDef = method.DeclaringTypeDefinition; } else if (parentTypeDef != null) { parentTypeDef = FindCommonDeclaringTypeDefinition(parentTypeDef, method.DeclaringTypeDefinition); } break; case HandleKind.FieldDefinition: IField field = module.GetDefinition((FieldDefinitionHandle)entity); syntaxTree.Members.Add(DoDecompile(field, decompileRun, new SimpleTypeResolveContext(field))); parentTypeDef = field.DeclaringTypeDefinition; break; case HandleKind.PropertyDefinition: IProperty property = module.GetDefinition((PropertyDefinitionHandle)entity); syntaxTree.Members.Add(DoDecompile(property, decompileRun, new SimpleTypeResolveContext(property), null)); if (first) { parentTypeDef = property.DeclaringTypeDefinition; } else if (parentTypeDef != null) { parentTypeDef = FindCommonDeclaringTypeDefinition(parentTypeDef, property.DeclaringTypeDefinition); } break; case HandleKind.EventDefinition: IEvent ev = module.GetDefinition((EventDefinitionHandle)entity); syntaxTree.Members.Add(DoDecompile(ev, decompileRun, new SimpleTypeResolveContext(ev))); if (first) { parentTypeDef = ev.DeclaringTypeDefinition; } else if (parentTypeDef != null) { parentTypeDef = FindCommonDeclaringTypeDefinition(parentTypeDef, ev.DeclaringTypeDefinition); } break; default: throw new NotSupportedException(entity.Kind.ToString()); } first = false; } RunTransforms(syntaxTree, decompileRun, parentTypeDef != null ? new SimpleTypeResolveContext(parentTypeDef) : new SimpleTypeResolveContext(typeSystem.MainModule)); return syntaxTree; } ITypeDefinition FindCommonDeclaringTypeDefinition(ITypeDefinition a, ITypeDefinition b) { if (a == null || b == null) return null; var declaringTypes = a.GetDeclaringTypeDefinitions(); var set = new HashSet(b.GetDeclaringTypeDefinitions()); return declaringTypes.FirstOrDefault(set.Contains); } /// /// Decompile the specified types and/or members. /// public string DecompileAsString(params EntityHandle[] definitions) { return SyntaxTreeToString(Decompile(definitions)); } /// /// Decompile the specified types and/or members. /// public string DecompileAsString(IEnumerable definitions) { return SyntaxTreeToString(Decompile(definitions)); } readonly Dictionary partialTypes = new(); public void AddPartialTypeDefinition(PartialTypeInfo info) { if (!partialTypes.TryGetValue(info.DeclaringTypeDefinitionHandle, out var existingInfo)) { partialTypes.Add(info.DeclaringTypeDefinitionHandle, info); } else { existingInfo.AddDeclaredMembers(info); } } IEnumerable AddInterfaceImplHelpers( EntityDeclaration memberDecl, IMethod method, TypeSystemAstBuilder astBuilder) { if (!memberDecl.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole).IsNull) { yield break; // cannot create forwarder for existing explicit interface impl } if (method.IsStatic) { yield break; // cannot create forwarder for static interface impl } if (memberDecl.HasModifier(Modifiers.Extern)) { yield break; // cannot create forwarder for extern method } var genericContext = new Decompiler.TypeSystem.GenericContext(method); var methodHandle = (MethodDefinitionHandle)method.MetadataToken; foreach (var h in methodHandle.GetMethodImplementations(metadata)) { var mi = metadata.GetMethodImplementation(h); IMethod m = module.ResolveMethod(mi.MethodDeclaration, genericContext); if (m == null || m.DeclaringType.Kind != TypeKind.Interface) continue; var methodDecl = new MethodDeclaration(); methodDecl.ReturnType = memberDecl.ReturnType.Clone(); methodDecl.PrivateImplementationType = astBuilder.ConvertType(m.DeclaringType); methodDecl.Name = m.Name; methodDecl.TypeParameters.AddRange(memberDecl.GetChildrenByRole(Roles.TypeParameter) .Select(n => (TypeParameterDeclaration)n.Clone())); methodDecl.Parameters.AddRange(memberDecl.GetChildrenByRole(Roles.Parameter).Select(n => n.Clone())); // Constraints are not copied because explicit interface implementations cannot have constraints. CS0460 methodDecl.Body = new BlockStatement(); methodDecl.Body.AddChild(new Comment( "ILSpy generated this explicit interface implementation from .override directive in " + memberDecl.Name), Roles.Comment); var forwardingCall = new InvocationExpression(new MemberReferenceExpression(new ThisReferenceExpression(), memberDecl.Name, methodDecl.TypeParameters.Select(tp => new SimpleType(tp.Name))), methodDecl.Parameters.Select(ForwardParameter) ); if (m.ReturnType.IsKnownType(KnownTypeCode.Void)) { methodDecl.Body.Add(new ExpressionStatement(forwardingCall)); } else { methodDecl.Body.Add(new ReturnStatement(forwardingCall)); } yield return methodDecl; } } Expression ForwardParameter(ParameterDeclaration p) { switch (p.ParameterModifier) { case ReferenceKind.None: return new IdentifierExpression(p.Name); case ReferenceKind.Ref: case ReferenceKind.RefReadOnly: return new DirectionExpression(FieldDirection.Ref, new IdentifierExpression(p.Name)); case ReferenceKind.Out: return new DirectionExpression(FieldDirection.Out, new IdentifierExpression(p.Name)); case ReferenceKind.In: return new DirectionExpression(FieldDirection.In, new IdentifierExpression(p.Name)); default: throw new NotSupportedException(); } } /// /// Sets new modifier if the member hides some other member from a base type. /// /// The node of the member which new modifier state should be determined. void SetNewModifier(EntityDeclaration member) { var entity = (IEntity)member.GetSymbol(); var lookup = new MemberLookup(entity.DeclaringTypeDefinition, entity.ParentModule); var baseTypes = entity.DeclaringType.GetNonInterfaceBaseTypes().Where(t => entity.DeclaringType != t).ToList(); // A constant, field, property, event, or type introduced in a class or struct hides all base class members with the same name. bool hideBasedOnSignature = !(entity is ITypeDefinition || entity.SymbolKind == SymbolKind.Field || entity.SymbolKind == SymbolKind.Property || entity.SymbolKind == SymbolKind.Event); const GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; if (HidesMemberOrTypeOfBaseType()) member.Modifiers |= Modifiers.New; bool HidesMemberOrTypeOfBaseType() { var parameterListComparer = ParameterListComparer.WithOptions(includeModifiers: true); foreach (IType baseType in baseTypes) { if (!hideBasedOnSignature) { if (baseType.GetNestedTypes(t => t.Name == entity.Name && lookup.IsAccessible(t, true), options).Any()) return true; if (baseType.GetMembers(m => m.Name == entity.Name && m.SymbolKind != SymbolKind.Indexer && lookup.IsAccessible(m, true), options).Any()) return true; } else { if (entity.SymbolKind == SymbolKind.Indexer) { // An indexer introduced in a class or struct hides all base class indexers with the same signature (parameter count and types). if (baseType.GetProperties(p => p.SymbolKind == SymbolKind.Indexer && lookup.IsAccessible(p, true)) .Any(p => parameterListComparer.Equals(((IProperty)entity).Parameters, p.Parameters))) { return true; } } else if (entity.SymbolKind == SymbolKind.Method) { // A method introduced in a class or struct hides all non-method base class members with the same name, and all // base class methods with the same signature (method name and parameter count, modifiers, and types). if (baseType.GetMembers(m => m.SymbolKind != SymbolKind.Indexer && m.SymbolKind != SymbolKind.Constructor && m.SymbolKind != SymbolKind.Destructor && m.Name == entity.Name && lookup.IsAccessible(m, true)) .Any(m => m.SymbolKind != SymbolKind.Method || (((IMethod)entity).TypeParameters.Count == ((IMethod)m).TypeParameters.Count && parameterListComparer.Equals(((IMethod)entity).Parameters, ((IMethod)m).Parameters)))) { return true; } } } } return false; } } void FixParameterNames(EntityDeclaration entity) { int i = 0; foreach (var parameter in entity.GetChildrenByRole(Roles.Parameter)) { if (string.IsNullOrWhiteSpace(parameter.Name) && !parameter.Type.IsArgList()) { // needs to be consistent with logic in ILReader.CreateILVariable parameter.Name = "P_" + i; } i++; } } EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentTypeDefinition == typeDef); var watch = System.Diagnostics.Stopwatch.StartNew(); var entityMap = new MultiDictionary(); var workList = new Queue(); TypeSystemAstBuilder typeSystemAstBuilder; try { typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var entityDecl = typeSystemAstBuilder.ConvertEntity(typeDef); if (entityDecl is DelegateDeclaration delegateDeclaration) { // Fix empty parameter names in delegate declarations FixParameterNames(delegateDeclaration); } var typeDecl = entityDecl as TypeDeclaration; if (typeDecl == null) { // e.g. DelegateDeclaration return entityDecl; } bool isRecord = typeDef.Kind switch { TypeKind.Class => settings.RecordClasses && typeDef.IsRecord, TypeKind.Struct => settings.RecordStructs && typeDef.IsRecord, _ => false, }; RecordDecompiler recordDecompiler = isRecord ? new RecordDecompiler(typeSystem, typeDef, settings, CancellationToken) : null; if (recordDecompiler != null) decompileRun.RecordDecompilers.Add(typeDef, recordDecompiler); // With C# 9 records, the relative order of fields and properties matters: IEnumerable fieldsAndProperties = isRecord ? recordDecompiler.FieldsAndProperties : typeDef.Fields.Concat(typeDef.Properties); // For COM interop scenarios, the relative order of virtual functions/properties matters: IEnumerable allOrderedMembers = RequiresNativeOrdering(typeDef) ? GetMembersWithNativeOrdering(typeDef) : fieldsAndProperties.Concat(typeDef.Events).Concat(typeDef.Methods); var allOrderedEntities = typeDef.NestedTypes.Concat(allOrderedMembers).ToArray(); if (!partialTypes.TryGetValue((TypeDefinitionHandle)typeDef.MetadataToken, out var partialTypeInfo)) { partialTypeInfo = null; } if (settings.ExtensionMembers) { foreach (var group in typeDef.ExtensionInfo?.GetGroups() ?? []) { var ext = new ExtensionDeclaration(); ITypeParameter[] typeParameters = group.Key.TypeParameters; var subst = new TypeParameterSubstitution(typeParameters, null); ext.TypeParameters.AddRange(typeParameters.Select(tp => typeSystemAstBuilder.ConvertTypeParameter(tp))); var marker = group.Key.Marker.Specialize(subst); ext.ReceiverParameters.Add(typeSystemAstBuilder.ConvertParameter(marker.Parameters.Single())); ext.Constraints.AddRange(typeParameters.Select(c => typeSystemAstBuilder.ConvertTypeParameterConstraint(c))); foreach (var member in group) { IMember extMember = member.ExtensionMember.Specialize(subst); if (member.ExtensionMember.IsAccessor) { extMember = member.ExtensionMember.AccessorOwner; } if (entityMap.Contains(extMember) || extMember.MetadataToken.IsNil) { // Member is already decompiled. continue; } EntityDeclaration extMemberDecl = DoDecompileExtensionMember(extMember, typeDef.ExtensionInfo, decompileRun, decompilationContext); ext.Members.Add(extMemberDecl); entityMap.Add(extMember, extMemberDecl); } typeDecl.Members.Add(ext); } } // Decompile members that are not compiler-generated. foreach (var entity in allOrderedEntities) { if (entity.MetadataToken.IsNil || MemberIsHidden(module.MetadataFile, entity.MetadataToken, settings)) { continue; } DoDecompileMember(entity, recordDecompiler, partialTypeInfo, typeDef.ExtensionInfo); } // Decompile compiler-generated members that are still needed. while (workList.Count > 0) { var entity = workList.Dequeue(); if (entityMap.Contains(entity) || entity.MetadataToken.IsNil) { // Member is already decompiled. continue; } DoDecompileMember(entity, recordDecompiler, partialTypeInfo, typeDef.ExtensionInfo); } // Add all decompiled members to syntax tree in the correct order. foreach (var member in allOrderedEntities) { typeDecl.Members.AddRange(entityMap[member]); } if (typeDecl.Members.OfType().Any(idx => idx.PrivateImplementationType.IsNull)) { // Remove the [DefaultMember] attribute if the class contains indexers RemoveAttribute(typeDecl, KnownAttribute.DefaultMember); } if (partialTypeInfo != null) { typeDecl.Modifiers |= Modifiers.Partial; } if (settings.IntroduceRefModifiersOnStructs) { RemoveObsoleteAttribute(typeDecl, "Types with embedded references are not supported in this version of your compiler."); RemoveCompilerFeatureRequiredAttribute(typeDecl, "RefStructs"); } if (settings.RequiredMembers) { RemoveAttribute(typeDecl, KnownAttribute.Required); } if (typeDecl.ClassType == ClassType.Enum) { Debug.Assert(typeDef.Kind == TypeKind.Enum); EnumValueDisplayMode displayMode = DetectBestEnumValueDisplayMode(typeDef, module.MetadataFile); switch (displayMode) { case EnumValueDisplayMode.FirstOnly: foreach (var enumMember in typeDecl.Members.OfType().Skip(1)) { enumMember.Initializer = null; } break; case EnumValueDisplayMode.None: foreach (var enumMember in typeDecl.Members.OfType()) { enumMember.Initializer = null; if (enumMember.GetSymbol() is IField f && f.GetConstantValue() == null) { typeDecl.InsertChildBefore(enumMember, new Comment(" error: enumerator has no value"), Roles.Comment); } } break; case EnumValueDisplayMode.All: // nothing needs to be changed. break; case EnumValueDisplayMode.AllHex: foreach (var enumMember in typeDecl.Members.OfType()) { var constantValue = (enumMember.GetSymbol() as IField).GetConstantValue(); if (constantValue == null || enumMember.Initializer is not PrimitiveExpression pe) { continue; } long initValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false); if (initValue >= 10) { pe.Format = LiteralFormat.HexadecimalNumber; } } break; default: throw new ArgumentOutOfRangeException(); } foreach (var item in typeDecl.Members) { if (item is not EnumMemberDeclaration) { typeDecl.InsertChildBefore(item, new Comment(" error: nested types are not permitted in C#."), Roles.Comment); } } } return typeDecl; } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, typeDef, innerException); } finally { watch.Stop(); Instrumentation.DecompilerEventSource.Log.DoDecompileTypeDefinition(typeDef.FullName, watch.ElapsedMilliseconds); } void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, PartialTypeInfo partialType, ExtensionInfo extensionInfo) { if (partialType != null && partialType.IsDeclaredMember(entity.MetadataToken)) { return; } if (settings.ExtensionMembers && extensionInfo != null) { switch (entity) { case ITypeDefinition td when extensionInfo.IsExtensionGroupingType(td): return; case IMethod m when extensionInfo.InfoOfImplementationMember(m).HasValue: return; } } EntityDeclaration entityDecl; switch (entity) { case IField field: if (typeDef.Kind == TypeKind.Enum && !field.IsConst) { return; } if (TransformFieldAndConstructorInitializers.IsGeneratedPrimaryConstructorBackingField(field)) { return; } entityDecl = DoDecompile(field, decompileRun, decompilationContext.WithCurrentMember(field)); entityMap.Add(field, entityDecl); break; case IProperty property: if (recordDecompiler?.PropertyIsGenerated(property) == true) { return; } entityDecl = DoDecompile(property, decompileRun, decompilationContext.WithCurrentMember(property), null); entityMap.Add(property, entityDecl); break; case IMethod method: if (recordDecompiler?.MethodIsGenerated(method) == true) { return; } entityDecl = DoDecompile(method, decompileRun, decompilationContext.WithCurrentMember(method), null); entityMap.Add(method, entityDecl); foreach (var helper in AddInterfaceImplHelpers(entityDecl, method, typeSystemAstBuilder)) { entityMap.Add(method, helper); } break; case IEvent @event: entityDecl = DoDecompile(@event, decompileRun, decompilationContext.WithCurrentMember(@event)); entityMap.Add(@event, entityDecl); break; case ITypeDefinition type: entityDecl = DoDecompile(type, decompileRun, decompilationContext.WithCurrentTypeDefinition(type)); SetNewModifier(entityDecl); entityMap.Add(type, entityDecl); break; default: throw new ArgumentOutOfRangeException("Unexpected member type"); } foreach (var node in entityDecl.Descendants) { var rr = node.GetResolveResult(); if (rr is MemberResolveResult mrr && mrr.Member.DeclaringTypeDefinition == typeDef && !(mrr.Member is IMethod { IsLocalFunction: true })) { workList.Enqueue(mrr.Member); } else if (rr is TypeResolveResult trr && trr.Type.GetDefinition()?.DeclaringTypeDefinition == typeDef) { workList.Enqueue(trr.Type.GetDefinition()); } } } } private EntityDeclaration DoDecompileExtensionMember(IMember extMember, ExtensionInfo info, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { switch (extMember) { case IProperty p: var prop = DoDecompile(p, decompileRun, decompilationContext.WithCurrentMember(p), info); RemoveAttribute(prop, KnownAttribute.ExtensionMarker); if (p.Getter != null) { RemoveAttribute(prop.GetChildByRole(PropertyDeclaration.GetterRole), KnownAttribute.ExtensionMarker); } if (p.Setter != null) { RemoveAttribute(prop.GetChildByRole(PropertyDeclaration.SetterRole), KnownAttribute.ExtensionMarker); } return prop; case IMethod m: var meth = DoDecompile(m, decompileRun, decompilationContext.WithCurrentMember(m), info); RemoveAttribute(meth, KnownAttribute.ExtensionMarker); return meth; } throw new NotSupportedException($"Extension member {extMember} is not supported for decompilation."); } EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, MetadataFile module) { if (typeDef.HasAttribute(KnownAttribute.Flags)) return EnumValueDisplayMode.AllHex; bool first = true; long firstValue = 0, previousValue = 0; bool allPowersOfTwo = true; bool allConsecutive = true; foreach (var field in typeDef.Fields) { if (MemberIsHidden(module, field.MetadataToken, settings)) continue; object constantValue = field.GetConstantValue(); if (constantValue == null) continue; long currentValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false); allConsecutive = allConsecutive && (first || previousValue + 1 == currentValue); // N & (N - 1) == 0, iff N is a power of 2, for all N != 0. // We define that 0 is a power of 2 in the context of enum values. allPowersOfTwo = allPowersOfTwo && unchecked(currentValue & (currentValue - 1)) == 0; if (first) { firstValue = currentValue; first = false; } else if (currentValue <= previousValue) { // If the values are out of order, we fallback to displaying all values. return EnumValueDisplayMode.All; } else if (!allConsecutive && !allPowersOfTwo) { // We already know that the values are neither consecutive nor all powers of 2, // so we can abort, and just display all values as-is. return EnumValueDisplayMode.All; } previousValue = currentValue; } if (allPowersOfTwo) { if (previousValue > 8) { // If all values are powers of 2 and greater 8, display all enum values, but use hex. return EnumValueDisplayMode.AllHex; } else if (!allConsecutive) { // If all values are powers of 2, display all enum values. return EnumValueDisplayMode.All; } } if (settings.AlwaysShowEnumMemberValues) { // The user always wants to see all enum values, but we know hex is not necessary. return EnumValueDisplayMode.All; } // We know that all values are consecutive, so if the first value is not 0 // display the first enum value only. return firstValue == 0 ? EnumValueDisplayMode.None : EnumValueDisplayMode.FirstOnly; } EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo) { Debug.Assert(decompilationContext.CurrentMember == method); var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); var methodDecl = typeSystemAstBuilder.ConvertEntity(method); int lastDot = method.Name.LastIndexOf('.'); if (methodDecl is not OperatorDeclaration && method.IsExplicitInterfaceImplementation && lastDot >= 0) { methodDecl.Name = method.Name.Substring(lastDot + 1); } FixParameterNames(methodDecl); var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule.MetadataFile, (MethodDefinitionHandle)method.MetadataToken)) { // if local functions are not active and we're dealing with a local function, // reduce the visibility of the method to private, // otherwise this leads to compile errors because the display classes have lesser accessibility. // Note: removing and then adding the static modifier again is necessary to set the private modifier before all other modifiers. methodDecl.Modifiers &= ~(Modifiers.Internal | Modifiers.Static); methodDecl.Modifiers |= Modifiers.Private | (method.IsStatic ? Modifiers.Static : 0); } if (methodDefinition.HasBody()) { DecompileBody(method, methodDecl, decompileRun, decompilationContext, extensionInfo); } else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) { methodDecl.Modifiers |= Modifiers.Extern; } if (method.SymbolKind == SymbolKind.Method && !method.IsExplicitInterfaceImplementation && methodDefinition.HasFlag(System.Reflection.MethodAttributes.Virtual) == methodDefinition.HasFlag(System.Reflection.MethodAttributes.NewSlot)) { SetNewModifier(methodDecl); } else if (!method.IsStatic && !method.IsExplicitInterfaceImplementation && !method.IsVirtual && method.IsOverride && InheritanceHelper.GetBaseMember(method) == null && IsTypeHierarchyKnown(method.DeclaringType)) { methodDecl.Modifiers &= ~Modifiers.Override; if (!method.DeclaringTypeDefinition.IsSealed) { methodDecl.Modifiers |= Modifiers.Virtual; } } if (IsCovariantReturnOverride(method)) { RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides); methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); methodDecl.Modifiers |= Modifiers.Override; } if (method.IsConstructor && settings.RequiredMembers && RemoveCompilerFeatureRequiredAttribute(methodDecl, "RequiredMembers")) { RemoveObsoleteAttribute(methodDecl, "Constructors of types with required members are not supported in this version of your compiler."); } return methodDecl; bool IsTypeHierarchyKnown(IType type) { var definition = type.GetDefinition(); if (definition == null) { return false; } if (decompileRun.TypeHierarchyIsKnown.TryGetValue(definition, out var value)) return value; value = method.DeclaringType.GetNonInterfaceBaseTypes().All(t => t.Kind != TypeKind.Unknown); decompileRun.TypeHierarchyIsKnown.Add(definition, value); return value; } } finally { watch.Stop(); Instrumentation.DecompilerEventSource.Log.DoDecompileMethod(method.FullName, watch.ElapsedMilliseconds); } } private bool IsCovariantReturnOverride(IEntity entity) { if (!settings.CovariantReturns) return false; if (!entity.HasAttribute(KnownAttribute.PreserveBaseOverrides)) return false; return true; } internal static bool IsWindowsFormsInitializeComponentMethod(IMethod method) { return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control"); } void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo) { try { var ilReader = new ILReader(typeSystem.MainModule) { UseDebugSymbols = settings.UseDebugSymbols, UseRefLocalsForAccurateOrderOfEvaluation = settings.UseRefLocalsForAccurateOrderOfEvaluation, DebugInfo = DebugInfoProvider }; int parameterOffset = 0; if (extensionInfo != null) { if (!method.IsStatic) parameterOffset = 1; // implementation method has an additional receiver parameter method = extensionInfo.InfoOfExtensionMember((IMethod)method.MemberDefinition).Value.ImplementationMethod; } var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); var body = BlockStatement.Null; MethodBodyBlock methodBody; try { methodBody = module.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress); } catch (BadImageFormatException ex) { body = new BlockStatement(); body.AddChild(new Comment("Invalid MethodBodyBlock: " + ex.Message), Roles.Comment); // insert explicit rbrace token to make the comment appear within the braces body.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.RBrace), Roles.RBrace); entityDecl.AddChild(body, Roles.Body); return; } var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken); function.CheckInvariant(ILPhase.Normal); AddAnnotationsToDeclaration(method, entityDecl, function, parameterOffset); var localSettings = settings.Clone(); if (IsWindowsFormsInitializeComponentMethod(method)) { localSettings.UseImplicitMethodGroupConversion = false; localSettings.UsingDeclarations = false; localSettings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls = true; localSettings.NamedArguments = false; localSettings.AlwaysQualifyMemberReferences = true; } var context = new ILTransformContext(function, typeSystem, DebugInfoProvider, localSettings) { CancellationToken = CancellationToken, DecompileRun = decompileRun }; foreach (var transform in ilTransforms) { CancellationToken.ThrowIfCancellationRequested(); transform.Run(function, context); function.CheckInvariant(ILPhase.Normal); // When decompiling definitions only, we can cancel decompilation of all steps // after yield and async detection, because only those are needed to properly set // IsAsync/IsIterator flags on ILFunction. if (!localSettings.DecompileMemberBodies && transform is AsyncAwaitDecompiler) break; } // Generate C# AST only if bodies should be displayed. if (localSettings.DecompileMemberBodies) { AddDefinesForConditionalAttributes(function, decompileRun); var statementBuilder = new StatementBuilder( typeSystem, decompilationContext, function, localSettings, decompileRun, CancellationToken ); body = statementBuilder.ConvertAsBlock(function.Body); Comment prev = null; foreach (string warning in function.Warnings) { body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); } entityDecl.AddChild(body, Roles.Body); } CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies); } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, method, innerException); } } internal static void AddAnnotationsToDeclaration(IMethod method, EntityDeclaration entityDecl, ILFunction function, int parameterOffset = 0) { int i = parameterOffset; var parameters = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); foreach (var parameter in entityDecl.GetChildrenByRole(Roles.Parameter)) { if (parameters.TryGetValue(i, out var v)) parameter.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type)); i++; } entityDecl.AddAnnotation(function); } internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true) { if (function.IsIterator) { if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { body.Add(new YieldBreakStatement()); } if (function.IsAsync) { RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine); } else { RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); } if (function.StateMachineCompiledWithMono) { RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); } if (function.StateMachineCompiledWithLegacyVisualBasic) { RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); if (function.Method?.IsAccessor == true && entityDecl.Parent is EntityDeclaration parentDecl) { RemoveAttribute(parentDecl, KnownAttribute.DebuggerStepThrough); } } } if (function.IsAsync) { entityDecl.Modifiers |= Modifiers.Async; RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); } } internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType) { bool found = false; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); if (symbol is ITypeDefinition td && td.FullTypeName == attributeType.GetTypeName()) { attr.Remove(); found = true; } } if (section.Attributes.Count == 0) { section.Remove(); } } return found; } internal static bool RemoveCompilerFeatureRequiredAttribute(EntityDeclaration entityDecl, string feature) { bool found = false; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); if (symbol is ITypeDefinition td && td.FullTypeName == KnownAttribute.CompilerFeatureRequired.GetTypeName() && attr.Arguments.Count == 1 && attr.Arguments.SingleOrDefault() is PrimitiveExpression pe && pe.Value is string s && s == feature) { attr.Remove(); found = true; } } if (section.Attributes.Count == 0) { section.Remove(); } } return found; } internal static bool RemoveObsoleteAttribute(EntityDeclaration entityDecl, string message) { bool found = false; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); if (symbol is ITypeDefinition td && td.FullTypeName == KnownAttribute.Obsolete.GetTypeName() && attr.Arguments.Count >= 1 && attr.Arguments.First() is PrimitiveExpression pe && pe.Value is string s && s == message) { attr.Remove(); found = true; } } if (section.Attributes.Count == 0) { section.Remove(); } } return found; } bool FindAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType, out Syntax.Attribute attribute) { attribute = null; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); if (symbol is ITypeDefinition td && td.FullTypeName == attributeType.GetTypeName()) { attribute = attr; return true; } } } return false; } void AddDefinesForConditionalAttributes(ILFunction function, DecompileRun decompileRun) { foreach (var call in function.Descendants.OfType()) { var attr = call.Method.GetAttribute(KnownAttribute.Conditional, inherit: true); var symbolName = attr?.FixedArguments.FirstOrDefault().Value as string; if (symbolName == null || !decompileRun.DefinedSymbols.Add(symbolName)) continue; syntaxTree.InsertChildAfter(null, new PreProcessorDirective(PreProcessorDirectiveType.Define, symbolName), Roles.PreProcessorDirective); } } EntityDeclaration DoDecompile(IField field, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == field); var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); if (decompilationContext.CurrentTypeDefinition.Kind == TypeKind.Enum && field.IsConst) { var enumDec = new EnumMemberDeclaration { Name = field.Name }; object constantValue = field.GetConstantValue(); if (constantValue != null) { enumDec.Initializer = typeSystemAstBuilder.ConvertConstantValue(decompilationContext.CurrentTypeDefinition.EnumUnderlyingType, constantValue); } enumDec.Attributes.AddRange(field.GetAttributes().Select(a => new AttributeSection(typeSystemAstBuilder.ConvertAttribute(a)))); enumDec.AddAnnotation(new MemberResolveResult(null, field)); return enumDec; } bool isMathPIOrE = ((field.Name == "PI" || field.Name == "E") && (field.DeclaringType.FullName == "System.Math" || field.DeclaringType.FullName == "System.MathF")); typeSystemAstBuilder.UseSpecialConstants = !(field.DeclaringType.Equals(field.ReturnType) || isMathPIOrE); var fieldDecl = typeSystemAstBuilder.ConvertEntity(field); SetNewModifier(fieldDecl); if (settings.RequiredMembers && RemoveAttribute(fieldDecl, KnownAttribute.Required)) { fieldDecl.Modifiers |= Modifiers.Required; } if (settings.FixedBuffers && IsFixedField(field, out var elementType, out var elementCount)) { var fixedFieldDecl = new FixedFieldDeclaration(); fieldDecl.Attributes.MoveTo(fixedFieldDecl.Attributes); fixedFieldDecl.Modifiers = fieldDecl.Modifiers; fixedFieldDecl.ReturnType = typeSystemAstBuilder.ConvertType(elementType); fixedFieldDecl.Variables.Add(new FixedVariableInitializer(field.Name, new PrimitiveExpression(elementCount))); fixedFieldDecl.Variables.Single().CopyAnnotationsFrom(((FieldDeclaration)fieldDecl).Variables.Single()); fixedFieldDecl.CopyAnnotationsFrom(fieldDecl); RemoveAttribute(fixedFieldDecl, KnownAttribute.FixedBuffer); return fixedFieldDecl; } var fieldDefinition = metadata.GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken); if (fieldDefinition.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) { // Field data as specified in II.16.3.1 of ECMA-335 6th edition: // .data I_X = int32(123) // .field public static int32 _x at I_X string message; try { var initVal = fieldDefinition.GetInitialValue(module.MetadataFile, TypeSystem); message = string.Format(" Not supported: data({0}) ", BitConverter.ToString(initVal.ReadBytes(initVal.RemainingBytes)).Replace('-', ' ')); } catch (BadImageFormatException ex) { message = ex.Message; } ((FieldDeclaration)fieldDecl).Variables.Single().AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); } return fieldDecl; } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, field, innerException); } finally { watch.Stop(); Instrumentation.DecompilerEventSource.Log.DoDecompileField(field.FullName, watch.ElapsedMilliseconds); } } internal static bool IsFixedField(IField field, out IType type, out int elementCount) { type = null; elementCount = 0; IAttribute attr = field.GetAttribute(KnownAttribute.FixedBuffer); if (attr != null && attr.FixedArguments.Length == 2) { if (attr.FixedArguments[0].Value is IType trr && attr.FixedArguments[1].Value is int length) { type = trr; elementCount = length; return true; } } return false; } EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo) { Debug.Assert(decompilationContext.CurrentMember == property); var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); EntityDeclaration propertyDecl = typeSystemAstBuilder.ConvertEntity(property); if (property.IsExplicitInterfaceImplementation && !property.IsIndexer) { int lastDot = property.Name.LastIndexOf('.'); propertyDecl.Name = property.Name.Substring(lastDot + 1); } FixParameterNames(propertyDecl); Accessor getter, setter; if (propertyDecl is PropertyDeclaration) { getter = ((PropertyDeclaration)propertyDecl).Getter; setter = ((PropertyDeclaration)propertyDecl).Setter; } else { getter = ((IndexerDeclaration)propertyDecl).Getter; setter = ((IndexerDeclaration)propertyDecl).Setter; } bool getterHasBody = property.CanGet && property.Getter.HasBody; bool setterHasBody = property.CanSet && property.Setter.HasBody; if (getterHasBody) { DecompileBody(property.Getter, getter, decompileRun, decompilationContext, extensionInfo); } if (setterHasBody) { DecompileBody(property.Setter, setter, decompileRun, decompilationContext, extensionInfo); } if (!getterHasBody && !setterHasBody && !property.IsAbstract && property.DeclaringType.Kind != TypeKind.Interface) { propertyDecl.Modifiers |= Modifiers.Extern; } var accessorHandle = (MethodDefinitionHandle)(property.Getter ?? property.Setter).MetadataToken; var accessor = metadata.GetMethodDefinition(accessorHandle); if (!accessorHandle.GetMethodImplementations(metadata).Any() && accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot)) { SetNewModifier(propertyDecl); } if (property.CanGet && IsCovariantReturnOverride(property.Getter)) { RemoveAttribute(getter, KnownAttribute.PreserveBaseOverrides); propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); propertyDecl.Modifiers |= Modifiers.Override; } if (settings.RequiredMembers && RemoveAttribute(propertyDecl, KnownAttribute.Required)) { propertyDecl.Modifiers |= Modifiers.Required; } return propertyDecl; } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, property, innerException); } finally { watch.Stop(); Instrumentation.DecompilerEventSource.Log.DoDecompileProperty(property.FullName, watch.ElapsedMilliseconds); } } EntityDeclaration DoDecompile(IEvent ev, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == ev); var watch = System.Diagnostics.Stopwatch.StartNew(); try { bool adderHasBody = ev.CanAdd && ev.AddAccessor.HasBody; bool removerHasBody = ev.CanRemove && ev.RemoveAccessor.HasBody; var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); typeSystemAstBuilder.UseCustomEvents = ev.DeclaringTypeDefinition.Kind != TypeKind.Interface || ev.IsExplicitInterfaceImplementation || adderHasBody || removerHasBody; var eventDecl = typeSystemAstBuilder.ConvertEntity(ev); int lastDot = ev.Name.LastIndexOf('.'); if (ev.IsExplicitInterfaceImplementation) { eventDecl.Name = ev.Name.Substring(lastDot + 1); } if (adderHasBody) { DecompileBody(ev.AddAccessor, ((CustomEventDeclaration)eventDecl).AddAccessor, decompileRun, decompilationContext, null); } if (removerHasBody) { DecompileBody(ev.RemoveAccessor, ((CustomEventDeclaration)eventDecl).RemoveAccessor, decompileRun, decompilationContext, null); } if (!adderHasBody && !removerHasBody && !ev.IsAbstract && ev.DeclaringType.Kind != TypeKind.Interface) { eventDecl.Modifiers |= Modifiers.Extern; } var accessor = metadata.GetMethodDefinition((MethodDefinitionHandle)(ev.AddAccessor ?? ev.RemoveAccessor).MetadataToken); if (accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot)) { SetNewModifier(eventDecl); } return eventDecl; } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, ev, innerException); } finally { watch.Stop(); Instrumentation.DecompilerEventSource.Log.DoDecompileEvent(ev.FullName, watch.ElapsedMilliseconds); } } #region Sequence Points /// /// Creates sequence points for the given syntax tree. /// /// This only works correctly when the nodes in the syntax tree have line/column information. /// public Dictionary> CreateSequencePoints(SyntaxTree syntaxTree) { SequencePointBuilder spb = new SequencePointBuilder(); syntaxTree.AcceptVisitor(spb); return spb.GetSequencePoints(); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.CSharp { public enum LanguageVersion { CSharp1 = 1, CSharp2 = 2, CSharp3 = 3, CSharp4 = 4, CSharp5 = 5, CSharp6 = 6, CSharp7 = 7, CSharp7_1 = 701, CSharp7_2 = 702, CSharp7_3 = 703, CSharp8_0 = 800, CSharp9_0 = 900, CSharp10_0 = 1000, CSharp11_0 = 1100, CSharp12_0 = 1200, CSharp13_0 = 1300, CSharp14_0 = 1400, Preview = 1400, Latest = 0x7FFFFFFF } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/CallBuilder.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { struct CallBuilder { struct ExpectedTargetDetails { public OpCode CallOpCode; public bool NeedsBoxingConversion; } struct ArgumentList { public TranslatedExpression[] Arguments; public IParameter[] ExpectedParameters; public string[] ParameterNames; public string[] ArgumentNames; public int FirstOptionalArgumentIndex; public BitSet IsPrimitiveValue; public IReadOnlyList ArgumentToParameterMap; public bool AddNamesToPrimitiveValues; public bool UseImplicitlyTypedOut; public bool IsExpandedForm; public int Length => Arguments.Length; private int GetActualArgumentCount() { if (FirstOptionalArgumentIndex < 0) return Arguments.Length; return FirstOptionalArgumentIndex; } public string[] GetArgumentNames(int skipCount = 0) { string[] argumentNames = ArgumentNames; if (AddNamesToPrimitiveValues && IsPrimitiveValue.Any() && !IsExpandedForm && !ParameterNames.Any(string.IsNullOrEmpty)) { Debug.Assert(skipCount == 0); if (argumentNames == null) { argumentNames = new string[Arguments.Length]; } for (int i = 0; i < Arguments.Length; i++) { if (IsPrimitiveValue[i] && argumentNames[i] == null) { argumentNames[i] = ParameterNames[i]; } } } return argumentNames; } public IList GetArgumentResolveResults(int skipCount = 0) { var expectedParameters = ExpectedParameters; var useImplicitlyTypedOut = UseImplicitlyTypedOut; return Arguments .SelectWithIndex(GetResolveResult) .Skip(skipCount) .Take(GetActualArgumentCount()) .ToArray(); ResolveResult GetResolveResult(int index, TranslatedExpression expression) { var param = expectedParameters[index]; if (useImplicitlyTypedOut && param.ReferenceKind == ReferenceKind.Out && expression.Type is ByReferenceType brt) return new OutVarResolveResult(brt.ElementType); return expression.ResolveResult; } } public IList GetArgumentResolveResultsDirect(int skipCount = 0) { return Arguments .Skip(skipCount) .Take(GetActualArgumentCount()) .Select(a => a.ResolveResult) .ToArray(); } public IEnumerable GetArgumentExpressions(int skipCount = 0) { var argumentNames = GetArgumentNames(skipCount); int argumentCount = GetActualArgumentCount(); var useImplicitlyTypedOut = UseImplicitlyTypedOut; if (argumentNames == null) { return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => AddAnnotations(arg.Expression)); } else { Debug.Assert(skipCount == 0); return Arguments.Take(argumentCount).Zip(argumentNames.Take(argumentCount), (arg, name) => { if (name == null) return AddAnnotations(arg.Expression); else return new NamedArgumentExpression(name, AddAnnotations(arg.Expression)); }); } Expression AddAnnotations(Expression expression) { if (!useImplicitlyTypedOut) return expression; if (expression.GetResolveResult() is ByReferenceResolveResult { ReferenceKind: ReferenceKind.Out } brrr) { expression.AddAnnotation(UseImplicitlyTypedOutAnnotation.Instance); } return expression; } } public bool CanInferAnonymousTypePropertyNamesFromArguments() { for (int i = 0; i < Arguments.Length; i++) { string inferredName; switch (Arguments[i].Expression) { case IdentifierExpression identifier: inferredName = identifier.Identifier; break; case MemberReferenceExpression member: inferredName = member.MemberName; break; default: inferredName = null; break; } if (inferredName != ExpectedParameters[i].Name) { return false; } } return true; } [Conditional("DEBUG")] public void CheckNoNamedOrOptionalArguments() { Debug.Assert(ArgumentToParameterMap == null && ArgumentNames == null && FirstOptionalArgumentIndex < 0); } } readonly DecompilerSettings settings; readonly ExpressionBuilder expressionBuilder; readonly CSharpResolver resolver; readonly IDecompilerTypeSystem typeSystem; public CallBuilder(ExpressionBuilder expressionBuilder, IDecompilerTypeSystem typeSystem, DecompilerSettings settings) { this.expressionBuilder = expressionBuilder; this.resolver = expressionBuilder.resolver; this.settings = settings; this.typeSystem = typeSystem; } public TranslatedExpression Build(CallInstruction inst, IType typeHint = null) { if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.MatchDelegateConstruction(newobj, out _, out _, out _)) { return HandleDelegateConstruction(newobj); } if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) { var elementTypes = TupleType.GetTupleElementTypes(inst.Method.DeclaringType); var elementNames = typeHint is TupleType tt ? tt.ElementNames : default; Debug.Assert(!elementTypes.IsDefault, "MatchTupleConstruction should not succeed unless we got a valid tuple type."); Debug.Assert(elementTypes.Length == tupleElements.Length); var tuple = new TupleExpression(); var elementRRs = new List(); foreach (var (index, element, elementType) in tupleElements.ZipWithIndex(elementTypes)) { var translatedElement = expressionBuilder.Translate(element, elementType) .ConvertTo(elementType, expressionBuilder, allowImplicitConversion: true); if (elementNames.IsDefaultOrEmpty || elementNames.ElementAtOrDefault(index) is not string { Length: > 0 } name) { tuple.Elements.Add(translatedElement.Expression); } else { tuple.Elements.Add(new NamedArgumentExpression(name, translatedElement.Expression)); } elementRRs.Add(translatedElement.ResolveResult); } return tuple.WithRR(new TupleResolveResult( expressionBuilder.compilation, elementRRs.ToImmutableArray(), elementNames, valueTupleAssembly: inst.Method.DeclaringType.GetDefinition()?.ParentModule )).WithILInstruction(inst); } if (settings.StringConcat && IsSpanBasedStringConcat(inst, out var operands)) { return BuildStringConcat(inst.Method, operands).WithILInstruction(inst); } return Build(inst.OpCode, inst.Method, inst.Arguments, constrainedTo: inst.ConstrainedTo) .WithILInstruction(inst); } private ExpressionWithResolveResult BuildStringConcat(IMethod method, List<(ILInstruction Instruction, KnownTypeCode TypeCode)> operands) { IType type = typeSystem.FindType(operands[0].TypeCode); ExpressionWithResolveResult result = expressionBuilder.Translate(operands[0].Instruction, type).ConvertTo(type, expressionBuilder); var rr = new MemberResolveResult(null, method); for (int i = 1; i < operands.Count; i++) { type = typeSystem.FindType(operands[i].TypeCode); var expr = expressionBuilder.Translate(operands[i].Instruction, type).ConvertTo(type, expressionBuilder); result = new BinaryOperatorExpression(result.Expression, BinaryOperatorType.Add, expr).WithRR(rr); } return result; } static bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstruction, KnownTypeCode)> operands) { operands = null; if (!IsSpanBasedStringConcat(call.Method)) { return false; } int? firstStringArgumentIndex = null; operands = new(); foreach (var arg in call.Arguments) { if (arg is Call opImplicit && IsStringToReadOnlySpanCharImplicitConversion(opImplicit.Method)) { firstStringArgumentIndex ??= arg.ChildIndex; operands.Add((opImplicit.Arguments.Single(), KnownTypeCode.String)); } else if (arg is NewObj { Arguments: [AddressOf addressOf] } newObj && ILInlining.IsReadOnlySpanCharCtor(newObj.Method)) { operands.Add((addressOf.Value, KnownTypeCode.Char)); } else { return false; } } return call.Arguments.Count >= 2 && firstStringArgumentIndex <= 1; } internal static bool IsSpanBasedStringConcat(IMethod method) { if (method is not { Name: "Concat", IsStatic: true }) { return false; } if (!method.DeclaringType.IsKnownType(KnownTypeCode.String)) { return false; } foreach (var p in method.Parameters) { if (!p.Type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) return false; if (!p.Type.TypeArguments[0].IsKnownType(KnownTypeCode.Char)) return false; } return true; } internal static bool IsStringToReadOnlySpanCharImplicitConversion(IMethod method) { return method.IsOperator && method.Name == "op_Implicit" && method.Parameters.Count == 1 && method.ReturnType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && method.ReturnType.TypeArguments[0].IsKnownType(KnownTypeCode.Char) && method.Parameters[0].Type.IsKnownType(KnownTypeCode.String); } public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap = null, IType constrainedTo = null) { if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call) { // Direct non-virtual call to explicit interface implementation. // This can't really be represented in C#, but at least in the case where // the class is sealed, we can equivalently call the interface member instead: var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList(); if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1) { method = (IMethod)interfaceMembers.Single(); callOpCode = OpCode.CallVirt; } } // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; ILFunction localFunction = null; if (method.IsLocalFunction) { localFunction = expressionBuilder.ResolveLocalFunction(method); Debug.Assert(localFunction != null); } TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target } else if (localFunction != null) { var ide = new IdentifierExpression(localFunction.Name); if (method.TypeArguments.Count > 0) { ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } ide.AddAnnotation(localFunction); target = ide.WithoutILInstruction() .WithRR(ToMethodGroup(method, localFunction)); } else { var thisArg = callArguments.FirstOrDefault(); if (thisArg is LdObjIfRef ldObjIfRef) { Debug.Assert(constrainedTo != null); thisArg = ldObjIfRef.Target; } target = expressionBuilder.TranslateTarget( thisArg, nonVirtualInvocation: callOpCode == OpCode.Call || method.IsConstructor, memberStatic: method.IsStatic, memberDeclaringType: method.DeclaringType, constrainedTo: constrainedTo); if (constrainedTo == null && target.Expression is CastExpression cast && target.ResolveResult is ConversionResolveResult conversion && target.Type.IsKnownType(KnownTypeCode.Object) && conversion.Conversion.IsBoxingConversion) { // boxing conversion on call target? // let's see if we can make that implicit: target = target.UnwrapChild(cast.Expression); // we'll need to make sure the boxing effect is preserved expectedTargetDetails.NeedsBoxingConversion = true; } } int firstParamIndex = (method.IsStatic || callOpCode == OpCode.NewObj) ? 0 : 1; Debug.Assert(firstParamIndex == 0 || argumentToParameterMap == null || argumentToParameterMap[0] == -1); var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, firstParamIndex, callArguments, argumentToParameterMap); if (localFunction != null) { return new InvocationExpression(target, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); } if (method is VarArgInstanceMethod) { argumentList.FirstOptionalArgumentIndex = -1; argumentList.AddNamesToPrimitiveValues = false; argumentList.UseImplicitlyTypedOut = false; int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount; var argListArg = new UndocumentedExpression(); argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; int paramIndex = regularParameterCount; var builder = expressionBuilder; Debug.Assert(argumentToParameterMap == null && argumentList.ArgumentNames == null); argListArg.Arguments.AddRange(argumentList.Arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(argumentList.ExpectedParameters[paramIndex++].Type, builder).Expression)); var argListRR = new ResolveResult(SpecialType.ArgList); argumentList.Arguments = argumentList.Arguments.Take(regularParameterCount) .Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray(); method = ((VarArgInstanceMethod)method).BaseMethod; argumentList.ExpectedParameters = method.Parameters.ToArray(); } if (settings.Ranges) { if (HandleRangeConstruction(out var result, callOpCode, method, target, argumentList)) { return result; } } if (callOpCode == OpCode.NewObj) { return HandleConstructorCall(expectedTargetDetails, target.ResolveResult, method, argumentList); } if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) { return new InvocationExpression(target, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm, isDelegateInvocation: true)); } if (settings.StringInterpolation && IsInterpolatedStringCreation(method, argumentList)) { var result = HandleStringInterpolation(method, argumentList); if (result.Expression != null) return result; } int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || argumentList.ExpectedParameters.Length == allowedParamCount)) { argumentList.CheckNoNamedOrOptionalArguments(); return HandleAccessorCall(expectedTargetDetails, method, target, argumentList.Arguments.ToList(), argumentList.ArgumentNames); } if (IsDelegateEqualityComparison(method, argumentList.Arguments)) { argumentList.CheckNoNamedOrOptionalArguments(); return HandleDelegateEqualityComparison(method, argumentList.Arguments) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); } if (method.IsOperator && method.Name == "op_Implicit" && argumentList.Length == 1) { argumentList.CheckNoNamedOrOptionalArguments(); return HandleImplicitConversion(method, argumentList.Arguments[0]); } if (settings.InlineArrays && method is { DeclaringType.FullName: "", Name: "InlineArrayAsSpan" or "InlineArrayAsReadOnlySpan" } && argumentList.Length == 2) { argumentList.CheckNoNamedOrOptionalArguments(); var arrayType = method.TypeArguments[0]; var arrayLength = arrayType.GetInlineArrayLength(); var arrayElementType = arrayType.GetInlineArrayElementType(); var argument = argumentList.Arguments[0]; var spanLengthExpr = argumentList.Arguments[1]; var targetType = method.ReturnType; var spanType = typeSystem.FindType(KnownTypeCode.SpanOfT); if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In or FieldDirection.Ref, Expression: var lvalueExpr }) { // `(TargetType)(in arg)` is invalid syntax. // Also, `f(in arg)` is invalid when there's an implicit conversion involved. argument = argument.UnwrapChild(lvalueExpr); } if (spanLengthExpr.ResolveResult.ConstantValue is int spanLength && spanLength <= arrayLength) { if (spanLength < arrayLength) { argument = new IndexerExpression(argument.Expression, new BinaryOperatorExpression { Operator = BinaryOperatorType.Range, Right = spanLengthExpr.Expression }).WithRR(new ResolveResult(new ParameterizedType(spanType, arrayElementType))).WithoutILInstruction(); if (targetType.IsKnownType(KnownTypeCode.SpanOfT)) { return argument; } } return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, Conversion.InlineArrayConversion)); } } if (settings.LiftNullables && method.Name == "GetValueOrDefault" && method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT) && method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Boolean) && argumentList.Length == 0) { argumentList.CheckNoNamedOrOptionalArguments(); return new BinaryOperatorExpression( target.Expression, BinaryOperatorType.Equality, new PrimitiveExpression(true)) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); } var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target, ref argumentList, CallTransformation.All, out IParameterizedMember foundMethod); // Note: after this, 'method' and 'foundMethod' may differ, // but as far as allowed by IsAppropriateCallTarget(). // Need to update list of parameter names, because foundMethod is different and thus might use different names. if (!method.Equals(foundMethod) && argumentList.ParameterNames.Length >= foundMethod.Parameters.Count) { for (int i = 0; i < foundMethod.Parameters.Count; i++) { argumentList.ParameterNames[i] = foundMethod.Parameters[i].Name; } } Expression targetExpr; string methodName = method.Name; AstNodeCollection typeArgumentList; if ((transform & CallTransformation.NoNamedArgsForPrettiness) != 0) { argumentList.AddNamesToPrimitiveValues = false; } if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0) { argumentList.FirstOptionalArgumentIndex = -1; } if ((transform & CallTransformation.RequireTarget) != 0) { targetExpr = new MemberReferenceExpression(target.Expression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; // HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method. // settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods. if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) { var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First(); var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach()); methodName = interfaceMember.Name; targetExpr = new MemberReferenceExpression(castExpression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; } if (constrainedTo != null && targetExpr is MemberReferenceExpression { Target: CastExpression cast }) { cast.AddChild(new Comment("cast due to .constrained prefix", CommentType.MultiLine), Roles.Comment); } } else { targetExpr = new IdentifierExpression(methodName); typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments; } if ((transform & CallTransformation.RequireTypeArguments) != 0 && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType()))) typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); return new InvocationExpression(targetExpr, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentList.GetArgumentResolveResultsDirect(), isExpandedForm: argumentList.IsExpandedForm)); } private ExpressionWithResolveResult HandleStringInterpolation(IMethod method, ArgumentList argumentList) { if (!TryGetStringInterpolationTokens(argumentList, out string format, out var tokens)) return default; var arguments = argumentList.Arguments; var content = new List(); bool unpackSingleElementArray = !argumentList.IsExpandedForm && argumentList.Length == 2 && argumentList.Arguments[1].Expression is ArrayCreateExpression ace && ace.Initializer?.Elements.Count == 1; void UnpackSingleElementArray(ref TranslatedExpression argument) { if (!unpackSingleElementArray) return; var arrayCreation = (ArrayCreateExpression)argumentList.Arguments[1].Expression; var arrayCreationRR = (ArrayCreateResolveResult)argumentList.Arguments[1].ResolveResult; var element = arrayCreation.Initializer.Elements.First().Detach(); argument = new TranslatedExpression(element, arrayCreationRR.InitializerElements.First()); } if (tokens.Count == 0) { return default; } foreach (var (kind, index, alignment, text) in tokens) { TranslatedExpression argument; switch (kind) { case TokenKind.String: content.Add(new InterpolatedStringText(text)); break; case TokenKind.Argument: argument = arguments[index + 1]; UnpackSingleElementArray(ref argument); content.Add(new Interpolation(argument)); break; case TokenKind.ArgumentWithFormat: argument = arguments[index + 1]; UnpackSingleElementArray(ref argument); content.Add(new Interpolation(argument, suffix: text)); break; case TokenKind.ArgumentWithAlignment: argument = arguments[index + 1]; UnpackSingleElementArray(ref argument); content.Add(new Interpolation(argument, alignment)); break; case TokenKind.ArgumentWithAlignmentAndFormat: argument = arguments[index + 1]; UnpackSingleElementArray(ref argument); content.Add(new Interpolation(argument, alignment, text)); break; } } var formattableStringType = expressionBuilder.compilation.FindType(KnownTypeCode.FormattableString); var isrr = new InterpolatedStringResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.String), format, argumentList.GetArgumentResolveResults(1).ToArray()); var expr = new InterpolatedStringExpression(); expr.Content.AddRange(content); if (method.Name == "Format") return expr.WithRR(isrr); return new CastExpression(expressionBuilder.ConvertType(formattableStringType), expr.WithRR(isrr)) .WithRR(new ConversionResolveResult(formattableStringType, isrr, Conversion.ImplicitInterpolatedStringConversion)); } /// /// Converts a call to an Add method to a collection initializer expression. /// public ExpressionWithResolveResult BuildCollectionInitializerExpression(OpCode callOpCode, IMethod method, InitializedObjectResolveResult target, IReadOnlyList callArguments) { // (see ECMA-334, section 12.7.11.4): // The collection object to which a collection initializer is applied shall be of a type that implements // System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, // the collection initializer invokes an Add method on the target object with the expression list of the // element initializer as argument list, applying normal overload resolution for each invocation. Thus, the // collection object shall contain an applicable Add method for each element initializer. // The list of applicable methods includes all methods (as of C# 6.0 extension methods, too) named 'Add' // that can be invoked on the target object, with the following exceptions: // - Methods with ref or out parameters may not be used, // - methods that have type parameters, that cannot be inferred from the parameter list may not be used, // - vararg methods may not be used. // - named arguments are not supported. // However, note that params methods may be used. // At this point, we assume that 'method' fulfills all the conditions mentioned above. We just need to make // sure that the correct method is called by resolving any ambiguities by inserting casts, if necessary. ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction(); var args = callArguments.ToList(); if (method.IsExtensionMethod) args.Insert(0, new Nop()); var argumentList = BuildArgumentList(expectedTargetDetails, target, method, firstParamIndex: 0, args, null); argumentList.ArgumentNames = null; argumentList.AddNamesToPrimitiveValues = false; argumentList.UseImplicitlyTypedOut = false; var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused, ref argumentList, CallTransformation.None, out _); Debug.Assert((transform & ~(CallTransformation.NoOptionalArgumentAllowed | CallTransformation.NoNamedArgsForPrettiness)) == 0); // Calls with only one argument do not need an array initializer expression to wrap them. // Any special cases are handled by the caller (i.e., ExpressionBuilder.TranslateObjectAndCollectionInitializer) // Note: we intentionally ignore the firstOptionalArgumentIndex in this case. int skipCount; if (method.IsExtensionMethod) { if (argumentList.Arguments.Length == 2) return argumentList.Arguments[1]; skipCount = 1; } else { if (argumentList.Arguments.Length == 1) return argumentList.Arguments[0]; skipCount = 0; } if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0) argumentList.FirstOptionalArgumentIndex = -1; return new ArrayInitializerExpression(argumentList.GetArgumentExpressions(skipCount)) .WithRR(new CSharpInvocationResolveResult(target, method, argumentList.GetArgumentResolveResults(skipCount).ToArray(), isExtensionMethodInvocation: method.IsExtensionMethod, isExpandedForm: argumentList.IsExpandedForm)); } public ExpressionWithResolveResult BuildDictionaryInitializerExpression(OpCode callOpCode, IMethod method, InitializedObjectResolveResult target, IReadOnlyList indices, ILInstruction value = null) { if (method is null) throw new ArgumentNullException(nameof(method)); ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; var callArguments = new List(); callArguments.Add(new LdNull()); callArguments.AddRange(indices); callArguments.Add(value ?? new Nop()); var argumentList = BuildArgumentList(expectedTargetDetails, target, method, 1, callArguments, null); var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction(); var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, argumentList.Arguments.ToList(), argumentList.ArgumentNames); if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull) indexer.Target.Remove(); if (value != null) return assignment; return new ExpressionWithResolveResult(((AssignmentExpression)assignment).Left.Detach()); } private static bool IsInterpolatedStringCreation(IMethod method, ArgumentList argumentList) { return method.IsStatic && ( (method.DeclaringType.IsKnownType(KnownTypeCode.String) && method.Name == "Format") || (method.Name == "Create" && method.DeclaringType.Name == "FormattableStringFactory" && method.DeclaringType.Namespace == "System.Runtime.CompilerServices") ) && argumentList.ArgumentNames == null // Argument names are not allowed && ( argumentList.IsExpandedForm // Must be expanded form || !method.Parameters.Last().IsParams // -or- not a params overload || (argumentList.Length == 2 && argumentList.Arguments[1].Expression is ArrayCreateExpression) // -or- an array literal ); } private bool TryGetStringInterpolationTokens(ArgumentList argumentList, out string format, out List<(TokenKind Kind, int Index, int Alignment, string Format)> tokens) { tokens = null; format = null; TranslatedExpression[] arguments = argumentList.Arguments; if (arguments.Length == 0 || argumentList.ArgumentNames != null || argumentList.ArgumentToParameterMap != null) return false; if (!(arguments[(int)0].ResolveResult is ConstantResolveResult crr && crr.Type.IsKnownType((KnownTypeCode)KnownTypeCode.String))) return false; if (!arguments.Skip(1).All(a => !a.Expression.DescendantsAndSelf.OfType().Any(p => p.Value is string))) return false; tokens = new List<(TokenKind Kind, int Index, int Alignment, string Format)>(); int i = 0; format = (string)crr.ConstantValue; foreach (var (kind, data) in TokenizeFormatString(format)) { int index; string[] arg; switch (kind) { case TokenKind.Error: return false; case TokenKind.String: tokens.Add((kind, -1, 0, data)); break; case TokenKind.Argument: if (!int.TryParse(data, out index) || index != i) return false; i++; tokens.Add((kind, index, 0, null)); break; case TokenKind.ArgumentWithFormat: arg = data.Split(new[] { ':' }, 2); if (arg.Length != 2 || arg[1].Length == 0) return false; if (!int.TryParse(arg[0], out index) || index != i) return false; i++; tokens.Add((kind, index, 0, arg[1])); break; case TokenKind.ArgumentWithAlignment: arg = data.Split(new[] { ',' }, 2); if (arg.Length != 2 || arg[1].Length == 0) return false; if (!int.TryParse(arg[0], out index) || index != i) return false; if (!int.TryParse(arg[1], out int alignment)) return false; i++; tokens.Add((kind, index, alignment, null)); break; case TokenKind.ArgumentWithAlignmentAndFormat: arg = data.Split(new[] { ',', ':' }, 3); if (arg.Length != 3 || arg[1].Length == 0 || arg[2].Length == 0) return false; if (!int.TryParse(arg[0], out index) || index != i) return false; if (!int.TryParse(arg[1], out alignment)) return false; i++; tokens.Add((kind, index, alignment, arg[2])); break; default: return false; } } return i == arguments.Length - 1; } private enum TokenKind { Error, String, Argument, ArgumentWithFormat, ArgumentWithAlignment, ArgumentWithAlignmentAndFormat, } private IEnumerable<(TokenKind, string)> TokenizeFormatString(string value) { int pos = -1; int Peek(int steps = 1) { if (pos + steps < value.Length) return value[pos + steps]; return -1; } int Next() { int val = Peek(); pos++; return val; } int next; TokenKind kind = TokenKind.String; StringBuilder sb = new StringBuilder(); while ((next = Next()) > -1) { switch ((char)next) { case '{': if (Peek() == '{') { kind = TokenKind.String; sb.Append("{{"); Next(); } else { if (sb.Length > 0) { yield return (kind, sb.ToString()); } kind = TokenKind.Argument; sb.Clear(); } break; case '}': if (kind != TokenKind.String) { yield return (kind, sb.ToString()); sb.Clear(); kind = TokenKind.String; } else if (Peek() == '}') { sb.Append("}}"); Next(); } else { yield return (TokenKind.Error, null); } break; case ':': if (kind == TokenKind.Argument) { kind = TokenKind.ArgumentWithFormat; } else if (kind == TokenKind.ArgumentWithAlignment) { kind = TokenKind.ArgumentWithAlignmentAndFormat; } sb.Append(':'); break; case ',': if (kind == TokenKind.Argument) { kind = TokenKind.ArgumentWithAlignment; } sb.Append(','); break; default: sb.Append((char)next); break; } } if (sb.Length > 0) { if (kind == TokenKind.String) yield return (kind, sb.ToString()); else yield return (TokenKind.Error, null); } } private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex, IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) { ArgumentList list = new ArgumentList(); // Translate arguments to the expected parameter types var arguments = new List(method.Parameters.Count); string[] argumentNames = null; Debug.Assert(callArguments.Count == firstParamIndex + method.Parameters.Count); var expectedParameters = new List(method.Parameters.Count); // parameters, but in argument order bool isExpandedForm = false; BitSet isPrimitiveValue = new BitSet(method.Parameters.Count); // Optional arguments: // This value has the following values: // -2 - there are no optional arguments // -1 - optional arguments are forbidden // >= 0 - the index of the first argument that can be removed, because it is optional // and is the default value of the parameter. int firstOptionalArgumentIndex = expressionBuilder.settings.OptionalArguments ? -2 : -1; for (int i = firstParamIndex; i < callArguments.Count; i++) { IParameter parameter; if (argumentToParameterMap != null) { if (argumentNames == null && argumentToParameterMap[i] != i - firstParamIndex) { // Starting at the first argument that is out-of-place, // assign names to that argument and all following arguments: argumentNames = new string[method.Parameters.Count]; } parameter = method.Parameters[argumentToParameterMap[i]]; if (argumentNames != null && AssignVariableNames.IsValidName(parameter.Name)) { argumentNames[arguments.Count] = parameter.Name; } } else { parameter = method.Parameters[i - firstParamIndex]; } var arg = expressionBuilder.Translate(callArguments[i], parameter.Type); if (IsPrimitiveValueThatShouldBeNamedArgument(arg, method, parameter)) { isPrimitiveValue.Set(arguments.Count); } if (IsOptionalArgument(parameter, arg)) { if (firstOptionalArgumentIndex == -2) firstOptionalArgumentIndex = i - firstParamIndex; } else { if (firstOptionalArgumentIndex != -1) firstOptionalArgumentIndex = -2; } if (expressionBuilder.settings.ExpandParamsArguments && parameter.IsParams && i + 1 == callArguments.Count && argumentToParameterMap == null) { // Parameter is marked params // If the argument is an array creation, inline all elements into the call and add missing default values. // Otherwise handle it normally. if (TransformParamsArgument(expectedTargetDetails, target, method, parameter, arg, ref expectedParameters, ref arguments)) { Debug.Assert(argumentNames == null); firstOptionalArgumentIndex = -1; isExpandedForm = true; continue; } } IType parameterType; if (parameter.Type.Kind == TypeKind.Dynamic) { parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object); } else { parameterType = parameter.Type; } arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic); if (parameter.ReferenceKind != ReferenceKind.None) { arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind, callArguments[i] is AddressOf); } arguments.Add(arg); expectedParameters.Add(parameter); } list.ExpectedParameters = expectedParameters.ToArray(); list.Arguments = arguments.ToArray(); list.ParameterNames = expectedParameters.SelectArray(p => p.Name); list.ArgumentNames = argumentNames; list.ArgumentToParameterMap = argumentToParameterMap; list.IsExpandedForm = isExpandedForm; list.IsPrimitiveValue = isPrimitiveValue; list.FirstOptionalArgumentIndex = firstOptionalArgumentIndex; list.UseImplicitlyTypedOut = true; list.AddNamesToPrimitiveValues = expressionBuilder.settings.NamedArguments && expressionBuilder.settings.NonTrailingNamedArguments; return list; } private bool IsPrimitiveValueThatShouldBeNamedArgument(TranslatedExpression arg, IMethod method, IParameter p) { if (!arg.ResolveResult.IsCompileTimeConstant || method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT)) return false; return p.Type.IsKnownType(KnownTypeCode.Boolean); } private bool TransformParamsArgument(ExpectedTargetDetails expectedTargetDetails, ResolveResult targetResolveResult, IMethod method, IParameter parameter, TranslatedExpression paramsArgument, ref List expectedParameters, ref List arguments) { var expressionBuilder = this.expressionBuilder; if (ExtractArguments(out IType elementType, out var expandedParameters, out var expandedArguments)) { expandedParameters.InsertRange(0, expectedParameters); expandedArguments.InsertRange(0, arguments); if (IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, Empty.Array, expandedArguments.SelectArray(a => a.ResolveResult), argumentNames: null, firstOptionalArgumentIndex: -1, out _, out var bestCandidateIsExpandedForm) == OverloadResolutionErrors.None && bestCandidateIsExpandedForm) { expectedParameters = expandedParameters; arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach())); return true; } } return false; bool ExtractArguments(out IType elementType, out List parameters, out List arguments) { elementType = null; parameters = null; arguments = null; switch (paramsArgument.ResolveResult) { case CSharpInvocationResolveResult { Member: IMethod method, Arguments: var args }: // match System.Array.Empty() if (args is [] && method is { IsStatic: true, FullName: "System.Array.Empty", TypeArguments: [var type] }) { elementType = type; arguments = new(); parameters = new(); return true; } // match System.ReadOnlySpan..ctor(ref readonly T) if (paramsArgument.Expression is ObjectCreateExpression oce && method is { IsConstructor: true, Parameters: [{ ReferenceKind: ReferenceKind.RefReadOnly, Type: ByReferenceType { ElementType: var paramType } }], DeclaringType: { TypeArguments: [var type2] } declaringType } && declaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && paramType.Equals(type2)) { elementType = type2; arguments = new() { new TranslatedExpression(oce.Arguments.Single()) }; parameters = new() { new DefaultParameter(type2, string.Empty) }; return true; } return false; case ArrayCreateResolveResult { Type: ArrayType { ElementType: var type3 }, SizeArguments: [{ ConstantValue: int arrayLength }] }: elementType = type3; arguments = new(((ArrayCreateExpression)paramsArgument.Expression).Initializer.Elements.Select(e => new TranslatedExpression(e))); parameters = new List(arrayLength); for (int i = 0; i < arrayLength; i++) { parameters.Add(new DefaultParameter(type3, string.Empty)); if (arguments.Count <= i) arguments.Add(new TranslatedExpression(expressionBuilder.GetDefaultValueExpression(type3).WithoutILInstruction())); } return true; default: return false; } } } bool IsOptionalArgument(IParameter parameter, TranslatedExpression arg) { if (!parameter.IsOptional) return false; if (!arg.ResolveResult.IsCompileTimeConstant && arg.ResolveResult is not ConversionResolveResult { Conversion.IsNullLiteralConversion: true }) return false; if (parameter.GetAttributes().Any(a => a.AttributeType.IsKnownType(KnownAttribute.CallerMemberName) || a.AttributeType.IsKnownType(KnownAttribute.CallerFilePath) || a.AttributeType.IsKnownType(KnownAttribute.CallerLineNumber))) return false; return object.Equals(parameter.GetConstantValue(), arg.ResolveResult.ConstantValue); } [Flags] enum CallTransformation { None = 0, RequireTarget = 1, RequireTypeArguments = 2, NoOptionalArgumentAllowed = 4, /// /// Add calls to AsRefReadOnly for in parameters that did not have an explicit DirectionExpression yet. /// EnforceExplicitIn = 8, NoNamedArgsForPrettiness = 0x10, All = 0x1f, } private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, ref TranslatedExpression target, ref ArgumentList argumentList, CallTransformation allowedTransforms, out IParameterizedMember foundMethod) { CallTransformation transform = CallTransformation.None; // initialize requireTarget flag bool requireTarget; ResolveResult targetResolveResult; if ((allowedTransforms & CallTransformation.RequireTarget) != 0) { if (settings.AlwaysQualifyMemberReferences || expressionBuilder.HidesVariableWithName(method.Name)) { requireTarget = true; } else { if (method.IsLocalFunction) requireTarget = false; else if (method.IsStatic) requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor"; else if (method.Name == ".ctor") requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this else if (target.Expression is BaseReferenceExpression) requireTarget = (expectedTargetDetails.CallOpCode != OpCode.CallVirt && method.IsVirtual); else requireTarget = target.Expression is not ThisReferenceExpression; } targetResolveResult = requireTarget ? target.ResolveResult : null; } else { // HACK: this is a special case for collection initializer calls, they do not allow a target to be // emitted, but we still need it for overload resolution. requireTarget = true; targetResolveResult = target.ResolveResult; } // initialize requireTypeArguments flag bool requireTypeArguments; IType[] typeArguments; bool appliedRequireTypeArgumentsShortcut = false; if (method.TypeParameters.Count > 0 && (allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !IsPossibleExtensionMethodCallOnNull(method, argumentList.Arguments)) { // The ambiguity resolution below only adds type arguments as last resort measure, however there are // methods, such as Enumerable.OfType(IEnumerable input) that always require type arguments, // as those cannot be inferred from the parameters, which leads to bloated expressions full of extra casts // that are no longer required once we add the type arguments. // We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments, // if necessary. if (!CanInferTypeArgumentsFromArguments(method, argumentList, expressionBuilder.typeInference)) { requireTypeArguments = true; typeArguments = method.TypeArguments.ToArray(); appliedRequireTypeArgumentsShortcut = true; } else { requireTypeArguments = false; typeArguments = Empty.Array; } } else { requireTypeArguments = false; typeArguments = Empty.Array; } bool targetCasted = false; bool argumentsCasted = false; bool originalRequireTarget = requireTarget; bool skipTargetCast = method.Accessibility <= Accessibility.Protected && expressionBuilder.IsBaseTypeOfCurrentType(method.DeclaringTypeDefinition); OverloadResolutionErrors errors; while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, argumentList.GetArgumentResolveResults().ToArray(), argumentList.GetArgumentNames(), argumentList.FirstOptionalArgumentIndex, out foundMethod, out var bestCandidateIsExpandedForm)) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm) { switch (errors) { case OverloadResolutionErrors.OutVarTypeMismatch: Debug.Assert(argumentList.UseImplicitlyTypedOut); argumentList.UseImplicitlyTypedOut = false; continue; case OverloadResolutionErrors.TypeInferenceFailed: if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0) { goto case OverloadResolutionErrors.WrongNumberOfTypeArguments; } goto default; case OverloadResolutionErrors.WrongNumberOfTypeArguments: Debug.Assert((allowedTransforms & CallTransformation.RequireTypeArguments) != 0); if (requireTypeArguments) goto default; requireTypeArguments = true; typeArguments = method.TypeArguments.ToArray(); continue; case OverloadResolutionErrors.MissingArgumentForRequiredParameter: if (argumentList.FirstOptionalArgumentIndex == -1) goto default; argumentList.FirstOptionalArgumentIndex = -1; continue; default: // TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args) // is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts. if (argumentList.AddNamesToPrimitiveValues) { argumentList.AddNamesToPrimitiveValues = false; } else if (argumentList.FirstOptionalArgumentIndex >= 0) { argumentList.FirstOptionalArgumentIndex = -1; } else if (!argumentsCasted) { // If we added type arguments beforehand, but that didn't make the code any better, // undo that decision and add casts first. if (appliedRequireTypeArgumentsShortcut) { requireTypeArguments = false; typeArguments = Empty.Array; appliedRequireTypeArgumentsShortcut = false; } argumentsCasted = true; argumentList.UseImplicitlyTypedOut = false; CastArguments(argumentList.Arguments, argumentList.ExpectedParameters); } else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; } else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !targetCasted) { if (skipTargetCast && requireTarget != originalRequireTarget) { requireTarget = originalRequireTarget; if (!originalRequireTarget) targetResolveResult = null; allowedTransforms &= ~CallTransformation.RequireTarget; } else { targetCasted = true; target = target.ConvertTo(method.DeclaringType, expressionBuilder); targetResolveResult = target.ResolveResult; } } else if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !requireTypeArguments) { requireTypeArguments = true; typeArguments = method.TypeArguments.ToArray(); } else if ((allowedTransforms & CallTransformation.EnforceExplicitIn) != 0) { EnforceExplicitIn(argumentList.Arguments, argumentList.ExpectedParameters); allowedTransforms &= ~CallTransformation.EnforceExplicitIn; } else { break; } continue; } // We've given up. foundMethod = method; break; } if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && requireTarget) transform |= CallTransformation.RequireTarget; if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && requireTypeArguments) transform |= CallTransformation.RequireTypeArguments; if (argumentList.FirstOptionalArgumentIndex < 0) transform |= CallTransformation.NoOptionalArgumentAllowed; if (!argumentList.AddNamesToPrimitiveValues) transform |= CallTransformation.NoNamedArgsForPrettiness; return transform; } private void EnforceExplicitIn(TranslatedExpression[] arguments, IParameter[] expectedParameters) { for (int i = 0; i < arguments.Length; i++) { if (expectedParameters[i].ReferenceKind != ReferenceKind.In) continue; if (arguments[i].Expression is DirectionExpression) continue; arguments[i] = WrapInAsRefReadOnly(arguments[i]); expressionBuilder.statementBuilder.EmitAsRefReadOnly = true; } } private TranslatedExpression WrapInAsRefReadOnly(TranslatedExpression arg) { return new DirectionExpression( FieldDirection.In, new InvocationExpression { Target = new IdentifierExpression("ILSpyHelper_AsRefReadOnly"), Arguments = { arg.Expression } } ).WithRR(new ByReferenceResolveResult(arg.Type, ReferenceKind.In)) .WithoutILInstruction(); } private bool IsPossibleExtensionMethodCallOnNull(IMethod method, IList arguments) { return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression; } static bool CanInferTypeArgumentsFromArguments(IMethod method, ArgumentList argumentList, TypeInference typeInference) { if (method.TypeParameters.Count == 0) return true; // always use unspecialized member, otherwise type inference fails method = (IMethod)method.MemberDefinition; IReadOnlyList paramTypesInArgumentOrder; if (argumentList.ArgumentToParameterMap == null) paramTypesInArgumentOrder = method.Parameters.SelectReadOnlyArray(p => p.Type); else paramTypesInArgumentOrder = argumentList.ArgumentToParameterMap .SelectReadOnlyArray( index => index >= 0 ? method.Parameters[index].Type : SpecialType.UnknownType ); typeInference.InferTypeArguments(method.TypeParameters, argumentList.Arguments.SelectReadOnlyArray(a => a.ResolveResult), paramTypesInArgumentOrder, out bool success); return success; } private void CastArguments(IList arguments, IList expectedParameters) { for (int i = 0; i < arguments.Count; i++) { if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) { if (arguments[i].Expression is LambdaExpression lambda) { ModifyReturnTypeOfLambda(lambda); } } else { IParameter parameter = expectedParameters[i]; IType parameterType; if (parameter.Type.Kind == TypeKind.Dynamic) { parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object); } else { parameterType = parameter.Type; } if (parameter.ReferenceKind == ReferenceKind.In && parameterType is ByReferenceType brt && arguments[i].Type is not ByReferenceType) { parameterType = brt.ElementType; } arguments[i] = arguments[i].ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: false); } } } static bool IsNullConditional(Expression expr) { return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional; } private void ModifyReturnTypeOfLambda(LambdaExpression lambda) { var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult(); if (lambda.Body is Expression exprBody) lambda.Body = new TranslatedExpression(exprBody.Detach()).ConvertTo(resolveResult.ReturnType, expressionBuilder); else ModifyReturnStatementInsideLambda(resolveResult.ReturnType, lambda); resolveResult.InferredReturnType = resolveResult.ReturnType; } private void ModifyReturnStatementInsideLambda(IType returnType, AstNode parent) { foreach (var child in parent.Children) { if (child is LambdaExpression || child is AnonymousMethodExpression) continue; if (child is ReturnStatement ret) { ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder); continue; } ModifyReturnStatementInsideLambda(returnType, child); } } private bool IsDelegateEqualityComparison(IMethod method, IList arguments) { // Comparison on a delegate type is a C# builtin operator // that compiles down to a Delegate.op_Equality call. // We handle this as a special case to avoid inserting a cast to System.Delegate. return method.IsOperator && method.DeclaringType.IsKnownType(KnownTypeCode.Delegate) && (method.Name == "op_Equality" || method.Name == "op_Inequality") && arguments.Count == 2 && arguments[0].Type.Kind == TypeKind.Delegate && arguments[1].Type.Equals(arguments[0].Type); } private Expression HandleDelegateEqualityComparison(IMethod method, IList arguments) { return new BinaryOperatorExpression( arguments[0], method.Name == "op_Equality" ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality, arguments[1] ); } private ExpressionWithResolveResult HandleImplicitConversion(IMethod method, TranslatedExpression argument) { var conversions = CSharpConversions.Get(expressionBuilder.compilation); IType targetType = method.ReturnType; var conv = conversions.ImplicitConversion(argument.Type, targetType); if (!(conv.IsUserDefined && conv.IsValid && conv.Method.Equals(method, NormalizeTypeVisitor.TypeErasure))) { // implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder); conv = conversions.ImplicitConversion(argument.Type, targetType); } if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In, Expression: var lvalueExpr }) { // `(TargetType)(in arg)` is invalid syntax. // Also, `f(in arg)` is invalid when there's an implicit conversion involved. argument = argument.UnwrapChild(lvalueExpr); } return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv)); } OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IType[] typeArguments, ResolveResult[] arguments, string[] argumentNames, int firstOptionalArgumentIndex, out IParameterizedMember foundMember, out bool bestCandidateIsExpandedForm) { foundMember = null; bestCandidateIsExpandedForm = false; var currentTypeDefinition = resolver.CurrentTypeDefinition; var lookup = new MemberLookup(currentTypeDefinition, currentTypeDefinition.ParentModule); Log.WriteLine("IsUnambiguousCall: Performing overload resolution for " + method); Log.WriteCollection(" Arguments: ", arguments); argumentNames = firstOptionalArgumentIndex < 0 || argumentNames == null ? argumentNames : argumentNames.Take(firstOptionalArgumentIndex).ToArray(); var or = new OverloadResolution(resolver.Compilation, arguments, argumentNames, typeArguments, conversions: expressionBuilder.resolver.conversions); if (expectedTargetDetails.CallOpCode == OpCode.NewObj) { foreach (IMethod ctor in method.DeclaringType.GetConstructors()) { bool allowProtectedAccess = resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition; if (lookup.IsAccessible(ctor, allowProtectedAccess)) { Log.Indent(); OverloadResolutionErrors errors = or.AddCandidate(ctor); Log.Unindent(); or.LogCandidateAddingResult(" Candidate", ctor, errors); } } } else if (method.IsOperator) { IEnumerable operatorCandidates; if (arguments.Length == 1) { IType argType = NullableType.GetUnderlyingType(arguments[0].Type); operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name); if (method.Name == "op_Explicit") { // For casts, also consider candidates from the target type we are casting to. var hashSet = new HashSet(operatorCandidates); IType targetType = NullableType.GetUnderlyingType(method.ReturnType); hashSet.UnionWith( resolver.GetUserDefinedOperatorCandidates(targetType, method.Name) ); operatorCandidates = hashSet; } } else if (arguments.Length == 2) { IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type); IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type); var hashSet = new HashSet(); hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name)); hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name)); operatorCandidates = hashSet; } else { operatorCandidates = EmptyList.Instance; } foreach (var m in operatorCandidates) { or.AddCandidate(m); } } else if (target == null) { var result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: true) as MethodGroupResolveResult; if (result == null) return OverloadResolutionErrors.AmbiguousMatch; or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); } else { var result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: true) as MethodGroupResolveResult; if (result == null) return OverloadResolutionErrors.AmbiguousMatch; or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); } bestCandidateIsExpandedForm = or.BestCandidateIsExpandedForm; if (or.BestCandidateErrors != OverloadResolutionErrors.None) return or.BestCandidateErrors; if (or.IsAmbiguous) return OverloadResolutionErrors.AmbiguousMatch; foundMember = or.GetBestCandidateWithSubstitutedTypeArguments(); if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMember)) return OverloadResolutionErrors.AmbiguousMatch; var map = or.GetArgumentToParameterMap(); for (int i = 0; i < arguments.Length; i++) { ResolveResult arg = arguments[i]; int parameterIndex = map[i]; if (arg is OutVarResolveResult rr && parameterIndex >= 0) { var param = foundMember.Parameters[parameterIndex]; var paramType = param.Type.UnwrapByRef(); if (!paramType.Equals(rr.OriginalVariableType)) return OverloadResolutionErrors.OutVarTypeMismatch; } } return OverloadResolutionErrors.None; } bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, IList arguments, string[] argumentNames, out IMember foundMember) { Log.WriteLine("IsUnambiguousAccess: Performing overload resolution for " + method); Log.WriteCollection(" Arguments: ", arguments.Select(a => a.ResolveResult)); foundMember = null; if (target == null) { var result = resolver.ResolveSimpleName(method.AccessorOwner.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; if (result == null || result.IsError) return false; foundMember = result.Member; } else { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer) { var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), argumentNames: argumentNames, typeArguments: Empty.Array, conversions: expressionBuilder.resolver.conversions); or.AddMethodLists(lookup.LookupIndexers(target)); if (or.BestCandidateErrors != OverloadResolutionErrors.None) return false; if (or.IsAmbiguous) return false; foundMember = or.GetBestCandidateWithSubstitutedTypeArguments(); } else { var result = lookup.Lookup(target, method.AccessorOwner.Name, EmptyList.Instance, isInvocation: false) as MemberResolveResult; if (result == null || result.IsError) return false; foundMember = result.Member; } } return foundMember != null && IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, foundMember); } ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, TranslatedExpression target, List arguments, string[] argumentNames) { bool requireTarget; if (settings.AlwaysQualifyMemberReferences || method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name)) requireTarget = true; else if (method.IsStatic) requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition); else requireTarget = !(target.Expression is ThisReferenceExpression); bool targetCasted = false; bool isSetter = method.ReturnType.IsKnownType(KnownTypeCode.Void); bool argumentsCasted = (isSetter && method.Parameters.Count == 1) || (!isSetter && method.Parameters.Count == 0); var targetResolveResult = requireTarget ? target.ResolveResult : null; TranslatedExpression value = default(TranslatedExpression); if (isSetter) { value = arguments.Last(); arguments.Remove(value); } IMember foundMember; while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, arguments, argumentNames, out foundMember)) { if (!argumentsCasted) { argumentsCasted = true; CastArguments(arguments, method.Parameters.ToList()); } else if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; } else if (!targetCasted) { targetCasted = true; target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder); targetResolveResult = target.ResolveResult; } else { foundMember = method.AccessorOwner; break; } } var rr = new MemberResolveResult(target.ResolveResult, foundMember); if (isSetter) { TranslatedExpression expr; if (arguments.Count != 0) { expr = new IndexerExpression(target.ResolveResult is InitializedObjectResolveResult ? null : target.Expression, arguments.Select(a => a.Expression)) .WithoutILInstruction().WithRR(rr); } else if (requireTarget) { expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name) .WithoutILInstruction().WithRR(rr); } else { expr = new IdentifierExpression(method.AccessorOwner.Name) .WithoutILInstruction().WithRR(rr); } var op = AssignmentOperatorType.Assign; if (method.AccessorOwner is IEvent parentEvent) { if (method.Equals(parentEvent.AddAccessor)) { op = AssignmentOperatorType.Add; } if (method.Equals(parentEvent.RemoveAccessor)) { op = AssignmentOperatorType.Subtract; } } return new AssignmentExpression(expr, op, value.Expression).WithRR(new TypeResolveResult(method.AccessorOwner.ReturnType)); } else { if (arguments.Count != 0) { return new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)) .WithoutILInstruction().WithRR(rr); } else if (requireTarget) { return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name) .WithoutILInstruction().WithRR(rr); } else { return new IdentifierExpression(method.AccessorOwner.Name) .WithoutILInstruction().WithRR(rr); } } } bool IsAppropriateCallTarget(ExpectedTargetDetails expectedTargetDetails, IMember expectedTarget, IMember actualTarget) { if (expectedTarget.Equals(actualTarget, NormalizeTypeVisitor.TypeErasure)) return true; if (expectedTargetDetails.CallOpCode == OpCode.CallVirt && actualTarget.IsOverride) { if (expectedTargetDetails.NeedsBoxingConversion && actualTarget.DeclaringType.IsReferenceType != true) return false; foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false)) { if (expectedTarget.Equals(possibleTarget, NormalizeTypeVisitor.TypeErasure)) return true; if (!possibleTarget.IsOverride) break; } } return false; } ExpressionWithResolveResult HandleConstructorCall(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, ArgumentList argumentList) { if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) { Debug.Assert(argumentList.ArgumentToParameterMap == null && argumentList.ArgumentNames == null && argumentList.FirstOptionalArgumentIndex < 0); var atce = new AnonymousTypeCreateExpression(); if (argumentList.CanInferAnonymousTypePropertyNamesFromArguments()) { atce.Initializers.AddRange(argumentList.GetArgumentExpressions()); } else { for (int i = 0; i < argumentList.Length; i++) { atce.Initializers.Add( new NamedExpression { Name = argumentList.ExpectedParameters[i].Name, Expression = argumentList.Arguments[i].ConvertTo(argumentList.ExpectedParameters[i].Type, expressionBuilder) }); } } return atce.WithRR(new CSharpInvocationResolveResult( target, method, argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap )); } else { while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty.Array, argumentList.GetArgumentResolveResults().ToArray(), argumentList.GetArgumentNames(), argumentList.FirstOptionalArgumentIndex, out _, out var bestCandidateIsExpandedForm) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm) { if (argumentList.AddNamesToPrimitiveValues) { argumentList.AddNamesToPrimitiveValues = false; continue; } if (argumentList.FirstOptionalArgumentIndex >= 0) { argumentList.FirstOptionalArgumentIndex = -1; continue; } CastArguments(argumentList.Arguments, argumentList.ExpectedParameters); break; // make sure that we don't not end up in an infinite loop } IType returnTypeOverride = null; if (typeSystem.MainModule.TypeSystemOptions.HasFlag(TypeSystemOptions.NativeIntegersWithoutAttribute)) { // For DeclaringType, we don't use nint/nuint (so that DeclaringType.GetConstructors etc. works), // but in NativeIntegersWithoutAttribute mode we must use nint/nuint for expression types, // so that the appropriate set of conversions is used for further overload resolution. if (method.DeclaringType.IsKnownType(KnownTypeCode.IntPtr)) returnTypeOverride = SpecialType.NInt; else if (method.DeclaringType.IsKnownType(KnownTypeCode.UIntPtr)) returnTypeOverride = SpecialType.NUInt; } return new ObjectCreateExpression( expressionBuilder.ConvertType(method.DeclaringType), argumentList.GetArgumentExpressions() ).WithRR(new CSharpInvocationResolveResult( target, method, argumentList.GetArgumentResolveResults().ToArray(), isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap, returnTypeOverride: returnTypeOverride )); } } TranslatedExpression HandleDelegateConstruction(CallInstruction inst) { ILInstruction thisArg = inst.Arguments[0]; ILInstruction func = inst.Arguments[1]; IMethod method; ExpectedTargetDetails expectedTargetDetails = default; switch (func.OpCode) { case OpCode.LdFtn: method = ((LdFtn)func).Method; expectedTargetDetails.CallOpCode = OpCode.Call; break; case OpCode.LdVirtFtn: method = ((LdVirtFtn)func).Method; expectedTargetDetails.CallOpCode = OpCode.CallVirt; break; default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod())) { return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); } else { var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method, 0, inst.Arguments, null); return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst); } } private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod) { // Accessors cannot be directly referenced as method group in C# // see https://github.com/icsharpcode/ILSpy/issues/1741#issuecomment-540179101 if (targetMethod.IsAccessor) return false; if (targetMethod.IsStatic) { // If the invoke method is known, we can compare the parameter counts to figure out whether the // delegate is static or binds the first argument if (invokeMethod != null) { if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count) { return thisArg.MatchLdNull(); } else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1) { return true; } else { return false; } } else { // delegate type unknown: return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod; } } else { // targetMethod is instance method if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count) return false; return true; } } internal TranslatedExpression Build(LdVirtDelegate inst) { return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst); } internal ExpressionWithResolveResult BuildMethodReference(IMethod method, bool isVirtual) { var expr = BuildDelegateReference(method, invokeMethod: null, new ExpectedTargetDetails { CallOpCode = isVirtual ? OpCode.CallVirt : OpCode.Call }, thisArg: null); expr.Expression.RemoveAnnotations(); return expr.Expression.WithRR(new MemberResolveResult(null, method)); } ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg) { ExpressionBuilder expressionBuilder = this.expressionBuilder; ExpressionWithResolveResult targetExpression; (TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) = DisambiguateDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg); if (target.Expression != null) { var mre = new MemberReferenceExpression(target, methodName); if (addTypeArguments) { mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } targetExpression = mre.WithRR(result); } else { var ide = new IdentifierExpression(methodName); if (addTypeArguments) { ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } targetExpression = ide.WithRR(result); } return targetExpression; } (TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) DisambiguateDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg) { if (method.IsLocalFunction) { ILFunction localFunction = expressionBuilder.ResolveLocalFunction(method); Debug.Assert(localFunction != null); return (default, addTypeArguments: true, localFunction.Name, ToMethodGroup(method, localFunction)); } if (method.IsExtensionMethod && method.Parameters.Count - 1 == invokeMethod?.Parameters.Count) { IType targetType = method.Parameters[0].Type; if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { targetType = ((ByReferenceType)targetType).ElementType; thisArg = thisArgBox.Argument; } TranslatedExpression target = expressionBuilder.Translate(thisArg, targetType); var currentTarget = target; bool targetCasted = false; bool addTypeArguments = false; // Initial inputs for IsUnambiguousMethodReference: ResolveResult targetResolveResult = target.ResolveResult; IReadOnlyList typeArguments = EmptyList.Instance; if (thisArg.MatchLdNull()) { targetCasted = true; currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); targetResolveResult = currentTarget.ResolveResult; } // Find somewhat minimal solution: ResolveResult result; while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, true, out result)) { if (!targetCasted) { // try casting target targetCasted = true; currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); targetResolveResult = currentTarget.ResolveResult; continue; } if (!addTypeArguments) { // try adding type arguments addTypeArguments = true; typeArguments = method.TypeArguments; continue; } break; } return (currentTarget, addTypeArguments, method.Name, result); } else { // Prepare call target IType targetType = method.DeclaringType; if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', // but delegate construction uses a 'box T'. if (thisArgBox.Argument is LdObj ldobj) { thisArg = ldobj.Target; } else { thisArg = new AddressOf(thisArgBox.Argument, thisArgBox.Type); } } TranslatedExpression target = expressionBuilder.TranslateTarget(thisArg, nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call, memberStatic: method.IsStatic, memberDeclaringType: method.DeclaringType); // check if target is required bool requireTarget = expressionBuilder.HidesVariableWithName(method.Name) || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); // Try to find minimal expression // If target is required, include it from the start bool targetAdded = requireTarget; TranslatedExpression currentTarget = targetAdded ? target : default; // Remember other decisions: bool targetCasted = false; bool addTypeArguments = false; // Initial inputs for IsUnambiguousMethodReference: ResolveResult targetResolveResult = targetAdded ? target.ResolveResult : null; IReadOnlyList typeArguments = EmptyList.Instance; // Find somewhat minimal solution: ResolveResult result; while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, false, out result)) { if (!addTypeArguments) { // try adding type arguments addTypeArguments = true; typeArguments = method.TypeArguments; continue; } if (!targetAdded) { // try adding target targetAdded = true; currentTarget = target; targetResolveResult = target.ResolveResult; continue; } if (!targetCasted) { // try casting target targetCasted = true; currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); targetResolveResult = currentTarget.ResolveResult; continue; } break; } if (result is MethodGroupResolveResult mgrr) { result = mgrr.WithChosenMethod(method); } return (currentTarget, addTypeArguments, method.Name, result); } } TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) { var invokeMethod = delegateType.GetDelegateInvokeMethod(); var targetExpression = BuildDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg); var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( delegateType, targetExpression.ResolveResult, Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false))); return oce; } bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, bool isExtensionMethodReference, out ResolveResult result) { Log.WriteLine("IsUnambiguousMethodReference: Performing overload resolution for " + method); var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); OverloadResolution or; if (isExtensionMethodReference) { result = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; if (result == null) return false; or = ((MethodGroupResolveResult)result).PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), argumentNames: null, allowExtensionMethods: true); if (or == null || or.IsAmbiguous) return false; } else { or = new OverloadResolution(resolver.Compilation, arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types argumentNames: null, // argument names are not possible typeArguments.ToArray(), conversions: expressionBuilder.resolver.conversions ); if (target == null) { result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); if (!(result is MethodGroupResolveResult mgrr)) return false; or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); } else { result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); if (!(result is MethodGroupResolveResult mgrr)) return false; or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); } } var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments(); if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod)) return false; return result is MethodGroupResolveResult; } static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction) { return new MethodGroupResolveResult( null, localFunction.Name, new[] { new MethodListWithDeclaringType( method.DeclaringType, new IParameterizedMember[] { method } ) }, method.TypeArguments ); } internal TranslatedExpression CallWithNamedArgs(Block block) { Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); var call = (CallInstruction)block.FinalInstruction; var arguments = new ILInstruction[call.Arguments.Count]; var argumentToParameterMap = new int[arguments.Length]; int firstParamIndex = call.IsInstanceCall ? 1 : 0; // Arguments from temporary variables (VariableKind.NamedArgument): int pos = 0; foreach (StLoc stloc in block.Instructions) { Debug.Assert(stloc.Variable.LoadInstructions.Single().Parent == call); arguments[pos] = stloc.Value; argumentToParameterMap[pos] = stloc.Variable.LoadInstructions.Single().ChildIndex - firstParamIndex; pos++; } // Remaining argument: foreach (var arg in call.Arguments) { if (arg.MatchLdLoc(out var v) && v.Kind == VariableKind.NamedArgument) { continue; // already handled in loop above } arguments[pos] = arg; argumentToParameterMap[pos] = arg.ChildIndex - firstParamIndex; pos++; } Debug.Assert(pos == arguments.Length); return Build(call.OpCode, call.Method, arguments, argumentToParameterMap, call.ConstrainedTo) .WithILInstruction(call).WithILInstruction(block); } private bool HandleRangeConstruction(out ExpressionWithResolveResult result, OpCode callOpCode, IMethod method, TranslatedExpression target, ArgumentList argumentList) { result = default; if (argumentList.ArgumentNames != null) { return false; // range syntax doesn't support named arguments } if (method.DeclaringType.IsKnownType(KnownTypeCode.Range)) { if (callOpCode == OpCode.NewObj && argumentList.Length == 2) { result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, argumentList.Arguments[1]) .WithRR(new MemberResolveResult(null, method)); return true; } else if (callOpCode == OpCode.Call && method.Name == "get_All" && argumentList.Length == 0) { result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, Expression.Null) .WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method)); return true; } else if (callOpCode == OpCode.Call && method.Name == "StartAt" && argumentList.Length == 1) { result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, Expression.Null) .WithRR(new MemberResolveResult(null, method)); return true; } else if (callOpCode == OpCode.Call && method.Name == "EndAt" && argumentList.Length == 1) { result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, argumentList.Arguments[0]) .WithRR(new MemberResolveResult(null, method)); return true; } } else if (callOpCode == OpCode.NewObj && method.DeclaringType.IsKnownType(KnownTypeCode.Index)) { if (argumentList.Length != 2) return false; if (!(argumentList.Arguments[1].Expression is PrimitiveExpression pe && pe.Value is true)) return false; result = new UnaryOperatorExpression(UnaryOperatorType.IndexFromEnd, argumentList.Arguments[0]) .WithRR(new MemberResolveResult(null, method)); return true; } else if (method is SyntheticRangeIndexAccessor rangeIndexAccessor && rangeIndexAccessor.IsSlicing) { // For slicing the method is called Slice()/Substring(), but we still need to output indexer notation. // So special-case range-based slicing here. result = new IndexerExpression(target, argumentList.Arguments.Select(a => a.Expression)) .WithRR(new MemberResolveResult(target.ResolveResult, method)); return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs ================================================ // Copyright (c) 2014-2020 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using ExpressionType = System.Linq.Expressions.ExpressionType; using PrimitiveType = ICSharpCode.Decompiler.CSharp.Syntax.PrimitiveType; namespace ICSharpCode.Decompiler.CSharp { /// /// Translates from ILAst to C# expressions. /// /// /// Every translated expression must have: /// * an ILInstruction annotation /// * a ResolveResult annotation /// Post-condition for Translate() calls: /// * The type of the ResolveResult must match the StackType of the corresponding ILInstruction, /// except that the width of integer types does not need to match (I4, I and I8 count as the same stack type here) /// * Evaluating the resulting C# expression shall produce the same side effects as evaluating the ILInstruction. /// * If the IL instruction has ResultType == StackType.Void, the C# expression may evaluate to an arbitrary type and value. /// * Otherwise, evaluating the resulting C# expression shall produce a similar value as evaluating the ILInstruction. /// * If the IL instruction evaluates to an integer stack type (I4, I, or I8), /// the C# type of the resulting expression shall also be an integer (or enum/pointer/char/bool) type. /// * If sizeof(C# type) == sizeof(IL stack type), the values must be the same. /// * If sizeof(C# type) > sizeof(IL stack type), the C# value truncated to the width of the IL stack type must equal the IL value. /// * If sizeof(C# type) < sizeof(IL stack type), the C# value (sign/zero-)extended to the width of the IL stack type /// must equal the IL value. /// Whether sign or zero extension is used depends on the sign of the C# type (as determined by IType.GetSign()). /// * If the IL instruction is a lifted nullable operation, and the underlying operation evaluates to an integer stack type, /// the C# type of the resulting expression shall be Nullable{T}, where T is an integer type (as above). /// The C# value shall be null iff the IL-level value evaluates to null, and otherwise the values shall correspond /// as with non-lifted integer operations. /// * If the IL instruction evaluates to a managed reference (Ref) created by starting tracking of an unmanaged reference, /// the C# instruction may evaluate to any integral/enum/pointer type that when converted to pointer type /// is equivalent to the managed reference. /// * Otherwise, the C# type of the resulting expression shall match the IL stack type, /// and the evaluated values shall be the same. /// sealed class ExpressionBuilder : ILVisitor { internal readonly StatementBuilder statementBuilder; readonly IDecompilerTypeSystem typeSystem; internal readonly ITypeResolveContext decompilationContext; internal readonly ILFunction currentFunction; internal readonly ICompilation compilation; internal readonly CSharpResolver resolver; internal readonly TypeSystemAstBuilder astBuilder; internal readonly TypeInference typeInference; internal readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, DecompileRun decompileRun, CancellationToken cancellationToken) { Debug.Assert(decompilationContext != null); this.statementBuilder = statementBuilder; this.typeSystem = typeSystem; this.decompilationContext = decompilationContext; this.currentFunction = currentFunction; this.settings = settings; this.cancellationToken = cancellationToken; this.compilation = decompilationContext.Compilation; this.resolver = new CSharpResolver(new CSharpTypeResolveContext( compilation.MainModule, decompileRun.UsingScope, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember )); this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AddResolveResultAnnotations = true; this.astBuilder.ShowAttributes = true; this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; this.astBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal; this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; } public AstType ConvertType(IType type) { var astType = astBuilder.ConvertType(type); Debug.Assert(astType.Annotation() != null); return astType; } public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, bool allowImplicitConversion = false) { var expr = astBuilder.ConvertConstantValue(rr); if (!allowImplicitConversion) { if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) { expr = new CastExpression(ConvertType(rr.Type), expr); } else if (rr.Type.IsCSharpSmallIntegerType()) { expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr); // Note: no unchecked annotation necessary, because the constant was folded to be in-range } else if (rr.Type.IsCSharpNativeIntegerType()) { expr = new CastExpression(new PrimitiveType(rr.Type.Name), expr); // Note: no unchecked annotation necessary, because the rr wouldn't be a constant if the value wasn't in-range on 32bit } } var exprRR = expr.Annotation(); if (exprRR == null) { exprRR = rr; expr.AddAnnotation(rr); } return new ExpressionWithResolveResult(expr, exprRR); } public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, bool allowImplicitConversion = false, bool displayAsHex = false) { astBuilder.PrintIntegralValuesAsHex = displayAsHex; try { return ConvertConstantValue(rr, allowImplicitConversion); } finally { astBuilder.PrintIntegralValuesAsHex = false; } } public TranslatedExpression Translate(ILInstruction inst, IType typeHint = null) { Debug.Assert(inst != null); cancellationToken.ThrowIfCancellationRequested(); TranslationContext context = new TranslationContext { TypeHint = typeHint ?? SpecialType.UnknownType }; var cexpr = inst.AcceptVisitor(this, context); #if DEBUG if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) { // Validate the Translate post-condition (documented at beginning of this file): if (inst.ResultType.IsIntegerType()) { Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); Debug.Assert(cexpr.Type.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); } else if (inst is ILiftableInstruction liftable && liftable.IsLifted) { if (liftable.UnderlyingResultType != StackType.Unknown) { Debug.Assert(NullableType.IsNullable(cexpr.Type)); IType underlying = NullableType.GetUnderlyingType(cexpr.Type); if (liftable.UnderlyingResultType.IsIntegerType()) { Debug.Assert(underlying.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); Debug.Assert(underlying.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); } else { Debug.Assert(underlying.GetStackType() == liftable.UnderlyingResultType); } } } else if (inst.ResultType == StackType.Ref) { Debug.Assert(cexpr.Type.GetStackType() == StackType.Ref || cexpr.Type.GetStackType().IsIntegerType()); } else { Debug.Assert(cexpr.Type.GetStackType() == inst.ResultType); } } #endif return cexpr; } public TranslatedExpression TranslateCondition(ILInstruction condition, bool negate = false) { Debug.Assert(condition.ResultType == StackType.I4); var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean)); if (expr.Type.GetStackType().GetSize() > 4) { expr = expr.ConvertTo(FindType(StackType.I4, expr.Type.GetSign()), this); } return expr.ConvertToBoolean(this, negate); } internal ExpressionWithResolveResult ConvertVariable(ILVariable variable) { Expression expr; if (variable.Kind == VariableKind.Parameter && variable.Index < 0) expr = new ThisReferenceExpression(); else expr = new IdentifierExpression(variable.Name); if (variable.Type.Kind == TypeKind.ByReference) { // When loading a by-ref parameter, use 'ref paramName'. // We'll strip away the 'ref' when dereferencing. // Ensure that the IdentifierExpression itself also gets a resolve result, as that might // get used after the 'ref' is stripped away: var elementType = ((ByReferenceType)variable.Type).ElementType; var elementRR = new ILVariableResolveResult(variable, elementType); expr.WithRR(elementRR); expr = new DirectionExpression(FieldDirection.Ref, expr); return expr.WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); } else { return expr.WithRR(new ILVariableResolveResult(variable, variable.Type)); } } internal bool HidesVariableWithName(string name) { return HidesVariableWithName(currentFunction, name); } internal static bool HidesVariableWithName(ILFunction currentFunction, string name) { return currentFunction.Ancestors.OfType().Any(HidesVariableOrNestedFunction); bool HidesVariableOrNestedFunction(ILFunction function) { foreach (var v in function.Variables) { if (v.Name == name) return true; } foreach (var f in function.LocalFunctions) { if (f.Name == name) return true; } return false; } } internal ILFunction ResolveLocalFunction(IMethod method) { Debug.Assert(method.IsLocalFunction); method = (IMethod)((IMethod)method.MemberDefinition).ReducedFrom.MemberDefinition; foreach (var parent in currentFunction.Ancestors.OfType()) { var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.MemberDefinition.Equals(method)); if (definition != null) { return definition; } } return null; } bool RequiresQualifier(IMember member, TranslatedExpression target) { if (settings.AlwaysQualifyMemberReferences || HidesVariableWithName(member.Name)) return true; if (member.IsStatic) return !IsCurrentOrContainingType(member.DeclaringTypeDefinition); return !(target.Expression is ThisReferenceExpression || target.Expression is BaseReferenceExpression); } ExpressionWithResolveResult ConvertField(IField field, ILInstruction targetInstruction = null) { var target = TranslateTarget(targetInstruction, nonVirtualInvocation: true, memberStatic: field.IsStatic, memberDeclaringType: field.DeclaringType); bool requireTarget; // If this is a reference to the backing field of an automatic property and we're going to transform automatic properties // in PatternStatementTransform, then we have to do the "requires qualifier"-check based on the property instead of the field. // It is easier to solve this special case here than in PatternStatementTransform, because here we perform all resolver checks. // It feels a bit hacky, though. if (settings.AutomaticProperties && PatternStatementTransform.IsBackingFieldOfAutomaticProperty(field, out var property) && decompilationContext.CurrentMember != property && (property.CanSet || settings.GetterOnlyAutomaticProperties)) { requireTarget = RequiresQualifier(property, target); } else { requireTarget = RequiresQualifier(field, target); } bool targetCasted = false; var targetResolveResult = requireTarget ? target.ResolveResult : null; bool IsAmbiguousAccess(out MemberResolveResult result) { if (targetResolveResult == null) { result = resolver.ResolveSimpleName(field.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; } else { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList.Instance, isInvocation: false) as MemberResolveResult; } return result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure); } MemberResolveResult mrr; while (IsAmbiguousAccess(out mrr)) { if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; } else if (!targetCasted) { targetCasted = true; target = target.ConvertTo(field.DeclaringType, this); targetResolveResult = target.ResolveResult; } else { // the field reference is still ambiguous, however, mrr might refer to a different member, // e.g., in the case of auto events, their backing fields have the same name. // "this.Event" is ambiguous, but should refer to the field, not the event. mrr = null; break; } } if (mrr == null) { mrr = new MemberResolveResult(target.ResolveResult, field); } var expr = requireTarget ? new MemberReferenceExpression(target, field.Name).WithRR(mrr) : new IdentifierExpression(field.Name).WithRR(mrr); if (field.Type.Kind == TypeKind.ByReference) { expr = new DirectionExpression(FieldDirection.Ref, expr) .WithRR(new ByReferenceResolveResult(mrr, ReferenceKind.Ref)); } return expr; } TranslatedExpression IsType(IsInst inst) { var arg = Translate(inst.Argument); arg = UnwrapBoxingConversion(arg); return new IsExpression(arg.Expression, ConvertType(inst.Type.TupleUnderlyingTypeOrSelf())) .WithILInstruction(inst) .WithRR(new TypeIsResolveResult(arg.ResolveResult, inst.Type, compilation.FindType(TypeCode.Boolean))); } protected internal override TranslatedExpression VisitIsInst(IsInst inst, TranslationContext context) { var arg = Translate(inst.Argument); if (inst.Type.IsReferenceType != true) { // isinst with a value type results in an expression of "boxed value type", // which is not supported in C#. // It's also not supported for unconstrained generic types. // Note that several other instructions special-case isinst arguments: // unbox.any T(isinst T(expr)) ==> "expr as T" for nullable value types and class-constrained generic types // comp(isinst T(expr) != null) ==> "expr is T" // on block level (StatementBuilder.VisitIsInst) => "expr is T" if (SemanticHelper.IsPure(inst.Argument.Flags) || (inst.Argument is Box box && SemanticHelper.IsPure(box.Argument.Flags))) { // We can emulate isinst using // expr is T ? expr : null // (doubling the boxing side-effect is harmless because the "expr is T" part won't observe object identity, // and we need to support this because Roslyn pattern matching sometimes generates such code.) return new ConditionalExpression( new IsExpression(arg, ConvertType(inst.Type)).WithILInstruction(inst), arg.Expression.Clone(), new NullReferenceExpression() ).WithoutILInstruction().WithRR(new ResolveResult(arg.Type)); } else { return ErrorExpression("isinst with value type is only supported in some contexts"); } } arg = UnwrapBoxingConversion(arg); return new AsExpression(arg.Expression, ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); } internal static TranslatedExpression UnwrapBoxingConversion(TranslatedExpression arg) { if (arg.Expression is CastExpression cast && arg.Type.IsKnownType(KnownTypeCode.Object) && arg.ResolveResult is ConversionResolveResult crr && crr.Conversion.IsBoxingConversion) { // When 'is' or 'as' is used with a value type or type parameter, // the C# compiler implicitly boxes the input. arg = arg.UnwrapChild(cast.Expression); } return arg; } protected internal override TranslatedExpression VisitNewObj(NewObj inst, TranslationContext context) { var type = inst.Method.DeclaringType; if (type.IsKnownType(KnownTypeCode.SpanOfT) || type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { if (inst.Arguments.Count == 2 && inst.Arguments[0] is Block b && b.Kind == BlockKind.StackAllocInitializer) { return TranslateStackAllocInitializer(b, type.TypeArguments[0]); } } return new CallBuilder(this, typeSystem, settings).Build(inst, context.TypeHint); } protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context) { return new CallBuilder(this, typeSystem, settings).Build(inst); } protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context) { var dimensions = inst.Indices.Count; var args = inst.Indices.Select(arg => TranslateArrayIndex(arg)).ToArray(); var expr = new ArrayCreateExpression { Type = ConvertType(inst.Type) }; if (expr.Type is ComposedType ct) { // change "new (int[,])[10] to new int[10][,]" ct.ArraySpecifiers.MoveTo(expr.AdditionalArraySpecifiers); } expr.Arguments.AddRange(args.Select(arg => arg.Expression)); return expr.WithILInstruction(inst) .WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), Empty.Array)); } protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context) { return TranslateLocAlloc(inst, context.TypeHint, out var elementType) .WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType))); } protected internal override TranslatedExpression VisitLocAllocSpan(LocAllocSpan inst, TranslationContext context) { return TranslateLocAllocSpan(inst, context.TypeHint, out _) .WithILInstruction(inst).WithRR(new ResolveResult(inst.Type)); } StackAllocExpression TranslateLocAllocSpan(LocAllocSpan inst, IType typeHint, out IType elementType) { elementType = inst.Type.TypeArguments[0]; TranslatedExpression countExpression = Translate(inst.Argument) .ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); return new StackAllocExpression { Type = ConvertType(elementType), CountExpression = countExpression }; } StackAllocExpression TranslateLocAlloc(LocAlloc inst, IType typeHint, out IType elementType) { TranslatedExpression countExpression; PointerType pointerType; if (inst.Argument.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out var left, out var right) && right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out elementType)) { // Determine the element type from the sizeof countExpression = Translate(left.UnwrapConv(ConversionKind.ZeroExtend)); pointerType = new PointerType(elementType); } else { // Determine the element type from the expected pointer type in this context pointerType = typeHint as PointerType; if (pointerType != null && GetPointerArithmeticOffset( inst.Argument, Translate(inst.Argument), pointerType.ElementType, checkForOverflow: true, unwrapZeroExtension: true ) is TranslatedExpression offset) { countExpression = offset; elementType = pointerType.ElementType; } else { elementType = compilation.FindType(KnownTypeCode.Byte); pointerType = new PointerType(elementType); countExpression = Translate(inst.Argument); } } countExpression = countExpression.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); return new StackAllocExpression { Type = ConvertType(elementType), CountExpression = countExpression }; } protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context) { ResolveResult rr; if (context.TypeHint.GetSign() == Sign.Unsigned) { rr = new ConstantResolveResult( compilation.FindType(KnownTypeCode.UInt32), unchecked((uint)inst.Value) ); } else { rr = new ConstantResolveResult( compilation.FindType(KnownTypeCode.Int32), inst.Value ); } rr = AdjustConstantToType(rr, context.TypeHint); return ConvertConstantValue( rr, allowImplicitConversion: true ).WithILInstruction(inst); } protected internal override TranslatedExpression VisitLdcI8(LdcI8 inst, TranslationContext context) { ResolveResult rr; if (context.TypeHint.GetSign() == Sign.Unsigned) { rr = new ConstantResolveResult( compilation.FindType(KnownTypeCode.UInt64), unchecked((ulong)inst.Value) ); } else { rr = new ConstantResolveResult( compilation.FindType(KnownTypeCode.Int64), inst.Value ); } rr = AdjustConstantToType(rr, context.TypeHint); return ConvertConstantValue( rr, allowImplicitConversion: true ).WithILInstruction(inst); } private bool ShouldDisplayAsHex(long value, IType type) { if (value >= 0 && value <= 9) return false; if (value < 0 && type.GetSign() == Sign.Signed) return false; return true; } protected internal override TranslatedExpression VisitLdcF4(LdcF4 inst, TranslationContext context) { var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Single), inst.Value); return new TranslatedExpression(expr.WithILInstruction(inst)); } protected internal override TranslatedExpression VisitLdcF8(LdcF8 inst, TranslationContext context) { var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Double), inst.Value); return new TranslatedExpression(expr.WithILInstruction(inst)); } protected internal override TranslatedExpression VisitLdcDecimal(LdcDecimal inst, TranslationContext context) { var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Decimal), inst.Value); return new TranslatedExpression(expr.WithILInstruction(inst)); } protected internal override TranslatedExpression VisitLdStr(LdStr inst, TranslationContext context) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value)); } protected internal override TranslatedExpression VisitLdStrUtf8(LdStrUtf8 inst, TranslationContext context) { var type = new ParameterizedType(compilation.FindType(KnownTypeCode.ReadOnlySpanOfT), new[] { compilation.FindType(KnownTypeCode.Byte) }); return new PrimitiveExpression(inst.Value, LiteralFormat.Utf8Literal) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(type, inst.Value)); } protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context) { return GetDefaultValueExpression(SpecialType.NullType).WithILInstruction(inst); } protected internal override TranslatedExpression VisitDefaultValue(DefaultValue inst, TranslationContext context) { return GetDefaultValueExpression(inst.Type).WithILInstruction(inst); } internal ExpressionWithResolveResult GetDefaultValueExpression(IType type) { Expression expr; IType constantType; object constantValue; if (type.IsReferenceType == true) { expr = new NullReferenceExpression(); constantType = SpecialType.NullType; constantValue = null; return expr.WithRR(new ConstantResolveResult(constantType, constantValue)); } else if (type.IsKnownType(KnownTypeCode.NullableOfT)) { expr = new NullReferenceExpression(); constantType = SpecialType.NullType; constantValue = null; var crr = new ConstantResolveResult(constantType, constantValue); return new CastExpression(ConvertType(type), expr.WithRR(crr)) .WithRR(new ConversionResolveResult(type, crr, Conversion.NullLiteralConversion)); } else { expr = new DefaultValueExpression(ConvertType(type)); constantType = type; constantValue = CSharpResolver.GetDefaultValue(type); return expr.WithRR(new ConstantResolveResult(constantType, constantValue)); } } protected internal override TranslatedExpression VisitSizeOf(SizeOf inst, TranslationContext context) { if (inst.Type.IsUnmanagedType(allowGenerics: settings.IntroduceUnmanagedConstraint)) { return new SizeOfExpression(ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new SizeOfResolveResult(compilation.FindType(KnownTypeCode.Int32), inst.Type, null)); } else { return CallUnsafeIntrinsic( name: "SizeOf", arguments: Array.Empty(), returnType: compilation.FindType(KnownTypeCode.Int32), inst: inst, typeArguments: new[] { inst.Type } ); } } protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst, TranslationContext context) { var typeofExpr = new TypeOfExpression(ConvertType(inst.Type)) .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); return new MemberReferenceExpression(typeofExpr, "TypeHandle") .WithILInstruction(inst) .WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); } protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) { var argument = Translate(inst.Argument); var argUType = NullableType.GetUnderlyingType(argument.Type); if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize() || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() || (argUType.GetStackType() == StackType.I && !argUType.IsCSharpNativeIntegerType()) || argUType.IsKnownType(KnownTypeCode.Boolean) || argUType.IsKnownType(KnownTypeCode.Char)) { // Argument is undersized (even after implicit integral promotion to I4) // -> we need to perform sign/zero-extension before the BitNot. // Same if the argument is an enum based on a small integer type // (those don't undergo numeric promotion in C# the way non-enum small integer types do). // Same if the type is one that does not support ~ (IntPtr, bool and char). Sign sign = context.TypeHint.GetSign(); if (sign == Sign.None) { sign = argUType.GetSign(); } IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); if (inst.IsLifted) { targetType = NullableType.Create(compilation, targetType); } argument = argument.ConvertTo(targetType, this); } return new UnaryOperatorExpression(UnaryOperatorType.BitNot, argument) .WithRR(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, argument.ResolveResult)) .WithILInstruction(inst); } internal ExpressionWithResolveResult LogicNot(TranslatedExpression expr) { // "!expr" implicitly converts to bool so we can remove the cast; // but only if doing so wouldn't cause us to call a user-defined "operator !" expr = expr.UnwrapImplicitBoolConversion(type => !type.GetMethods(m => m.IsOperator && m.Name == "op_LogicalNot").Any()); return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression) .WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not, expr.ResolveResult)); } readonly HashSet loadedVariablesSet = new HashSet(); protected internal override TranslatedExpression VisitLdLoc(LdLoc inst, TranslationContext context) { if (inst.Variable.Kind == VariableKind.StackSlot && inst.Variable.IsSingleDefinition) { loadedVariablesSet.Add(inst.Variable); } return ConvertVariable(inst.Variable).WithILInstruction(inst); } protected internal override TranslatedExpression VisitLdLoca(LdLoca inst, TranslationContext context) { var expr = ConvertVariable(inst.Variable).WithILInstruction(inst); // Note that we put the instruction on the IdentifierExpression instead of the DirectionExpression, // because the DirectionExpression might get removed by dereferencing instructions such as LdObj return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) { var translatedValue = Translate(inst.Value, typeHint: inst.Variable.Type); if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) { // Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O) // so we should replace them with more accurate types where possible: if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type) && inst.Variable.StackType == translatedValue.Type.GetStackType() && translatedValue.Type.Kind != TypeKind.Null) { inst.Variable.Type = translatedValue.Type; } else if (inst.Value.MatchDefaultValue(out var type) && IsOtherValueType(type)) { inst.Variable.Type = type; } } var lhs = ConvertVariable(inst.Variable).WithoutILInstruction(); if (lhs.Expression is DirectionExpression dirExpr && lhs.ResolveResult is ByReferenceResolveResult lhsRefRR) { // ref (re-)assignment, emit "ref (a = ref b)". lhs = lhs.UnwrapChild(dirExpr.Expression); translatedValue = translatedValue.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true); var assign = new AssignmentExpression(lhs.Expression, translatedValue.Expression) .WithRR(new OperatorResolveResult(lhs.Type, ExpressionType.Assign, lhsRefRR, translatedValue.ResolveResult)); return new DirectionExpression(FieldDirection.Ref, assign) .WithoutILInstruction().WithRR(lhsRefRR); } else { return Assignment(lhs, translatedValue).WithILInstruction(inst); } bool CanUseTypeForStackSlot(ILVariable v, IType type) { return v.IsSingleDefinition || IsOtherValueType(type) || v.StackType == StackType.Ref || AllStoresUseConsistentType(v.StoreInstructions, type); } bool IsOtherValueType(IType type) { return type.IsReferenceType == false && type.GetStackType() == StackType.O; } bool AllStoresUseConsistentType(IReadOnlyList storeInstructions, IType expectedType) { expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); foreach (var store in storeInstructions) { if (!(store is StLoc stloc)) return false; IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure); if (!type.Equals(expectedType)) return false; } return true; } } protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context) { if (inst.LiftingKind == ComparisonLiftingKind.ThreeValuedLogic) { if (inst.Kind == ComparisonKind.Equality && inst.Right.MatchLdcI4(0)) { // lifted logic.not var targetType = NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); var arg = Translate(inst.Left, targetType).ConvertTo(targetType, this); return new UnaryOperatorExpression(UnaryOperatorType.Not, arg.Expression) .WithRR(new OperatorResolveResult(targetType, ExpressionType.Not, arg.ResolveResult)) .WithILInstruction(inst); } return ErrorExpression("Nullable comparisons with three-valued-logic not supported in C#"); } if (inst.InputType == StackType.Ref) { // Reference comparison using Unsafe intrinsics Debug.Assert(!inst.IsLifted); (string methodName, bool negate) = inst.Kind switch { ComparisonKind.Equality => ("AreSame", false), ComparisonKind.Inequality => ("AreSame", true), ComparisonKind.LessThan => ("IsAddressLessThan", false), ComparisonKind.LessThanOrEqual => ("IsAddressGreaterThan", true), ComparisonKind.GreaterThan => ("IsAddressGreaterThan", false), ComparisonKind.GreaterThanOrEqual => ("IsAddressLessThan", true), _ => throw new InvalidOperationException("Invalid ComparisonKind") }; var left = Translate(inst.Left); var right = Translate(inst.Right); if (left.Type.Kind != TypeKind.ByReference || !NormalizeTypeVisitor.TypeErasure.EquivalentTypes(left.Type, right.Type)) { IType commonRefType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); left = left.ConvertTo(commonRefType, this); right = right.ConvertTo(commonRefType, this); } IType boolType = compilation.FindType(KnownTypeCode.Boolean); TranslatedExpression expr = CallUnsafeIntrinsic( name: methodName, arguments: new Expression[] { left, right }, returnType: boolType, inst: inst ); if (negate) { expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr) .WithoutILInstruction().WithRR(new ResolveResult(boolType)); } return expr; } if (inst.Kind.IsEqualityOrInequality()) { var result = TranslateCeq(inst, out bool negateOutput); if (negateOutput) return LogicNot(result).WithILInstruction(inst); else return result; } else { return TranslateComp(inst); } } /// /// Translates the equality comparison between left and right. /// TranslatedExpression TranslateCeq(Comp inst, out bool negateOutput) { Debug.Assert(inst.Kind.IsEqualityOrInequality()); // Translate '(e as T) == null' to '!(e is T)'. // This is necessary for correctness when T is a value type. if (inst.Left.OpCode == OpCode.IsInst && inst.Right.OpCode == OpCode.LdNull) { negateOutput = inst.Kind == ComparisonKind.Equality; return IsType((IsInst)inst.Left); } else if (inst.Right.OpCode == OpCode.IsInst && inst.Left.OpCode == OpCode.LdNull) { negateOutput = inst.Kind == ComparisonKind.Equality; return IsType((IsInst)inst.Right); } var left = Translate(inst.Left); var right = Translate(inst.Right); // Remove redundant bool comparisons if (left.Type.IsKnownType(KnownTypeCode.Boolean)) { if (inst.Right.MatchLdcI4(0)) { // 'b == 0' => '!b' // 'b != 0' => 'b' negateOutput = inst.Kind == ComparisonKind.Equality; return left; } if (inst.Right.MatchLdcI4(1)) { // 'b == 1' => 'b' // 'b != 1' => '!b' negateOutput = inst.Kind == ComparisonKind.Inequality; return left; } } else if (right.Type.IsKnownType(KnownTypeCode.Boolean)) { if (inst.Left.MatchLdcI4(0)) { // '0 == b' => '!b' // '0 != b' => 'b' negateOutput = inst.Kind == ComparisonKind.Equality; return right; } if (inst.Left.MatchLdcI4(1)) { // '1 == b' => 'b' // '1 != b' => '!b' negateOutput = inst.Kind == ComparisonKind.Inequality; return right; } } // Handle comparisons between unsafe pointers and null: if (left.Type.Kind == TypeKind.Pointer && inst.Right.MatchLdcI(0)) { negateOutput = false; right = new NullReferenceExpression().WithRR(new ConstantResolveResult(SpecialType.NullType, null)) .WithILInstruction(inst.Right); return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) .WithILInstruction(inst); } else if (right.Type.Kind == TypeKind.Pointer && inst.Left.MatchLdcI(0)) { negateOutput = false; left = new NullReferenceExpression().WithRR(new ConstantResolveResult(SpecialType.NullType, null)) .WithILInstruction(inst.Left); return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) .WithILInstruction(inst); } // Special case comparisons with enum and char literals left = TryUniteEqualityOperandType(left, right); right = TryUniteEqualityOperandType(right, left); if (IsSpecialCasedReferenceComparisonWithNull(left, right)) { // When comparing a string/delegate with null, the C# compiler generates a reference comparison. negateOutput = false; return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) .WithILInstruction(inst); } OperatorResolveResult rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) as OperatorResolveResult; if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null || NullableType.GetUnderlyingType(rr.Operands[0].Type).GetStackType() != inst.InputType || !rr.Type.IsKnownType(KnownTypeCode.Boolean)) { IType targetType; if (inst.InputType == StackType.O) { targetType = compilation.FindType(KnownTypeCode.Object); } else { var leftUType = NullableType.GetUnderlyingType(left.Type); var rightUType = NullableType.GetUnderlyingType(right.Type); if (leftUType.GetStackType() == inst.InputType && !leftUType.IsSmallIntegerType()) { targetType = leftUType; } else if (rightUType.GetStackType() == inst.InputType && !rightUType.IsSmallIntegerType()) { targetType = rightUType; } else { targetType = FindType(inst.InputType, leftUType.GetSign()); } } if (inst.IsLifted) { targetType = NullableType.Create(compilation, targetType); } if (targetType.Equals(left.Type)) { right = right.ConvertTo(targetType, this); } else { left = left.ConvertTo(targetType, this); } rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) as OperatorResolveResult; if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null || NullableType.GetUnderlyingType(rr.Operands[0].Type).GetStackType() != inst.InputType || !rr.Type.IsKnownType(KnownTypeCode.Boolean)) { // If converting one input wasn't sufficient, convert both: left = left.ConvertTo(targetType, this); right = right.ConvertTo(targetType, this); rr = new OperatorResolveResult( compilation.FindType(KnownTypeCode.Boolean), BinaryOperatorExpression.GetLinqNodeType(inst.Kind.ToBinaryOperatorType(), false), left.ResolveResult, right.ResolveResult); } } negateOutput = false; return new BinaryOperatorExpression(left.Expression, inst.Kind.ToBinaryOperatorType(), right.Expression) .WithILInstruction(inst) .WithRR(rr); } TranslatedExpression TryUniteEqualityOperandType(TranslatedExpression left, TranslatedExpression right) { // Special case for enum flag check "(enum & EnumType.SomeValue) == 0" // so that the const 0 value is printed as 0 integer and not as enum type, e.g. EnumType.None if (left.ResolveResult.IsCompileTimeConstant && left.ResolveResult.Type.IsCSharpPrimitiveIntegerType() && (left.ResolveResult.ConstantValue as int?) == 0 && NullableType.GetUnderlyingType(right.Type).Kind == TypeKind.Enum && right.Expression is BinaryOperatorExpression binaryExpr && binaryExpr.Operator == BinaryOperatorType.BitwiseAnd) { return AdjustConstantExpressionToType(left, compilation.FindType(KnownTypeCode.Int32)); } else return AdjustConstantExpressionToType(left, right.Type); } bool IsSpecialCasedReferenceComparisonWithNull(TranslatedExpression lhs, TranslatedExpression rhs) { if (lhs.Type.Kind == TypeKind.Null) ExtensionMethods.Swap(ref lhs, ref rhs); return rhs.Type.Kind == TypeKind.Null && (lhs.Type.Kind == TypeKind.Delegate || lhs.Type.IsKnownType(KnownTypeCode.String)) && lhs.Type.GetDefinition() != decompilationContext.CurrentTypeDefinition; } ExpressionWithResolveResult CreateBuiltinBinaryOperator( TranslatedExpression left, BinaryOperatorType type, TranslatedExpression right, bool checkForOverflow = false) { return new BinaryOperatorExpression(left.Expression, type, right.Expression) .WithRR(new OperatorResolveResult( compilation.FindType(KnownTypeCode.Boolean), BinaryOperatorExpression.GetLinqNodeType(type, checkForOverflow), left.ResolveResult, right.ResolveResult)); } /// /// Handle Comp instruction, operators other than equality/inequality. /// TranslatedExpression TranslateComp(Comp inst) { var op = inst.Kind.ToBinaryOperatorType(); var left = Translate(inst.Left); var right = Translate(inst.Right); if (left.Type.Kind == TypeKind.Pointer && right.Type.Kind == TypeKind.Pointer) { return CreateBuiltinBinaryOperator(left, op, right) .WithILInstruction(inst); } left = PrepareArithmeticArgument(left, inst.InputType, inst.Sign, inst.IsLifted); right = PrepareArithmeticArgument(right, inst.InputType, inst.Sign, inst.IsLifted); // Special case comparisons with enum and char literals left = AdjustConstantExpressionToType(left, right.Type); right = AdjustConstantExpressionToType(right, left.Type); // attempt comparison without any additional casts var rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) as OperatorResolveResult; if (rr != null && !rr.IsError) { IType compUType = NullableType.GetUnderlyingType(rr.Operands[0].Type); if (compUType.GetSign() == inst.Sign && compUType.GetStackType() == inst.InputType) { return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); } } if (inst.InputType.IsIntegerType()) { // Ensure the inputs have the correct sign: IType inputType = FindArithmeticType(inst.InputType, inst.Sign); if (inst.IsLifted) { inputType = NullableType.Create(compilation, inputType); } left = left.ConvertTo(inputType, this); right = right.ConvertTo(inputType, this); } else if (inst.InputType == StackType.O) { // Unsafe.As(ref left) op Unsafe.As(ref right) // TTo Unsafe.As(ref TFrom source) var integerType = compilation.FindType(inst.Sign == Sign.Signed ? KnownTypeCode.IntPtr : KnownTypeCode.UIntPtr); left = WrapInUnsafeAs(left, inst.Left); right = WrapInUnsafeAs(right, inst.Right); TranslatedExpression WrapInUnsafeAs(TranslatedExpression expr, ILInstruction inst) { var type = expr.Type; expr = WrapInRef(expr, new ByReferenceType(type)); return CallUnsafeIntrinsic("As", [expr], integerType, typeArguments: [type, integerType]); } } return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(compilation.FindType(TypeCode.Boolean), BinaryOperatorExpression.GetLinqNodeType(op, false), left.ResolveResult, right.ResolveResult)); } protected internal override TranslatedExpression VisitThreeValuedBoolAnd(ThreeValuedBoolAnd inst, TranslationContext context) { return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseAnd, ExpressionType.And); } protected internal override TranslatedExpression VisitThreeValuedBoolOr(ThreeValuedBoolOr inst, TranslationContext context) { return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseOr, ExpressionType.Or); } TranslatedExpression HandleThreeValuedLogic(BinaryInstruction inst, BinaryOperatorType op, ExpressionType eop) { var left = Translate(inst.Left); var right = Translate(inst.Right); IType boolType = compilation.FindType(KnownTypeCode.Boolean); IType nullableBoolType = NullableType.Create(compilation, boolType); if (NullableType.IsNullable(left.Type)) { left = left.ConvertTo(nullableBoolType, this); if (NullableType.IsNullable(right.Type)) { right = right.ConvertTo(nullableBoolType, this); } else { right = right.ConvertTo(boolType, this); } } else { left = left.ConvertTo(boolType, this); right = right.ConvertTo(nullableBoolType, this); } return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithRR(new OperatorResolveResult(nullableBoolType, eop, null, true, new[] { left.ResolveResult, right.ResolveResult })) .WithILInstruction(inst); } protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) { return new ThrowExpression(Translate(inst.Argument)) .WithILInstruction(inst) .WithRR(new ThrowResolveResult()); } protected internal override TranslatedExpression VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, TranslationContext context) { var left = Translate(inst.Left, inst.Method.Parameters[0].Type).ConvertTo(inst.Method.Parameters[0].Type, this); var right = Translate(inst.Right, inst.Method.Parameters[1].Type).ConvertTo(inst.Method.Parameters[1].Type, this); BinaryOperatorType op; if (inst.Method.Name == "op_BitwiseAnd") { op = BinaryOperatorType.ConditionalAnd; } else if (inst.Method.Name == "op_BitwiseOr") { op = BinaryOperatorType.ConditionalOr; } else { throw new InvalidOperationException("Invalid method name"); } return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithRR(new InvocationResolveResult(null, inst.Method, new ResolveResult[] { left.ResolveResult, right.ResolveResult })) .WithILInstruction(inst); } ExpressionWithResolveResult Assignment(TranslatedExpression left, TranslatedExpression right) { right = right.ConvertTo(left.Type, this, allowImplicitConversion: true); return new AssignmentExpression(left.Expression, right.Expression) .WithRR(new OperatorResolveResult(left.Type, ExpressionType.Assign, left.ResolveResult, right.ResolveResult)); } protected internal override TranslatedExpression VisitBinaryNumericInstruction(BinaryNumericInstruction inst, TranslationContext context) { switch (inst.Operator) { case BinaryNumericOperator.Add: return HandleBinaryNumeric(inst, BinaryOperatorType.Add, context); case BinaryNumericOperator.Sub: return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract, context); case BinaryNumericOperator.Mul: return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply, context); case BinaryNumericOperator.Div: return HandlePointerSubtraction(inst) ?? HandleBinaryNumeric(inst, BinaryOperatorType.Divide, context); case BinaryNumericOperator.Rem: return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus, context); case BinaryNumericOperator.BitAnd: return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd, context); case BinaryNumericOperator.BitOr: return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr, context); case BinaryNumericOperator.BitXor: return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context); case BinaryNumericOperator.ShiftLeft: return HandleShift(inst, BinaryOperatorType.ShiftLeft, context); case BinaryNumericOperator.ShiftRight: return HandleShift(inst, BinaryOperatorType.ShiftRight, context); default: throw new ArgumentOutOfRangeException(); } } /// /// Translates pointer arithmetic: /// ptr + int /// int + ptr /// ptr - int /// Returns null if 'inst' is not performing pointer arithmetic. /// 'ptr - ptr' is not handled here, but in HandlePointerSubtraction()! /// TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right, TranslationContext context) { if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub)) return null; if (inst.CheckForOverflow || inst.IsLifted) return null; if (!(inst.LeftInputType == StackType.I && inst.RightInputType == StackType.I)) return null; PointerType pointerType; ILInstruction byteOffsetInst; TranslatedExpression byteOffsetExpr; if (left.Type.Kind == TypeKind.Pointer) { byteOffsetInst = inst.Right; byteOffsetExpr = right; pointerType = (PointerType)left.Type; } else if (right.Type.Kind == TypeKind.Pointer) { if (inst.Operator != BinaryNumericOperator.Add) return null; byteOffsetInst = inst.Left; byteOffsetExpr = left; pointerType = (PointerType)right.Type; } else { return null; } TranslatedExpression? offsetExpressionFromTypeHint = null; if (context.TypeHint.Kind == TypeKind.Pointer) { // We use the type hint if one of the following is true: // * The current element type is a non-primitive struct. // * The current element type has a different size than the type hint element type. // This prevents the type hint from overriding in undesirable situations (eg changing char* to short*). var typeHint = (PointerType)context.TypeHint; int elementTypeSize = pointerType.ElementType.GetSize(); if (elementTypeSize == 0 || typeHint.ElementType.GetSize() != elementTypeSize) { offsetExpressionFromTypeHint = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, typeHint.ElementType, inst.CheckForOverflow); if (offsetExpressionFromTypeHint != null) { pointerType = typeHint; } } } TranslatedExpression offsetExpr = offsetExpressionFromTypeHint ?? GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow) ?? FallBackToBytePointer(); if (left.Type.Kind == TypeKind.Pointer) { Debug.Assert(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub); left = left.ConvertTo(pointerType, this); right = offsetExpr; } else { Debug.Assert(inst.Operator == BinaryNumericOperator.Add); Debug.Assert(right.Type.Kind == TypeKind.Pointer); left = offsetExpr; right = right.ConvertTo(pointerType, this); } var operatorType = inst.Operator == BinaryNumericOperator.Add ? BinaryOperatorType.Add : BinaryOperatorType.Subtract; return new BinaryOperatorExpression(left, operatorType, right) .WithILInstruction(inst) .WithRR(new OperatorResolveResult( pointerType, BinaryOperatorExpression.GetLinqNodeType(operatorType, inst.CheckForOverflow), left.ResolveResult, right.ResolveResult)); TranslatedExpression FallBackToBytePointer() { pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte)); return EnsureIntegerType(byteOffsetExpr); } } /// /// Translates pointer arithmetic with managed pointers: /// ref + int /// int + ref /// ref - int /// ref - ref /// TranslatedExpression? HandleManagedPointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right) { if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub)) return null; if (inst.CheckForOverflow || inst.IsLifted) return null; if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) { // ref - ref => i return CallUnsafeIntrinsic("ByteOffset", new[] { // ByteOffset() expects the parameters the wrong way around, so order using named arguments new NamedArgumentExpression("target", left.Expression), new NamedArgumentExpression("origin", right.Expression) }, compilation.FindType(KnownTypeCode.IntPtr), inst); } if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()) { // ref [+-] int var brt = left.Type as ByReferenceType; if (brt == null) { brt = GetReferenceType(left.Type); left = left.ConvertTo(brt, this); } string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add"); ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Right, brt?.ElementType, inst.CheckForOverflow); if (offsetInst != null) { if (settings.FixedBuffers && inst.Operator == BinaryNumericOperator.Add && inst.Left is LdFlda ldFlda && ldFlda.Target is LdFlda nestedLdFlda && CSharpDecompiler.IsFixedField(nestedLdFlda.Field, out var elementType, out _)) { Expression fieldAccess = ConvertField(nestedLdFlda.Field, nestedLdFlda.Target); var mrr = (MemberResolveResult)fieldAccess.GetResolveResult(); fieldAccess.RemoveAnnotations(); var result = fieldAccess.WithRR(new MemberResolveResult(mrr.TargetResult, mrr.Member, new PointerType(elementType))) .WithILInstruction(inst); right = TranslateArrayIndex(offsetInst); TranslatedExpression expr = new IndexerExpression(result.Expression, right.Expression) .WithILInstruction(inst) .WithRR(new ResolveResult(elementType)); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } right = Translate(offsetInst); right = ConvertArrayIndex(right, inst.RightInputType, allowIntPtr: true); return CallUnsafeIntrinsic(name, new[] { left.Expression, right.Expression }, brt, inst); } else { right = ConvertArrayIndex(right, inst.RightInputType, allowIntPtr: true); return CallUnsafeIntrinsic(name + "ByteOffset", new[] { left.Expression, right.Expression }, brt, inst); } } if (inst.LeftInputType == StackType.I && inst.RightInputType == StackType.Ref && inst.Operator == BinaryNumericOperator.Add) { // int + ref var brt = right.Type as ByReferenceType; if (brt == null) { brt = GetReferenceType(right.Type); right = right.ConvertTo(brt, this); } ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Left, brt.ElementType, inst.CheckForOverflow); if (offsetInst != null) { left = Translate(offsetInst); left = ConvertArrayIndex(left, inst.LeftInputType, allowIntPtr: true); return CallUnsafeIntrinsic("Add", new[] { new NamedArgumentExpression("elementOffset", left), new NamedArgumentExpression("source", right) }, brt, inst); } else { left = ConvertArrayIndex(left, inst.LeftInputType, allowIntPtr: true); return CallUnsafeIntrinsic("AddByteOffset", new[] { new NamedArgumentExpression("byteOffset", left.Expression), new NamedArgumentExpression("source", right) }, brt, inst); } } return null; ByReferenceType GetReferenceType(IType type) { if (type is PointerType pt) { return new ByReferenceType(pt.ElementType); } else { return new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); } } } internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst = null, IEnumerable typeArguments = null) { var target = new MemberReferenceExpression { Target = new TypeReferenceExpression(astBuilder.ConvertType(compilation.FindType(KnownTypeCode.Unsafe))), MemberName = name }; if (typeArguments != null) { target.TypeArguments.AddRange(typeArguments.Select(astBuilder.ConvertType)); } var invocationExpr = new InvocationExpression(target, arguments); var invocation = inst != null ? invocationExpr.WithILInstruction(inst) : invocationExpr.WithoutILInstruction(); if (returnType is ByReferenceType brt) { return WrapInRef(invocation.WithRR(new ResolveResult(brt.ElementType)), brt); } else { return invocation.WithRR(new ResolveResult(returnType)); } } TranslatedExpression EnsureIntegerType(TranslatedExpression expr) { if (!expr.Type.IsCSharpPrimitiveIntegerType() && !expr.Type.IsCSharpNativeIntegerType()) { // pointer arithmetic accepts all primitive integer types, but no enums etc. expr = expr.ConvertTo(FindArithmeticType(expr.Type.GetStackType(), expr.Type.GetSign()), this); } return expr; } TranslatedExpression? GetPointerArithmeticOffset(ILInstruction byteOffsetInst, TranslatedExpression byteOffsetExpr, IType pointerElementType, bool checkForOverflow, bool unwrapZeroExtension = false) { var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerElementType, checkForOverflow: checkForOverflow, unwrapZeroExtension: unwrapZeroExtension); if (countOffsetInst == null) { return null; } if (countOffsetInst == byteOffsetInst) { return EnsureIntegerType(byteOffsetExpr); } else { TranslatedExpression expr = Translate(countOffsetInst); // Keep original ILInstruction as annotation expr.Expression.RemoveAnnotations(); return EnsureIntegerType(expr.WithILInstruction(byteOffsetInst)); } } /// /// Called for divisions, detect and handles the code pattern: /// div(sub(a, b), sizeof(T)) /// when a,b are of type T*. /// This is what the C# compiler generates for pointer subtraction. /// TranslatedExpression? HandlePointerSubtraction(BinaryNumericInstruction inst) { Debug.Assert(inst.Operator == BinaryNumericOperator.Div); if (inst.CheckForOverflow || inst.LeftInputType != StackType.I) return null; if (!(inst.Left is BinaryNumericInstruction sub && sub.Operator == BinaryNumericOperator.Sub)) return null; if (sub.CheckForOverflow) return null; // First, attempt to parse the 'sizeof' on the RHS IType elementType; if (inst.Right.MatchLdcI(out long elementSize)) { elementType = null; // OK, might be pointer subtraction if the element size matches } else if (inst.Right.UnwrapConv(ConversionKind.SignExtend).MatchSizeOf(out elementType)) { // OK, might be pointer subtraction if the element type matches } else { return null; } var left = Translate(sub.Left); var right = Translate(sub.Right); IType pointerType; if (IsMatchingPointerType(left.Type)) { pointerType = left.Type; } else if (IsMatchingPointerType(right.Type)) { pointerType = right.Type; } else if (elementSize == 1 && left.Type.Kind == TypeKind.Pointer && right.Type.Kind == TypeKind.Pointer) { // two pointers (neither matching), we're dividing by 1 (debug builds only), // -> subtract two byte pointers pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte)); } else { // neither is a matching pointer type // -> not a pointer subtraction after all return null; } // We got a pointer subtraction. left = left.ConvertTo(pointerType, this); right = right.ConvertTo(pointerType, this); var rr = new OperatorResolveResult( compilation.FindType(KnownTypeCode.Int64), ExpressionType.Subtract, left.ResolveResult, right.ResolveResult ); var result = new BinaryOperatorExpression( left.Expression, BinaryOperatorType.Subtract, right.Expression ).WithILInstruction(new[] { inst, sub }) .WithRR(rr); return result; bool IsMatchingPointerType(IType type) { if (type is PointerType pt) { if (elementType != null) return elementType.Equals(pt.ElementType); else if (elementSize > 0) return PointerArithmeticOffset.ComputeSizeOf(pt.ElementType) == elementSize; } return false; } } TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context) { var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); bool propagateTypeHint = op.IsBitwise() && inst.LeftInputType != inst.RightInputType; var left = Translate(inst.Left, propagateTypeHint ? context.TypeHint : null); var right = Translate(inst.Right, propagateTypeHint ? context.TypeHint : null); if (inst.UnderlyingResultType == StackType.Ref) { var ptrResult = HandleManagedPointerArithmetic(inst, left, right); if (ptrResult != null) return ptrResult.Value; } if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) { var ptrResult = HandlePointerArithmetic(inst, left, right, context); if (ptrResult != null) return ptrResult.Value; } left = PrepareArithmeticArgument(left, inst.LeftInputType, inst.Sign, inst.IsLifted); right = PrepareArithmeticArgument(right, inst.RightInputType, inst.Sign, inst.IsLifted); if (op == BinaryOperatorType.Subtract && inst.Left.MatchLdcI(0)) { IType rightUType = NullableType.GetUnderlyingType(right.Type); if (rightUType.IsKnownType(KnownTypeCode.Int32) || rightUType.IsKnownType(KnownTypeCode.Int64) || rightUType.IsCSharpSmallIntegerType() || rightUType.Kind == TypeKind.NInt) { // unary minus is supported on signed int, nint and long, and on the small integer types (since they promote to int) var uoe = new UnaryOperatorExpression(UnaryOperatorType.Minus, right.Expression); uoe.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); var resultType = FindArithmeticType(inst.RightInputType, Sign.Signed); if (inst.IsLifted) resultType = NullableType.Create(compilation, resultType); return uoe.WithILInstruction(inst).WithRR(new OperatorResolveResult( resultType, inst.CheckForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate, right.ResolveResult)); } } if (op.IsBitwise() && left.Type.IsKnownType(KnownTypeCode.Boolean) && right.Type.IsKnownType(KnownTypeCode.Boolean) && SemanticHelper.IsPure(inst.Right.Flags)) { // Undo the C# compiler's optimization of "a && b" to "a & b". if (op == BinaryOperatorType.BitwiseAnd) { op = BinaryOperatorType.ConditionalAnd; } else if (op == BinaryOperatorType.BitwiseOr) { op = BinaryOperatorType.ConditionalOr; } } if (op.IsBitwise() && (left.Type.Kind == TypeKind.Enum || right.Type.Kind == TypeKind.Enum)) { left = AdjustConstantExpressionToType(left, right.Type); right = AdjustConstantExpressionToType(right, left.Type); } var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); if (rr.IsError || NullableType.GetUnderlyingType(rr.Type).GetStackType() != inst.UnderlyingResultType || !IsCompatibleWithSign(rr.Type, inst.Sign)) { // Left and right operands are incompatible, so convert them to a common type Sign sign = inst.Sign; if (sign == Sign.None) { // If the sign doesn't matter, try to use the same sign as expected by the context sign = context.TypeHint.GetSign(); if (sign == Sign.None) { sign = op.IsBitwise() ? Sign.Unsigned : Sign.Signed; } } IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); left = left.ConvertTo(NullableType.IsNullable(left.Type) ? NullableType.Create(compilation, targetType) : targetType, this); right = right.ConvertTo(NullableType.IsNullable(right.Type) ? NullableType.Create(compilation, targetType) : targetType, this); rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); } if (op.IsBitwise()) { if (left.ResolveResult.ConstantValue != null) { long value = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, left.ResolveResult.ConstantValue, checkForOverflow: false); left = ConvertConstantValue( left.ResolveResult, allowImplicitConversion: false, ShouldDisplayAsHex(value, left.Type) ).WithILInstruction(left.ILInstructions); } if (right.ResolveResult.ConstantValue != null) { long value = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, right.ResolveResult.ConstantValue, checkForOverflow: false); right = ConvertConstantValue( right.ResolveResult, allowImplicitConversion: false, ShouldDisplayAsHex(value, right.Type) ).WithILInstruction(right.ILInstructions); } } var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); if (BinaryOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); } return resultExpr; } /// /// Gets a type matching the stack type and sign. /// IType FindType(StackType stackType, Sign sign) { if (stackType == StackType.I && settings.NativeIntegers) { return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; } else { return compilation.FindType(stackType, sign); } } /// /// Gets a type used for performing arithmetic with the stack type and sign. /// /// This may result in a larger type than requested when the selected C# version /// doesn't support native integers. /// Should only be used after a call to PrepareArithmeticArgument() /// to ensure that we're not preserving extra bits from an oversized TranslatedExpression. /// IType FindArithmeticType(StackType stackType, Sign sign) { if (stackType == StackType.I) { if (settings.NativeIntegers) { return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; } else { // If native integers are not available, use 64-bit arithmetic instead stackType = StackType.I8; } } return compilation.FindType(stackType, sign); } /// /// Handle oversized arguments needing truncation; and avoid IntPtr/pointers in arguments. /// TranslatedExpression PrepareArithmeticArgument(TranslatedExpression arg, StackType argStackType, Sign sign, bool isLifted) { if (isLifted && !NullableType.IsNullable(arg.Type)) { isLifted = false; // don't cast to nullable if this input wasn't already nullable } IType argUType = isLifted ? NullableType.GetUnderlyingType(arg.Type) : arg.Type; if (argStackType.IsIntegerType() && argStackType.GetSize() < argUType.GetSize()) { // If the argument is oversized (needs truncation to match stack size of its ILInstruction), // perform the truncation now. IType targetType = FindType(argStackType, sign); argUType = targetType; if (isLifted) targetType = NullableType.Create(compilation, targetType); arg = arg.ConvertTo(targetType, this); } if (argUType.IsKnownType(KnownTypeCode.IntPtr) || argUType.IsKnownType(KnownTypeCode.UIntPtr)) { // None of the operators we might want to apply are supported by IntPtr/UIntPtr. // Also, pointer arithmetic has different semantics (works in number of elements, not bytes). // So any inputs of size StackType.I must be converted to long/ulong. IType targetType = FindArithmeticType(StackType.I, sign); if (isLifted) targetType = NullableType.Create(compilation, targetType); arg = arg.ConvertTo(targetType, this); } return arg; } /// /// Gets whether has the specified . /// If is None, always returns true. /// static bool IsCompatibleWithSign(IType type, Sign sign) { return sign == Sign.None || NullableType.GetUnderlyingType(type).GetSign() == sign; } static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op) { switch (op) { case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: case BinaryOperatorType.UnsignedShiftRight: return false; default: return true; } } TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context) { var left = Translate(inst.Left); var right = Translate(inst.Right); left = PrepareArithmeticArgument(left, inst.LeftInputType, inst.Sign, inst.IsLifted); Sign sign = inst.Sign; var leftUType = NullableType.GetUnderlyingType(left.Type); bool couldUseUnsignedRightShift = ( sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift && (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType()) // If we need to cast to unsigned anyway, don't use >>> operator. && context.TypeHint.GetSign() != Sign.Unsigned ); if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 && (sign != Sign.Unsigned || couldUseUnsignedRightShift)) { // With small integer types, C# will promote to int and perform signed shifts. // We thus don't need any casts in this case. // The >>> operator also promotes to signed int, but then performs an unsigned shift. if (sign == Sign.Unsigned) { op = BinaryOperatorType.UnsignedShiftRight; } } else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize() && leftUType.GetSign() == Sign.Signed) { // Use C# 11 unsigned right shift operator. We don't need any casts in this case. op = BinaryOperatorType.UnsignedShiftRight; } else { // Insert cast to target type. if (sign == Sign.None) { // if we don't need a specific sign, prefer keeping that of the input: sign = leftUType.GetSign(); } IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); if (NullableType.IsNullable(left.Type)) { targetType = NullableType.Create(compilation, targetType); } left = left.ConvertTo(targetType, this); } // Shift operators in C# always expect type 'int' on the right-hand-side if (NullableType.IsNullable(right.Type)) { right = right.ConvertTo(NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Int32)), this); } else { right = right.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); } return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult)); } protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) { IType loadType; bool isSpanBasedStringConcat = CallBuilder.IsSpanBasedStringConcat(inst.Method); if (isSpanBasedStringConcat) { loadType = typeSystem.FindType(KnownTypeCode.String); } else { loadType = inst.Method.Parameters[0].Type; } ExpressionWithResolveResult target; if (inst.TargetKind == CompoundTargetKind.Address) { target = LdObj(inst.Target, loadType); } else { target = Translate(inst.Target, loadType); } var opType = OperatorDeclaration.GetOperatorType(inst.Method.Name); if (opType != null && OperatorDeclaration.IsChecked(opType.Value)) { target.Expression.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); } else if (ReplaceMethodCallsWithOperators.HasCheckedEquivalent(inst.Method)) { target.Expression.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); } if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) { Debug.Assert(inst.Method.Parameters.Count == 2); Expression valueExpr; ResolveResult valueResolveResult; if (isSpanBasedStringConcat && inst.Value is NewObj { Arguments: [AddressOf addressOf] }) { IType charType = typeSystem.FindType(KnownTypeCode.Char); var value = Translate(addressOf.Value, charType).ConvertTo(charType, this); valueExpr = value.Expression; valueResolveResult = value.ResolveResult; } else { var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach(); valueResolveResult = value.ResolveResult; } return new AssignmentExpression(target, AssignmentOperatorType.Add, valueExpr) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(inst.Method.ReturnType, ExpressionType.AddAssign, inst.Method, inst.IsLifted, new[] { target.ResolveResult, valueResolveResult })); } else if (inst.Method.Parameters.Count == 2) { var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this); AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name, settings); Debug.Assert(op != null); return new AssignmentExpression(target, op.Value, value) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); } else { UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue); Debug.Assert(op != null); return new UnaryOperatorExpression(op.Value, target) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(inst.Method.ReturnType, UnaryOperatorExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult })); } } internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { case "op_Addition": return AssignmentOperatorType.Add; case "op_Subtraction": return AssignmentOperatorType.Subtract; case "op_Multiply": return AssignmentOperatorType.Multiply; case "op_Division": return AssignmentOperatorType.Divide; case "op_Modulus": return AssignmentOperatorType.Modulus; case "op_BitwiseAnd": return AssignmentOperatorType.BitwiseAnd; case "op_BitwiseOr": return AssignmentOperatorType.BitwiseOr; case "op_ExclusiveOr": return AssignmentOperatorType.ExclusiveOr; case "op_LeftShift": return AssignmentOperatorType.ShiftLeft; case "op_RightShift": return AssignmentOperatorType.ShiftRight; case "op_UnsignedRightShift" when settings.UnsignedRightShift: return AssignmentOperatorType.UnsignedShiftRight; default: return null; } } internal static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name, bool isPostfix) { switch (name) { case "op_Increment": case "op_CheckedIncrement": return isPostfix ? UnaryOperatorType.PostIncrement : UnaryOperatorType.Increment; case "op_Decrement": case "op_CheckedDecrement": return isPostfix ? UnaryOperatorType.PostDecrement : UnaryOperatorType.Decrement; default: return null; } } protected internal override TranslatedExpression VisitNumericCompoundAssign(NumericCompoundAssign inst, TranslationContext context) { switch (inst.Operator) { case BinaryNumericOperator.Add: return HandleCompoundAssignment(inst, AssignmentOperatorType.Add); case BinaryNumericOperator.Sub: return HandleCompoundAssignment(inst, AssignmentOperatorType.Subtract); case BinaryNumericOperator.Mul: return HandleCompoundAssignment(inst, AssignmentOperatorType.Multiply); case BinaryNumericOperator.Div: return HandleCompoundAssignment(inst, AssignmentOperatorType.Divide); case BinaryNumericOperator.Rem: return HandleCompoundAssignment(inst, AssignmentOperatorType.Modulus); case BinaryNumericOperator.BitAnd: return HandleCompoundAssignment(inst, AssignmentOperatorType.BitwiseAnd); case BinaryNumericOperator.BitOr: return HandleCompoundAssignment(inst, AssignmentOperatorType.BitwiseOr); case BinaryNumericOperator.BitXor: return HandleCompoundAssignment(inst, AssignmentOperatorType.ExclusiveOr); case BinaryNumericOperator.ShiftLeft: return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft); case BinaryNumericOperator.ShiftRight: if (inst.Sign == Sign.Unsigned && inst.Type.GetSign() == Sign.Signed) { Debug.Assert(settings.UnsignedRightShift); return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight); } else if (inst.Sign == Sign.Unsigned && inst.Type.IsCSharpSmallIntegerType() && settings.UnsignedRightShift) { // For small unsigned integer types promoted to signed int, the sign bit will be zero, // so there is no difference between signed and unsigned shift. // However the IL still indicates which C# operator was used, so preserve that if the setting allows us to. return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight); } else { return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); } default: throw new ArgumentOutOfRangeException(); } } TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op) { ExpressionWithResolveResult target; if (inst.TargetKind == CompoundTargetKind.Address) { target = LdObj(inst.Target, inst.Type); } else { target = Translate(inst.Target, inst.Type); } TranslatedExpression resultExpr; if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) { Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract); #if DEBUG if (target.Type is PointerType ptrType) { ILInstruction instValue = PointerArithmeticOffset.Detect(inst.Value, ptrType.ElementType, inst.CheckForOverflow); Debug.Assert(instValue is not null); Debug.Assert(instValue.MatchLdcI(1)); } else Debug.Assert(inst.Value.MatchLdcI(1) || inst.Value.MatchLdcF4(1) || inst.Value.MatchLdcF8(1)); #endif UnaryOperatorType unary; ExpressionType exprType; if (op == AssignmentOperatorType.Add) { unary = UnaryOperatorType.PostIncrement; exprType = ExpressionType.PostIncrementAssign; } else { unary = UnaryOperatorType.PostDecrement; exprType = ExpressionType.PostDecrementAssign; } resultExpr = new UnaryOperatorExpression(unary, target) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(target.Type, exprType, target.ResolveResult)); } else { var value = Translate(inst.Value); value = PrepareArithmeticArgument(value, inst.RightInputType, inst.Sign, inst.IsLifted); switch (op) { case AssignmentOperatorType.Add: case AssignmentOperatorType.Subtract: if (target.Type.Kind == TypeKind.Pointer) { var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow); if (pao != null) { value = pao.Value; } else { value.Expression.AddChild(new Comment("ILSpy Error: GetPointerArithmeticOffset() failed", CommentType.MultiLine), Roles.Comment); } } else { IType targetType = NullableType.GetUnderlyingType(target.Type).GetEnumUnderlyingType(); value = ConvertValue(value, targetType); } break; case AssignmentOperatorType.Multiply: case AssignmentOperatorType.Divide: case AssignmentOperatorType.Modulus: case AssignmentOperatorType.BitwiseAnd: case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.ExclusiveOr: { IType targetType = NullableType.GetUnderlyingType(target.Type); value = ConvertValue(value, targetType); break; } } resultExpr = new AssignmentExpression(target.Expression, op, value.Expression) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(target.Type, AssignmentExpression.GetLinqNodeType(op, inst.CheckForOverflow), target.ResolveResult, value.ResolveResult)); } if (AssignmentOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); } return resultExpr; TranslatedExpression ConvertValue(TranslatedExpression value, IType targetType) { bool allowImplicitConversion = true; if (targetType.GetStackType() == StackType.I) { // Force explicit cast for (U)IntPtr, keep allowing implicit conversion only for n(u)int allowImplicitConversion = targetType.IsCSharpNativeIntegerType(); targetType = targetType.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; } if (NullableType.IsNullable(value.Type)) { targetType = NullableType.Create(compilation, targetType); } return value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion); } } TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op) { Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue); ExpressionWithResolveResult target; if (inst.TargetKind == CompoundTargetKind.Address) { target = LdObj(inst.Target, inst.Type); } else { target = Translate(inst.Target, inst.Type); } var value = Translate(inst.Value); // Shift operators in C# always expect type 'int' on the right-hand-side if (NullableType.IsNullable(value.Type)) { value = value.ConvertTo(NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Int32)), this); } else { value = value.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); } return new AssignmentExpression(target.Expression, op, value.Expression) .WithILInstruction(inst) .WithRR(resolver.ResolveAssignment(op, target.ResolveResult, value.ResolveResult)); } static bool AssignmentOperatorMightCheckForOverflow(AssignmentOperatorType op) { switch (op) { case AssignmentOperatorType.BitwiseAnd: case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.ExclusiveOr: case AssignmentOperatorType.ShiftLeft: case AssignmentOperatorType.ShiftRight: return false; default: return true; } } protected internal override TranslatedExpression VisitConv(Conv inst, TranslationContext context) { Sign hintSign = inst.InputSign; if (hintSign == Sign.None) { hintSign = context.TypeHint.GetSign(); } var arg = Translate(inst.Argument, typeHint: FindArithmeticType(inst.InputType, hintSign)); IType inputType = NullableType.GetUnderlyingType(arg.Type); StackType inputStackType = inst.InputType; // Note: we're dealing with two conversions here: // a) the implicit conversion from `inputType` to `inputStackType` // (due to the ExpressionBuilder post-condition being flexible with regards to the integer type width) // If this is a widening conversion, I'm calling the argument C# type "oversized". // If this is a narrowing conversion, I'm calling the argument C# type "undersized". // b) the actual conversion instruction from `inputStackType` to `inst.TargetType` // Also, we need to be very careful with regards to the conversions we emit: // In C#, zero vs. sign-extension depends on the input type, // but in the ILAst conv instruction it depends on the output type. // However, in the conv.ovf instructions, the .NET runtime behavior seems to depend on the input type, // in violation of the ECMA-335 spec! IType GetType(KnownTypeCode typeCode) { IType type = compilation.FindType(typeCode); // Prefer n(u)int over (U)IntPtr if (typeCode == KnownTypeCode.IntPtr && settings.NativeIntegers && !type.Equals(context.TypeHint)) { type = SpecialType.NInt; } else if (typeCode == KnownTypeCode.UIntPtr && settings.NativeIntegers && !type.Equals(context.TypeHint)) { type = SpecialType.NUInt; } if (inst.IsLifted) { type = NullableType.Create(compilation, type); } return type; } if (inst.CheckForOverflow || inst.Kind == ConversionKind.IntToFloat) { // We need to first convert the argument to the expected sign. // We also need to perform any input narrowing conversion so that it doesn't get mixed up with the overflow check. Debug.Assert(inst.InputSign != Sign.None); if (inputType.GetSize() > inputStackType.GetSize() || inputType.GetSign() != inst.InputSign) { arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(inst.InputSign)), this); } // Because casts with overflow check match C# semantics (zero/sign-extension depends on source type), // we can just directly cast to the target type. return arg.ConvertTo(GetType(inst.TargetType.ToKnownTypeCode()), this, inst.CheckForOverflow) .WithILInstruction(inst); } switch (inst.Kind) { case ConversionKind.StartGCTracking: // A "start gc tracking" conversion is inserted in the ILAst whenever // some instruction expects a managed pointer, but we pass an unmanaged pointer. // We'll leave the C#-level conversion (from T* to ref T) to the consumer that expects the managed pointer. return arg; case ConversionKind.StopGCTracking: if (inputType.Kind == TypeKind.ByReference) { if (PointerArithmeticOffset.IsFixedVariable(inst.Argument)) { // cast to corresponding pointer type: var pointerType = new PointerType(((ByReferenceType)inputType).ElementType); return arg.ConvertTo(pointerType, this).WithILInstruction(inst); } else { // emit Unsafe.AsPointer() intrinsic: return CallUnsafeIntrinsic("AsPointer", arguments: new Expression[] { arg }, returnType: new PointerType(compilation.FindType(KnownTypeCode.Void)), inst: inst); } } else if (arg.Type.GetStackType().IsIntegerType()) { // ConversionKind.StopGCTracking should only be used with managed references, // but it's possible that we're supposed to stop tracking something we just started to track. return arg; } else { goto default; } case ConversionKind.SignExtend: // We just need to ensure the input type before the conversion is signed. // Also, if the argument was translated into an oversized C# type, // we need to perform the truncatation to the input stack type. if (inputType.GetSign() != Sign.Signed || ValueMightBeOversized(arg.ResolveResult, inputStackType)) { // Note that an undersized C# type is handled just fine: // If it is unsigned we'll zero-extend it to the width of the inputStackType here, // and it is signed we just combine the two sign-extensions into a single sign-extending conversion. arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(Sign.Signed)), this); } // Then, we can just return the argument as-is: the ExpressionBuilder post-condition allows us // to force our parent instruction to handle the actual sign-extension conversion. // (our caller may have more information to pick a better fitting target type) return arg.WithILInstruction(inst); case ConversionKind.ZeroExtend: // If overflow check cannot fail, handle this just like sign extension (except for swapped signs) if (inputType.GetSign() != Sign.Unsigned || inputType.GetSize() > inputStackType.GetSize()) { arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this); } return arg.WithILInstruction(inst); case ConversionKind.Nop: // no need to generate any C# code for a nop conversion return arg.WithILInstruction(inst); case ConversionKind.Truncate: // Note: there are three sizes involved here: // A = inputType.GetSize() // B = inputStackType.GetSize() // C = inst.TargetType.GetSize(). // We know that C < B (otherwise this wouldn't be the truncation case). // 1) If C < B < A, we just combine the two truncations into one. // 2) If C < B = A, there's no input conversion, just the truncation // 3) If C <= A < B, all the extended bits get removed again by the truncation. // 4) If A < C < B, some extended bits remain even after truncation. // In cases 1-3, the overall conversion is a truncation or no-op. // In case 4, the overall conversion is a zero/sign extension, but to a smaller // size than the original conversion. if (inst.TargetType.IsSmallIntegerType()) { // If the target type is a small integer type, IL will implicitly sign- or zero-extend // the result after the truncation back to StackType.I4. // (which means there's actually 3 conversions involved!) // Note that we must handle truncation to small integer types ourselves: // our caller only sees the StackType.I4 and doesn't know to truncate to the small type. if (inputType.GetSize() <= inst.TargetType.GetSize() && inputType.GetSign() == inst.TargetType.GetSign()) { // There's no actual truncation involved, and the result of the Conv instruction is extended // the same way as the original instruction // -> we can return arg directly return arg.WithILInstruction(inst); } else { // We need to actually truncate; *or* we need to change the sign for the remaining extension to I4. goto default; // Emit simple cast to inst.TargetType } } else { Debug.Assert(inst.TargetType.GetSize() == inst.UnderlyingResultType.GetSize()); // For non-small integer types, we can let the whole unchecked truncation // get handled by our caller (using the ExpressionBuilder post-condition). // Case 4 (left-over extension from implicit conversion) can also be handled by our caller. return arg.WithILInstruction(inst); } case ConversionKind.Invalid: if (inst.InputType == StackType.Unknown && inst.TargetType == IL.PrimitiveType.None && arg.Type.Kind == TypeKind.Unknown) { // Unknown -> O conversion. // Our post-condition allows us to also use expressions with unknown type where O is expected, // so avoid introducing an `(object)` cast because we're likely to cast back to the same unknown type, // just in a signature context where we know that it's a class type. return arg.WithILInstruction(inst); } goto default; default: { // We need to convert to inst.TargetType, or to an equivalent type. IType targetType; if (inst.TargetType == NullableType.GetUnderlyingType(context.TypeHint).ToPrimitiveType() && NullableType.IsNullable(context.TypeHint) == inst.IsLifted) { targetType = context.TypeHint; } else if (inst.TargetType == IL.PrimitiveType.Ref) { // converting to unknown ref-type targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); } else if (inst.TargetType == IL.PrimitiveType.None) { // convert to some object type // (e.g. invalid I4->O conversion) targetType = compilation.FindType(KnownTypeCode.Object); } else { targetType = GetType(inst.TargetType.ToKnownTypeCode()); } return arg.ConvertTo(targetType, this, inst.CheckForOverflow) .WithILInstruction(inst); } } } /// /// Gets whether the ResolveResult computes a value that might be oversized for the specified stack type. /// bool ValueMightBeOversized(ResolveResult rr, StackType stackType) { IType inputType = NullableType.GetUnderlyingType(rr.Type); if (inputType.GetSize() <= stackType.GetSize()) { // The input type is smaller or equal to the stack type, // it can't be an oversized value. return false; } if (rr is OperatorResolveResult orr) { if (stackType == StackType.I && orr.OperatorType == ExpressionType.Subtract && orr.Operands.Count == 2 && orr.Operands[0].Type.Kind == TypeKind.Pointer && orr.Operands[1].Type.Kind == TypeKind.Pointer) { // Even though a pointer subtraction produces a value of type long in C#, // the value will always fit in a native int. return false; } } // We don't have any information about the value, so it might be oversized. return true; } protected internal override TranslatedExpression VisitCall(Call inst, TranslationContext context) { return WrapInRef(new CallBuilder(this, typeSystem, settings).Build(inst), inst.Method.ReturnType); } protected internal override TranslatedExpression VisitCallVirt(CallVirt inst, TranslationContext context) { return WrapInRef(new CallBuilder(this, typeSystem, settings).Build(inst), inst.Method.ReturnType); } TranslatedExpression WrapInRef(TranslatedExpression expr, IType type) { if (type.Kind == TypeKind.ByReference) { return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } return expr; } internal bool IsCurrentOrContainingType(ITypeDefinition type) { var currentTypeDefinition = decompilationContext.CurrentTypeDefinition; while (currentTypeDefinition != null) { if (type == currentTypeDefinition) return true; currentTypeDefinition = currentTypeDefinition.DeclaringTypeDefinition; } return false; } internal bool IsBaseTypeOfCurrentType(ITypeDefinition type) { return decompilationContext.CurrentTypeDefinition.GetAllBaseTypeDefinitions().Any(t => t == type); } internal ExpressionWithResolveResult TranslateFunction(IType delegateType, ILFunction function) { var method = function.Method?.MemberDefinition as IMethod; // Create AnonymousMethodExpression and prepare parameters AnonymousMethodExpression ame = new AnonymousMethodExpression(); ame.IsAsync = function.IsAsync; ame.Parameters.AddRange(MakeParameters(function.Parameters, function)); ame.HasParameterList = ame.Parameters.Count > 0; var builder = new StatementBuilder( typeSystem, this.decompilationContext, function, settings, statementBuilder.decompileRun, cancellationToken ); var body = builder.ConvertAsBlock(function.Body); Comment prev = null; foreach (string warning in function.Warnings) { body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); } var attributeSections = new List(); foreach (var attr in method?.GetAttributes() ?? Enumerable.Empty()) { if (attr.AttributeType.IsKnownType(KnownAttribute.CompilerGenerated)) continue; if (function.IsAsync) { if (attr.AttributeType.IsKnownType(KnownAttribute.AsyncStateMachine)) continue; if (attr.AttributeType.IsKnownType(KnownAttribute.DebuggerStepThrough)) continue; } attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr))); } foreach (var attr in method?.GetReturnTypeAttributes() ?? Enumerable.Empty()) { attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr)) { AttributeTarget = "return" }); } bool isLambda = false; if (ame.Parameters.Any(p => p.Type.IsNull)) { // if there is an anonymous type involved, we are forced to use a lambda expression. isLambda = true; } else if (attributeSections.Count > 0 || ame.Parameters.Any(p => p.Attributes.Any())) { // C# 10 lambdas can have attributes, but anonymous methods cannot isLambda = true; } else if (settings.UseLambdaSyntax && ame.Parameters.All(p => p.ParameterModifier == ReferenceKind.None && !p.IsParams)) { // otherwise use lambda only if an expression lambda is possible isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); } // Remove the parameter list from an AnonymousMethodExpression if the parameters are not used in the method body var parameterReferencingIdentifiers = from ident in body.Descendants.OfType() let v = ident.GetILVariable() where v != null && v.Function == function && v.Kind == VariableKind.Parameter select ident; if (!isLambda && !parameterReferencingIdentifiers.Any()) { ame.Parameters.Clear(); ame.HasParameterList = false; } Expression replacement; IType inferredReturnType; if (isLambda) { LambdaExpression lambda = new LambdaExpression(); lambda.Attributes.AddRange(attributeSections); lambda.IsAsync = ame.IsAsync; lambda.CopyAnnotationsFrom(ame); ame.Parameters.MoveTo(lambda.Parameters); if (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement returnStmt) { lambda.Body = returnStmt.Expression.Detach(); inferredReturnType = lambda.Body.GetResolveResult().Type; } else { lambda.Body = body; inferredReturnType = InferReturnType(body); } replacement = lambda; } else { ame.Body = body; inferredReturnType = InferReturnType(body); replacement = ame; } if (ame.IsAsync) { inferredReturnType = GetTaskType(inferredReturnType); } var rr = new DecompiledLambdaResolveResult( function, delegateType, inferredReturnType, hasParameterList: isLambda || ame.HasParameterList, isAnonymousMethod: !isLambda, isImplicitlyTyped: ame.Parameters.Any(p => p.Type.IsNull)); TranslatedExpression translatedLambda = replacement.WithILInstruction(function).WithRR(rr); return new CastExpression(ConvertType(delegateType), translatedLambda) .WithRR(new ConversionResolveResult(delegateType, rr, LambdaConversion.Instance)); } protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context) { return TranslateFunction(function.DelegateType, function) .WithILInstruction(function); } IType InferReturnType(BlockStatement body) { var returnExpressions = new List(); CollectReturnExpressions(body); var ti = new TypeInference(compilation, resolver.conversions); return ti.GetBestCommonType(returnExpressions, out _); // Failure to infer a return type does not make the lambda invalid, // so we can ignore the 'success' value void CollectReturnExpressions(AstNode node) { if (node is ReturnStatement ret) { if (!ret.Expression.IsNull) { returnExpressions.Add(ret.Expression.GetResolveResult()); } } else if (node is LambdaExpression || node is AnonymousMethodExpression) { // do not recurse into nested lambdas return; } foreach (var child in node.Children) { CollectReturnExpressions(child); } } } IType GetTaskType(IType resultType) { if (resultType.Kind == TypeKind.Unknown) return SpecialType.UnknownType; if (resultType.Kind == TypeKind.Void) return compilation.FindType(KnownTypeCode.Task); ITypeDefinition def = compilation.FindType(KnownTypeCode.TaskOfT).GetDefinition(); if (def != null) return new ParameterizedType(def, new[] { resultType }); else return SpecialType.UnknownType; } IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); int i = 0; foreach (var parameter in parameters) { var pd = astBuilder.ConvertParameter(parameter); if (variables.TryGetValue(i, out var v)) { pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type)); pd.Name = v.Name; } if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { // needs to be consistent with logic in ILReader.CreateILVariable pd.Name = "P_" + i; } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) pd.Type = null; yield return pd; i++; } } protected internal override TranslatedExpression VisitBlockContainer(BlockContainer container, TranslationContext context) { var oldReturnContainer = statementBuilder.currentReturnContainer; var oldResultType = statementBuilder.currentResultType; var oldIsIterator = statementBuilder.currentIsIterator; statementBuilder.currentReturnContainer = container; statementBuilder.currentResultType = context.TypeHint; statementBuilder.currentIsIterator = false; try { var body = statementBuilder.ConvertAsBlock(container); var comment = new Comment(" Could not convert BlockContainer to single expression"); body.InsertChildAfter(null, comment, Roles.Comment); // set ILVariable.UsesInitialValue for any variables being used inside the container foreach (var stloc in container.Descendants.OfType()) stloc.Variable.UsesInitialValue = true; var ame = new AnonymousMethodExpression { Body = body }; var systemFuncType = compilation.FindType(typeof(Func<>)); var blockReturnType = InferReturnType(body); var delegateType = new ParameterizedType(systemFuncType, blockReturnType); var invocationTarget = new CastExpression(ConvertType(delegateType), ame); ResolveResult rr; // This might happen when trying to decompile an assembly built for a target framework // where System.Func does not exist yet. if (systemFuncType.Kind == TypeKind.Unknown) { rr = new ResolveResult(blockReturnType); } else { var invokeMethod = delegateType.GetDelegateInvokeMethod(); rr = new CSharpInvocationResolveResult( new ResolveResult(delegateType), invokeMethod, EmptyList.Instance); } return new InvocationExpression(new MemberReferenceExpression(invocationTarget, "Invoke")) .WithILInstruction(container) .WithRR(rr); } finally { statementBuilder.currentReturnContainer = oldReturnContainer; statementBuilder.currentResultType = oldResultType; statementBuilder.currentIsIterator = oldIsIterator; } } internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, bool memberStatic, IType memberDeclaringType, IType constrainedTo = null) { // If references are missing member.IsStatic might not be set correctly. // Additionally check target for null, in order to avoid a crash. if (!memberStatic && target != null) { if (ShouldUseBaseReference()) { var baseReferenceType = resolver.CurrentTypeDefinition.DirectBaseTypes .FirstOrDefault(t => t.Kind != TypeKind.Interface); return new BaseReferenceExpression() .WithILInstruction(target) .WithRR(new ThisResolveResult(baseReferenceType ?? memberDeclaringType, nonVirtualInvocation)); } else { IType targetTypeHint = constrainedTo ?? memberDeclaringType; if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType, constrainedTo) == StackType.Ref) { if (target.ResultType == StackType.Ref) { targetTypeHint = new ByReferenceType(targetTypeHint); } else { targetTypeHint = new PointerType(targetTypeHint); } } var translatedTarget = Translate(target, targetTypeHint); if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType, constrainedTo) == StackType.Ref) { // When accessing members on value types, ensure we use a reference of the correct type, // and not a pointer or a reference to a different type (issue #1333) if (!(translatedTarget.Type is ByReferenceType brt && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(brt.ElementType, constrainedTo ?? memberDeclaringType))) { translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(constrainedTo ?? memberDeclaringType), this); } } if (translatedTarget.Expression is DirectionExpression) { // (ref x).member => x.member translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); } else if (translatedTarget.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && uoe.Expression is DirectionExpression) { // (ref x)?.member => x?.member translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)uoe.Expression).Expression); // note: we need to create a new ResolveResult for the null-conditional operator, // using the underlying type of the input expression without the DirectionExpression translatedTarget = new UnaryOperatorExpression(UnaryOperatorType.NullConditional, translatedTarget) .WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type))) .WithoutILInstruction(); } translatedTarget = EnsureTargetNotNullable(translatedTarget, target); return translatedTarget; } } else { return new TypeReferenceExpression(ConvertType(constrainedTo ?? memberDeclaringType)) .WithoutILInstruction() .WithRR(new TypeResolveResult(constrainedTo ?? memberDeclaringType)); } bool ShouldUseBaseReference() { if (!nonVirtualInvocation) return false; if (!MatchLdThis(target)) return false; if ((constrainedTo ?? memberDeclaringType).GetDefinition() == resolver.CurrentTypeDefinition) return false; return true; } bool MatchLdThis(ILInstruction inst) { // ldloc this if (inst.MatchLdThis()) return true; if (resolver.CurrentTypeDefinition.Kind == TypeKind.Struct) { // box T(ldobj T(ldloc this)) if (!inst.MatchBox(out var arg, out var type)) return false; if (!arg.MatchLdObj(out var arg2, out var type2)) return false; if (!type.Equals(type2) || !type.Equals(resolver.CurrentTypeDefinition)) return false; return arg2.MatchLdThis(); } return false; } } private TranslatedExpression EnsureTargetNotNullable(TranslatedExpression expr, ILInstruction inst) { /* // TODO Improve nullability support so that we do not sprinkle ! operators everywhere. // inst is the instruction that got translated into expr. if (expr.Type.Nullability == Nullability.Nullable) { if (expr.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional) { return expr; } if (inst.HasFlag(InstructionFlags.MayUnwrapNull)) { // We can't use ! in the chain of operators after a NullConditional, due to // https://github.com/dotnet/roslyn/issues/43659 return expr; } return new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, expr) .WithRR(new ResolveResult(expr.Type.ChangeNullability(Nullability.Oblivious))) .WithoutILInstruction(); } */ return expr; } protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) { IType loadType = inst.Type; bool loadTypeUsedInGeneric = inst.UnalignedPrefix != 0 || inst.Target.ResultType == StackType.Ref; if (context.TypeHint.Kind != TypeKind.Unknown && TypeUtils.IsCompatibleTypeForMemoryAccess(context.TypeHint, loadType) && !(loadTypeUsedInGeneric && context.TypeHint.Kind.IsAnyPointer())) { loadType = context.TypeHint; } if (inst.UnalignedPrefix != 0) { // Use one of: Unsafe.ReadUnaligned(void*) // or: Unsafe.ReadUnaligned(ref byte) var pointer = Translate(inst.Target); if (pointer.Expression is DirectionExpression) { pointer = pointer.ConvertTo(new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)), this); } else { pointer = pointer.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), this, allowImplicitConversion: true); } return CallUnsafeIntrinsic( name: "ReadUnaligned", arguments: new Expression[] { pointer }, returnType: loadType, inst: inst, typeArguments: new IType[] { loadType } ); } var result = LdObj(inst.Target, loadType); //if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign()) // return result.ConvertTo(loadType, this); return result.WithILInstruction(inst); } ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType) { IType addressTypeHint = address.ResultType == StackType.Ref ? new ByReferenceType(loadType) : (IType)new PointerType(loadType); var target = Translate(address, typeHint: addressTypeHint); if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) { ExpressionWithResolveResult result; if (target.Expression is DirectionExpression dirExpr) { // we can dereference the managed reference by stripping away the 'ref' result = target.UnwrapChild(dirExpr.Expression); } else if (target.Type is PointerType pointerType) { if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // We can dereference the pointer by stripping away the '&' result = target.UnwrapChild(uoe.Expression); } else { // Dereference the existing pointer result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithRR(new ResolveResult(pointerType.ElementType)); } } else { // reference type behind non-DirectionExpression? // this case should be impossible, but we can use a pointer cast // just to make sure target = target.ConvertTo(new PointerType(loadType), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithRR(new ResolveResult(loadType)); } // we don't convert result to inst.Type, because the LdObj type // might be inaccurate (it's often System.Object for all reference types), // and our parent node should already insert casts where necessary return result; } else { // We need to cast the pointer type: if (target.Expression is DirectionExpression) { target = target.ConvertTo(new ByReferenceType(loadType), this); } else if (!loadType.IsUnmanagedType(settings.IntroduceUnmanagedConstraint)) { // Use: Unsafe.Read(void*) target = target.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), this, allowImplicitConversion: true); return CallUnsafeIntrinsic( name: "Read", arguments: new Expression[] { target }, returnType: loadType, typeArguments: new IType[] { loadType } ); } else { target = target.ConvertTo(new PointerType(loadType), this); } if (target.Expression is DirectionExpression dirExpr) { return target.UnwrapChild(dirExpr.Expression); } else { return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithRR(new ResolveResult(loadType)); } } } protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context) { if (inst.UnalignedPrefix != 0 || (inst.Target.ResultType != StackType.Ref && !inst.Type.IsUnmanagedType(settings.IntroduceUnmanagedConstraint))) { return StObjViaHelperCall(inst); } IType pointerTypeHint = inst.Target.ResultType == StackType.Ref ? new ByReferenceType(inst.Type) : (IType)new PointerType(inst.Type); var pointer = Translate(inst.Target, typeHint: pointerTypeHint); TranslatedExpression target; TranslatedExpression value = default; IType memoryType; // Check if we need to cast to pointer type: if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointer.Type, inst.Type)) { // cast not necessary, we can use the existing type memoryType = ((TypeWithElementType)pointer.Type).ElementType; } else { // We need to introduce a pointer cast value = Translate(inst.Value, typeHint: inst.Type); if (TypeUtils.IsCompatibleTypeForMemoryAccess(value.Type, inst.Type)) { memoryType = value.Type; } else { memoryType = inst.Type; } if (pointer.Expression is DirectionExpression) { pointer = pointer.ConvertTo(new ByReferenceType(memoryType), this); } else { pointer = pointer.ConvertTo(new PointerType(memoryType), this); } } if (pointer.Expression is DirectionExpression) { // we can deference the managed reference by stripping away the 'ref' target = pointer.UnwrapChild(((DirectionExpression)pointer.Expression).Expression); } else { if (pointer.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // *&ptr -> ptr target = pointer.UnwrapChild(uoe.Expression); } else { target = new UnaryOperatorExpression(UnaryOperatorType.Dereference, pointer.Expression) .WithoutILInstruction() .WithRR(new ResolveResult(memoryType)); } } if (value.Expression == null) { value = Translate(inst.Value, typeHint: target.Type); } if (target.Expression is DirectionExpression dirExpr && target.ResolveResult is ByReferenceResolveResult lhsRefRR) { // ref (re-)assignment, emit "ref (a = ref b)". target = target.UnwrapChild(dirExpr.Expression); value = value.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true); var assign = new AssignmentExpression(target.Expression, value.Expression) .WithRR(new OperatorResolveResult(target.Type, ExpressionType.Assign, lhsRefRR, value.ResolveResult)); return new DirectionExpression(FieldDirection.Ref, assign) .WithoutILInstruction().WithRR(lhsRefRR); } else { return Assignment(target, value).WithILInstruction(inst); } } private TranslatedExpression StObjViaHelperCall(StObj inst) { // "unaligned.1; stobj" -> decompile to a call of // Unsafe.WriteUnaligned(void*, T) // or Unsafe.WriteUnaligned(ref byte, T) // "stobj ManagedType" -> decompile to a call of // Unsafe.Write(void*, T) var pointer = Translate(inst.Target); var value = Translate(inst.Value, typeHint: inst.Type); if (pointer.Expression is DirectionExpression && inst.UnalignedPrefix != 0) { pointer = pointer.ConvertTo(new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)), this); } else { pointer = pointer.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), this, allowImplicitConversion: true); } if (!TypeUtils.IsCompatibleTypeForMemoryAccess(value.Type, inst.Type)) { value = value.ConvertTo(inst.Type, this); } if (inst.UnalignedPrefix != 0) { return CallUnsafeIntrinsic( name: "WriteUnaligned", arguments: new Expression[] { pointer, value }, returnType: compilation.FindType(KnownTypeCode.Void), inst: inst ); } else { return CallUnsafeIntrinsic( name: "Write", arguments: new Expression[] { pointer, value }, returnType: inst.Type, inst: inst ); } } protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context) { IType arrayType = compilation.FindType(KnownTypeCode.Array); TranslatedExpression arrayExpr = Translate(inst.Array, typeHint: arrayType); if (arrayExpr.Type.Kind != TypeKind.Array) { arrayExpr = arrayExpr.ConvertTo(arrayType, this); } arrayExpr = EnsureTargetNotNullable(arrayExpr, inst.Array); string memberName; KnownTypeCode code; if (inst.ResultType == StackType.I4) { memberName = "Length"; code = KnownTypeCode.Int32; } else { memberName = "LongLength"; code = KnownTypeCode.Int64; } IProperty member = arrayType.GetProperties(p => p.Name == memberName).FirstOrDefault(); ResolveResult rr = member == null ? new ResolveResult(compilation.FindType(code)) : new MemberResolveResult(arrayExpr.ResolveResult, member); return new MemberReferenceExpression(arrayExpr.Expression, memberName) .WithILInstruction(inst) .WithRR(rr); } protected internal override TranslatedExpression VisitLdFlda(LdFlda inst, TranslationContext context) { if (settings.FixedBuffers && inst.Field.Name == "FixedElementField" && inst.Target is LdFlda nestedLdFlda && CSharpDecompiler.IsFixedField(nestedLdFlda.Field, out var elementType, out _)) { Expression fieldAccess = ConvertField(nestedLdFlda.Field, nestedLdFlda.Target); var mrr = (MemberResolveResult)fieldAccess.GetResolveResult(); fieldAccess.RemoveAnnotations(); var result = fieldAccess.WithRR(new MemberResolveResult(mrr.TargetResult, mrr.Member, new PointerType(elementType))) .WithILInstruction(inst); if (inst.ResultType == StackType.Ref) { // `target.field` has pointer-type. if (inst.SlotInfo == PinnedRegion.InitSlot || inst.Parent is Conv { TargetType: IL.PrimitiveType.U }) { // Convert pointer to ref if we're in a context where we're going to convert // the ref back to a pointer. return result.ConvertTo(new ByReferenceType(elementType), this); } else { // We can't use `ref *target.field` unless `target` is non-movable, // but we can use `ref target.field[0]`. var arrayAccess = new IndexerExpression(result, new PrimitiveExpression(0)) .WithRR(new ResolveResult(elementType)); return new DirectionExpression(FieldDirection.Ref, arrayAccess) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(arrayAccess.ResolveResult, ReferenceKind.Ref)); } } else { return result; } } TranslatedExpression expr; if (TupleTransform.MatchTupleFieldAccess(inst, out IType underlyingTupleType, out var target, out int position)) { var translatedTarget = TranslateTarget(target, nonVirtualInvocation: true, memberStatic: false, memberDeclaringType: underlyingTupleType); if (translatedTarget.Type is TupleType tupleType && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(tupleType, underlyingTupleType) && position <= tupleType.ElementNames.Length) { string elementName = tupleType.ElementNames[position - 1]; if (elementName == null) { elementName = "Item" + position; } // tupleType.ElementTypes are more accurate w.r.t. nullability/dynamic than inst.Field.Type var rr = new MemberResolveResult(translatedTarget.ResolveResult, inst.Field, returnTypeOverride: tupleType.ElementTypes[position - 1]); expr = new MemberReferenceExpression(translatedTarget, elementName) .WithRR(rr).WithILInstruction(inst); } else { expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); } } else { expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); } if (inst.ResultType == StackType.I) { // ldflda producing native pointer return new UnaryOperatorExpression(UnaryOperatorType.AddressOf, expr) .WithoutILInstruction().WithRR(new ResolveResult(new PointerType(expr.Type))); } else { // ldflda producing managed pointer return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } } protected internal override TranslatedExpression VisitLdsFlda(LdsFlda inst, TranslationContext context) { var expr = ConvertField(inst.Field).WithILInstruction(inst); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitLdElema(LdElema inst, TranslationContext context) { TranslatedExpression arrayExpr = Translate(inst.Array); var arrayType = arrayExpr.Type as ArrayType; if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) { arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); arrayExpr = arrayExpr.ConvertTo(arrayType, this); } IndexerExpression indexerExpr; if (inst.WithSystemIndex) { var systemIndex = compilation.FindType(KnownTypeCode.Index); indexerExpr = new IndexerExpression( arrayExpr, inst.Indices.Select(i => Translate(i, typeHint: systemIndex).ConvertTo(systemIndex, this).Expression) ); } else { indexerExpr = new IndexerExpression( arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) ); } TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(arrayType.ElementType)); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitLdElemaInlineArray(LdElemaInlineArray inst, TranslationContext context) { TranslatedExpression arrayExpr = TranslateTarget( inst.Array, nonVirtualInvocation: true, memberStatic: false, memberDeclaringType: inst.Type ); var inlineArrayElementType = inst.Type.GetInlineArrayElementType(); IndexerExpression indexerExpr = new IndexerExpression( arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) ); TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(inlineArrayElementType)); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } TranslatedExpression TranslateArrayIndex(ILInstruction i) { var input = Translate(i); return ConvertArrayIndex(input, i.ResultType, allowIntPtr: false); } TranslatedExpression ConvertArrayIndex(TranslatedExpression input, StackType stackType, bool allowIntPtr) { if (input.Type.GetSize() > stackType.GetSize()) { // truncate oversized result return input.ConvertTo(FindType(stackType, input.Type.GetSign()), this); } if (input.Type.IsCSharpPrimitiveIntegerType() || input.Type.IsCSharpNativeIntegerType()) { // can be used as array index as-is return input; } if (allowIntPtr && (input.Type.IsKnownType(KnownTypeCode.IntPtr) || input.Type.IsKnownType(KnownTypeCode.UIntPtr))) { return input; } if (stackType != StackType.I4 && input.Type.GetStackType() == StackType.I4) { // prefer casting to int if that's big enough stackType = StackType.I4; } IType targetType = FindArithmeticType(stackType, input.Type.GetSign()); return input.ConvertTo(targetType, this); } internal static bool IsUnboxAnyWithIsInst(UnboxAny unboxAny, IType isInstType) { return unboxAny.Type.Equals(isInstType) && (unboxAny.Type.IsKnownType(KnownTypeCode.NullableOfT) || isInstType.IsReferenceType == true); } protected internal override TranslatedExpression VisitUnboxAny(UnboxAny inst, TranslationContext context) { TranslatedExpression arg; if (inst.Argument is IsInst isInst && IsUnboxAnyWithIsInst(inst, isInst.Type)) { // unbox.any T(isinst T(expr)) ==> expr as T // This is used for generic types and nullable value types arg = UnwrapBoxingConversion(Translate(isInst.Argument)); return new AsExpression(arg, ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); } arg = Translate(inst.Argument); IType targetType = inst.Type; if (targetType.Kind == TypeKind.TypeParameter) { var rr = resolver.ResolveCast(targetType, arg.ResolveResult); if (rr.IsError) { // C# 6.2.7 Explicit conversions involving type parameters: // if we can't directly convert to a type parameter, // try via its effective base class. arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this); } } else { // Before unboxing arg must be a object arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this); } return new CastExpression(ConvertType(targetType), arg.Expression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(targetType, arg.ResolveResult, Conversion.UnboxingConversion)); } protected internal override TranslatedExpression VisitUnbox(Unbox inst, TranslationContext context) { var arg = Translate(inst.Argument); var castExpression = new CastExpression(ConvertType(inst.Type), arg.Expression) .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion)); return new DirectionExpression(FieldDirection.Ref, castExpression) .WithILInstruction(inst) .WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) { IType targetType = inst.Type; var arg = Translate(inst.Argument, typeHint: targetType); if (settings.NativeIntegers && !arg.Type.Equals(targetType)) { if (targetType.IsKnownType(KnownTypeCode.IntPtr)) { targetType = SpecialType.NInt; } else if (targetType.IsKnownType(KnownTypeCode.UIntPtr)) { targetType = SpecialType.NUInt; } } arg = arg.ConvertTo(targetType, this); var obj = compilation.FindType(KnownTypeCode.Object); return new CastExpression(ConvertType(obj), arg.Expression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion)); } protected internal override TranslatedExpression VisitCastClass(CastClass inst, TranslationContext context) { return Translate(inst.Argument).ConvertTo(inst.Type, this); } protected internal override TranslatedExpression VisitExpressionTreeCast(ExpressionTreeCast inst, TranslationContext context) { return Translate(inst.Argument).ConvertTo(inst.Type, this, inst.IsChecked); } protected internal override TranslatedExpression VisitArglist(Arglist inst, TranslationContext context) { return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess } .WithILInstruction(inst) .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeArgumentHandle")))); } protected internal override TranslatedExpression VisitMakeRefAny(MakeRefAny inst, TranslationContext context) { var arg = Translate(inst.Argument).Expression; if (arg is DirectionExpression) { arg = ((DirectionExpression)arg).Expression; } return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, Arguments = { arg.Detach() } } .WithILInstruction(inst) .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "TypedReference")))); } protected internal override TranslatedExpression VisitRefAnyType(RefAnyType inst, TranslationContext context) { return new MemberReferenceExpression(new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { Translate(inst.Argument).Expression.Detach() } }, "TypeHandle") .WithILInstruction(inst) .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")))); } protected internal override TranslatedExpression VisitRefAnyValue(RefAnyValue inst, TranslationContext context) { var expr = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefValue, Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) } }.WithRR(new ResolveResult(inst.Type)); return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction() .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBlock(Block block, TranslationContext context) { switch (block.Kind) { case BlockKind.ArrayInitializer: return TranslateArrayInitializer(block); case BlockKind.StackAllocInitializer: return TranslateStackAllocInitializer(block, context.TypeHint); case BlockKind.CollectionInitializer: case BlockKind.ObjectInitializer: return TranslateObjectAndCollectionInitializer(block); case BlockKind.WithInitializer: return TranslateWithInitializer(block); case BlockKind.CallInlineAssign: return TranslateSetterCallAssignment(block); case BlockKind.CallWithNamedArgs: return TranslateCallWithNamedArgs(block); case BlockKind.InterpolatedString: return TranslateInterpolatedString(block); default: return ErrorExpression("Unknown block type: " + block.Kind); } } private TranslatedExpression TranslateInterpolatedString(Block block) { var content = new List(); for (int i = 1; i < block.Instructions.Count; i++) { var call = (Call)block.Instructions[i]; Interpolation BuildInterpolation(int alignment = 0, string suffix = null) { return new Interpolation(Translate(call.Arguments[1]).ConvertTo(call.GetParameter(1).Type, this, allowImplicitConversion: true), alignment, suffix); } switch (call.Method.Name) { case "AppendLiteral": content.Add(new InterpolatedStringText(((LdStr)call.Arguments[1]).Value.Replace("{", "{{").Replace("}", "}}"))); break; case "AppendFormatted" when call.Arguments.Count == 2: content.Add(BuildInterpolation()); break; case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdStr ldstr: content.Add(BuildInterpolation(suffix: ldstr.Value)); break; case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdcI4 ldci4: content.Add(BuildInterpolation(alignment: ldci4.Value)); break; case "AppendFormatted" when call.Arguments.Count == 4 && call.Arguments[2] is LdcI4 ldci4 && call.Arguments[3] is LdStr ldstr: content.Add(BuildInterpolation(ldci4.Value, ldstr.Value)); break; default: throw new NotSupportedException(); } } return new InterpolatedStringExpression(content) .WithILInstruction(block) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.String))); } private TranslatedExpression TranslateCallWithNamedArgs(Block block) { return WrapInRef( new CallBuilder(this, typeSystem, settings).CallWithNamedArgs(block), ((CallInstruction)block.FinalInstruction).Method.ReturnType); } private TranslatedExpression TranslateSetterCallAssignment(Block block) { if (!block.MatchInlineAssignBlock(out var call, out var value)) { // should never happen unless the ILAst is invalid return ErrorExpression("Error: MatchInlineAssignBlock() returned false"); } var arguments = call.Arguments.ToList(); arguments[arguments.Count - 1] = value; return new CallBuilder(this, typeSystem, settings) .Build(call.OpCode, call.Method, arguments) .WithILInstruction(call); } TranslatedExpression TranslateObjectAndCollectionInitializer(Block block) { var stloc = block.Instructions.FirstOrDefault() as StLoc; var final = block.FinalInstruction as LdLoc; // Check basic structure of block if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) throw new ArgumentException("given Block is invalid!"); InitializedObjectResolveResult initObjRR; TranslatedExpression expr; // Detect type of initializer switch (stloc.Value) { case NewObj newObjInst: initObjRR = new InitializedObjectResolveResult(newObjInst.Method.DeclaringType); expr = new CallBuilder(this, typeSystem, settings).Build(newObjInst); break; case DefaultValue defaultVal: initObjRR = new InitializedObjectResolveResult(defaultVal.Type); expr = new ObjectCreateExpression(ConvertType(defaultVal.Type)) .WithILInstruction(defaultVal) .WithRR(new TypeResolveResult(defaultVal.Type)); break; case Block callWithNamedArgs when callWithNamedArgs.Kind == BlockKind.CallWithNamedArgs: expr = TranslateCallWithNamedArgs(callWithNamedArgs); initObjRR = new InitializedObjectResolveResult(expr.Type); break; case Call c when c.Method.FullNameIs("System.Activator", "CreateInstance") && c.Method.TypeArguments.Count == 1: IType type = c.Method.TypeArguments[0]; initObjRR = new InitializedObjectResolveResult(type); expr = new ObjectCreateExpression(ConvertType(type)) .WithILInstruction(c) .WithRR(new TypeResolveResult(type)); break; default: throw new ArgumentException("given Block is invalid!"); } var oce = (ObjectCreateExpression)expr.Expression; oce.Initializer = BuildArrayInitializerExpression(block, initObjRR); return expr.WithILInstruction(block); } private ArrayInitializerExpression BuildArrayInitializerExpression(Block block, InitializedObjectResolveResult initObjRR) { var elementsStack = new Stack>(); var elements = new List(block.Instructions.Count); elementsStack.Push(elements); List currentPath = null; var indexVariables = new Dictionary(); foreach (var inst in block.Instructions.Skip(1)) { // Collect indexer variables (for C# 6 dictionary initializers) if (inst is StLoc indexStore) { indexVariables.Add(indexStore.Variable, indexStore.Value); continue; } // Get current path var info = IL.Transforms.AccessPathElement.GetAccessPath(inst, initObjRR.Type, settings: settings); // This should not happen, because the IL transform should not create invalid access paths, // but we leave it here as sanity check. if (info.Kind == IL.Transforms.AccessPathKind.Invalid) continue; // Calculate "difference" to previous path if (currentPath == null) { currentPath = info.Path; } else { int minLen = Math.Min(currentPath.Count, info.Path.Count); int firstDifferenceIndex = 0; while (firstDifferenceIndex < minLen && info.Path[firstDifferenceIndex] == currentPath[firstDifferenceIndex]) firstDifferenceIndex++; while (elementsStack.Count - 1 > firstDifferenceIndex) { var methodElement = currentPath[elementsStack.Count - 1]; var pathElement = currentPath[elementsStack.Count - 2]; var values = elementsStack.Pop(); elementsStack.Peek().Add(MakeInitializerAssignment(initObjRR, methodElement, pathElement, values, indexVariables)); } currentPath = info.Path; } // Fill the stack with empty expression lists while (elementsStack.Count < currentPath.Count) elementsStack.Push(new List()); var lastElement = currentPath.Last(); var memberRR = new MemberResolveResult(initObjRR, lastElement.Member); switch (info.Kind) { case IL.Transforms.AccessPathKind.Adder: Debug.Assert(lastElement.Member is IMethod); elementsStack.Peek().Add( new CallBuilder(this, typeSystem, settings) .BuildCollectionInitializerExpression(lastElement.OpCode, (IMethod)lastElement.Member, initObjRR, info.Values) .WithILInstruction(inst) ); break; case IL.Transforms.AccessPathKind.Setter: Debug.Assert(lastElement.Member is IProperty or IField); if (lastElement.Indices?.Length is var indices and > 0) { var property = (IProperty)lastElement.Member; Debug.Assert(property.Parameters.Count == indices); Debug.Assert(property.Setter != null, $"Indexer property {property} has no setter"); elementsStack.Peek().Add( new CallBuilder(this, typeSystem, settings) .BuildDictionaryInitializerExpression(lastElement.OpCode, property.Setter, initObjRR, GetIndices(lastElement.Indices, indexVariables).ToList(), info.Values.Single()) .WithILInstruction(inst) ); } else { var value = Translate(info.Values.Single(), typeHint: memberRR.Type) .ConvertTo(memberRR.Type, this, allowImplicitConversion: true); var assignment = new NamedExpression(lastElement.Member.Name, value) .WithILInstruction(inst).WithRR(memberRR); elementsStack.Peek().Add(assignment); } break; } } while (elementsStack.Count > 1) { var methodElement = currentPath[elementsStack.Count - 1]; var pathElement = currentPath[elementsStack.Count - 2]; var values = elementsStack.Pop(); elementsStack.Peek().Add( MakeInitializerAssignment(initObjRR, methodElement, pathElement, values, indexVariables) ); } return new ArrayInitializerExpression(elements.SelectArray(e => e.Expression)); } IEnumerable GetIndices(IEnumerable indices, Dictionary indexVariables) { foreach (var inst in indices) { if (inst is LdLoc ld && indexVariables.TryGetValue(ld.Variable, out var newInst)) yield return newInst; else yield return inst; } } TranslatedExpression MakeInitializerAssignment(InitializedObjectResolveResult rr, IL.Transforms.AccessPathElement memberPath, IL.Transforms.AccessPathElement valuePath, List values, Dictionary indexVariables) { TranslatedExpression value; if (memberPath.Member is IMethod method && method.Name == "Add") { value = new ArrayInitializerExpression(values.Select(v => v.Expression)) .WithRR(new ResolveResult(SpecialType.UnknownType)) .WithoutILInstruction(); } else if (values.Count == 1 && !(values[0].Expression is AssignmentExpression || values[0].Expression is NamedExpression)) { value = values[0]; } else { value = new ArrayInitializerExpression(values.Select(v => v.Expression)) .WithRR(new ResolveResult(SpecialType.UnknownType)) .WithoutILInstruction(); } if (valuePath.Indices?.Length > 0) { Expression index = new IndexerExpression(null, GetIndices(valuePath.Indices, indexVariables).Select(i => Translate(i).Expression)); return new AssignmentExpression(index, value) .WithRR(new MemberResolveResult(rr, valuePath.Member)) .WithoutILInstruction(); } else { return new NamedExpression(valuePath.Member.Name, value) .WithRR(new MemberResolveResult(rr, valuePath.Member)) .WithoutILInstruction(); } } class ArrayInitializer { public ArrayInitializer(ArrayInitializerExpression expression) { this.Expression = expression; this.CurrentElementCount = 0; } public ArrayInitializerExpression Expression; // HACK: avoid using Expression.Elements.Count: https://github.com/icsharpcode/ILSpy/issues/1202 public int CurrentElementCount; } TranslatedExpression TranslateArrayInitializer(Block block) { var stloc = block.Instructions.FirstOrDefault() as StLoc; var final = block.FinalInstruction as LdLoc; if (stloc == null || final == null || !stloc.Value.MatchNewArr(out IType type)) throw new ArgumentException("given Block is invalid!"); if (stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) throw new ArgumentException("given Block is invalid!"); var newArr = (NewArr)stloc.Value; var translatedDimensions = newArr.Indices.SelectArray(i => Translate(i)); if (!translatedDimensions.All(dim => dim.ResolveResult.IsCompileTimeConstant)) throw new ArgumentException("given Block is invalid!"); int dimensions = newArr.Indices.Count; int[] dimensionSizes = translatedDimensions.SelectArray(dim => (int)dim.ResolveResult.ConstantValue); var container = new Stack(); var root = new ArrayInitializer(new ArrayInitializerExpression()); container.Push(root); var elementResolveResults = new List(); for (int i = 1; i < block.Instructions.Count; i++) { if (!block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType t) || !type.Equals(t)) throw new ArgumentException("given Block is invalid!"); if (!target.MatchLdElema(out t, out ILInstruction array) || !type.Equals(t)) throw new ArgumentException("given Block is invalid!"); if (!array.MatchLdLoc(out ILVariable v) || v != final.Variable) throw new ArgumentException("given Block is invalid!"); while (container.Count < dimensions) { var aie = new ArrayInitializerExpression(); var parentInitializer = container.Peek(); if (parentInitializer.CurrentElementCount > 0) parentInitializer.Expression.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); parentInitializer.Expression.Elements.Add(aie); parentInitializer.CurrentElementCount++; container.Push(new ArrayInitializer(aie)); } TranslatedExpression val; var old = astBuilder.UseSpecialConstants; try { astBuilder.UseSpecialConstants = !type.IsCSharpPrimitiveIntegerType() && !type.IsKnownType(KnownTypeCode.Decimal); val = Translate(value, typeHint: type).ConvertTo(type, this, allowImplicitConversion: true); } finally { astBuilder.UseSpecialConstants = old; } var currentInitializer = container.Peek(); if (currentInitializer.CurrentElementCount > 0) currentInitializer.Expression.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); currentInitializer.Expression.Elements.Add(val); currentInitializer.CurrentElementCount++; elementResolveResults.Add(val.ResolveResult); while (container.Count > 0 && container.Peek().CurrentElementCount == dimensionSizes[container.Count - 1]) { container.Pop(); } } ArraySpecifier[] additionalSpecifiers; AstType typeExpression; if (settings.AnonymousTypes && type.ContainsAnonymousType()) { typeExpression = null; additionalSpecifiers = new[] { new ArraySpecifier() }; } else { typeExpression = ConvertType(type); if (typeExpression is ComposedType compType && compType.ArraySpecifiers.Count > 0) { additionalSpecifiers = compType.ArraySpecifiers.Select(a => (ArraySpecifier)a.Clone()).ToArray(); compType.ArraySpecifiers.Clear(); } else { additionalSpecifiers = Empty.Array; } } var expr = new ArrayCreateExpression { Type = typeExpression, Initializer = root.Expression }; expr.AdditionalArraySpecifiers.AddRange(additionalSpecifiers); if (!type.ContainsAnonymousType()) expr.Arguments.AddRange(newArr.Indices.Select(i => Translate(i).Expression)); return expr.WithILInstruction(block) .WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, type, dimensions), newArr.Indices.Select(i => Translate(i).ResolveResult).ToArray(), elementResolveResults)); } TranslatedExpression TranslateStackAllocInitializer(Block block, IType typeHint) { var stloc = block.Instructions.FirstOrDefault() as StLoc; var final = block.FinalInstruction as LdLoc; if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) throw new ArgumentException("given Block is invalid!"); StackAllocExpression stackAllocExpression; IType elementType; if (block.Instructions.Count < 2 || !block.Instructions[1].MatchStObj(out _, out _, out var t)) throw new ArgumentException("given Block is invalid!"); if (typeHint is PointerType pt && !TypeUtils.IsCompatibleTypeForMemoryAccess(t, pt.ElementType)) { typeHint = new PointerType(t); } switch (stloc.Value) { case LocAlloc locAlloc: stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out elementType); break; case LocAllocSpan locAllocSpan: stackAllocExpression = TranslateLocAllocSpan(locAllocSpan, typeHint, out elementType); break; default: throw new ArgumentException("given Block is invalid!"); } var initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression(); var pointerType = new PointerType(elementType); long expectedOffset = 0; for (int i = 1; i < block.Instructions.Count; i++) { // stobj type(binary.add.i(ldloc I_0, conv i4->i (ldc.i4 offset)), value) if (!block.Instructions[i].MatchStObj(out var target, out var value, out t) || !TypeUtils.IsCompatibleTypeForMemoryAccess(elementType, t)) throw new ArgumentException("given Block is invalid!"); long offset = 0; target = target.UnwrapConv(ConversionKind.StopGCTracking); if (!target.MatchLdLoc(stloc.Variable)) { if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) throw new ArgumentException("given Block is invalid!"); var binary = (BinaryNumericInstruction)target; left = left.UnwrapConv(ConversionKind.StopGCTracking); var offsetInst = PointerArithmeticOffset.Detect(right, pointerType.ElementType, binary.CheckForOverflow); if (!left.MatchLdLoc(final.Variable) || offsetInst == null) throw new ArgumentException("given Block is invalid!"); if (!offsetInst.MatchLdcI(out offset)) throw new ArgumentException("given Block is invalid!"); } while (expectedOffset < offset) { initializer.Elements.Add(Translate(IL.Transforms.TransformArrayInitializers.GetNullExpression(elementType), typeHint: elementType)); expectedOffset++; } var val = Translate(value, typeHint: elementType).ConvertTo(elementType, this, allowImplicitConversion: true); initializer.Elements.Add(val); expectedOffset++; } return stackAllocExpression.WithILInstruction(block) .WithRR(new ResolveResult(stloc.Variable.Type)); } TranslatedExpression TranslateWithInitializer(Block block) { var stloc = block.Instructions.FirstOrDefault() as StLoc; var final = block.FinalInstruction as LdLoc; if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) throw new ArgumentException("given Block is invalid!"); WithInitializerExpression withInitializerExpression = new WithInitializerExpression(); withInitializerExpression.Expression = Translate(stloc.Value, stloc.Variable.Type); withInitializerExpression.Initializer = BuildArrayInitializerExpression(block, new InitializedObjectResolveResult(stloc.Variable.Type)); return withInitializerExpression.WithILInstruction(block) .WithRR(new ResolveResult(stloc.Variable.Type)); } /// /// If expr is a constant integer expression, and its value fits into type, /// convert the expression into the target type. /// Otherwise, returns the expression unmodified. /// TranslatedExpression AdjustConstantExpressionToType(TranslatedExpression expr, IType typeHint) { var newRR = AdjustConstantToType(expr.ResolveResult, typeHint); if (newRR == expr.ResolveResult) { return expr; } else { return ConvertConstantValue(newRR, allowImplicitConversion: true).WithILInstruction(expr.ILInstructions); } } private ResolveResult AdjustConstantToType(ResolveResult rr, IType typeHint) { if (!rr.IsCompileTimeConstant) { return rr; } typeHint = NullableType.GetUnderlyingType(typeHint); if (rr.Type.Equals(typeHint)) { return rr; } // Convert to type hint, if this is possible without loss of accuracy if (typeHint.IsKnownType(KnownTypeCode.Boolean)) { if (object.Equals(rr.ConstantValue, 0) || object.Equals(rr.ConstantValue, 0u)) { rr = new ConstantResolveResult(typeHint, false); } else if (object.Equals(rr.ConstantValue, 1) || object.Equals(rr.ConstantValue, 1u)) { rr = new ConstantResolveResult(typeHint, true); } } else if (typeHint.Kind == TypeKind.Enum || typeHint.IsKnownType(KnownTypeCode.Char) || typeHint.IsCSharpSmallIntegerType()) { var castRR = resolver.WithCheckForOverflow(true).ResolveCast(typeHint, rr); if (castRR.IsCompileTimeConstant && !castRR.IsError) { rr = castRR; } } else if (typeHint.Kind.IsAnyPointer() && (object.Equals(rr.ConstantValue, 0) || object.Equals(rr.ConstantValue, 0u))) { rr = new ConstantResolveResult(typeHint, null); } return rr; } protected internal override TranslatedExpression VisitNullCoalescingInstruction(NullCoalescingInstruction inst, TranslationContext context) { var value = Translate(inst.ValueInst); var fallback = Translate(inst.FallbackInst); fallback = AdjustConstantExpressionToType(fallback, value.Type); var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult); if (rr.IsError) { IType targetType; if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) { targetType = NullableType.GetUnderlyingType(value.Type); } else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { targetType = compilation.FindType(inst.UnderlyingResultType); } else { targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type; } if (inst.Kind != NullCoalescingKind.Ref) { value = value.ConvertTo(NullableType.Create(compilation, targetType), this); } else { value = value.ConvertTo(targetType, this); } if (inst.Kind == NullCoalescingKind.Nullable) { value = value.ConvertTo(NullableType.Create(compilation, targetType), this); } else { fallback = fallback.ConvertTo(targetType, this); } rr = new ResolveResult(targetType); } return new BinaryOperatorExpression(value, BinaryOperatorType.NullCoalescing, fallback) .WithILInstruction(inst) .WithRR(rr); } protected internal override TranslatedExpression VisitIfInstruction(IfInstruction inst, TranslationContext context) { var condition = TranslateCondition(inst.Condition); var trueBranch = Translate(inst.TrueInst, typeHint: context.TypeHint); var falseBranch = Translate(inst.FalseInst, typeHint: context.TypeHint); BinaryOperatorType op = BinaryOperatorType.Any; TranslatedExpression rhs = default(TranslatedExpression); if (inst.MatchLogicAnd(out var lhsInst, out var rhsInst) && !rhsInst.MatchLdcI4(1)) { op = BinaryOperatorType.ConditionalAnd; Debug.Assert(rhsInst == inst.TrueInst); rhs = trueBranch; } else if (inst.MatchLogicOr(out lhsInst, out rhsInst) && !rhsInst.MatchLdcI4(0)) { op = BinaryOperatorType.ConditionalOr; Debug.Assert(rhsInst == inst.FalseInst); rhs = falseBranch; } // ILAst LogicAnd/LogicOr can return a different value than 0 or 1 // if the rhs is evaluated. // We can only correctly translate it to C# if the rhs is of type boolean: if (op != BinaryOperatorType.Any && (rhs.Type.IsKnownType(KnownTypeCode.Boolean) || IfInstruction.IsInConditionSlot(inst))) { if (rhs.Type.GetStackType().GetSize() > 4) { rhs = rhs.ConvertTo(FindType(StackType.I4, rhs.Type.GetSign()), this); } rhs = rhs.ConvertToBoolean(this); return new BinaryOperatorExpression(condition, op, rhs) .WithILInstruction(inst) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); } condition = condition.UnwrapImplicitBoolConversion(); trueBranch = AdjustConstantExpressionToType(trueBranch, falseBranch.Type); falseBranch = AdjustConstantExpressionToType(falseBranch, trueBranch.Type); var rr = resolver.ResolveConditional(condition.ResolveResult, trueBranch.ResolveResult, falseBranch.ResolveResult); if (rr.IsError) { IType targetType; if (!trueBranch.Type.Equals(SpecialType.NullType) && !falseBranch.Type.Equals(SpecialType.NullType) && !trueBranch.Type.Equals(falseBranch.Type)) { targetType = typeInference.GetBestCommonType(new[] { trueBranch.ResolveResult, falseBranch.ResolveResult }, out bool success); if (!success || targetType.GetStackType() != inst.ResultType) { // Figure out the target type based on inst.ResultType. if (context.TypeHint.Kind != TypeKind.Unknown && context.TypeHint.GetStackType() == inst.ResultType) { targetType = context.TypeHint; } else if (inst.ResultType == StackType.Ref) { // targetType should be a ref-type if (trueBranch.Type.Kind == TypeKind.ByReference) { targetType = trueBranch.Type; } else if (falseBranch.Type.Kind == TypeKind.ByReference) { targetType = falseBranch.Type; } else { // fall back to 'ref byte' if we can't determine a referenced type otherwise targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); } } else { targetType = FindType(inst.ResultType, context.TypeHint.GetSign()); } } } else { targetType = trueBranch.Type.Equals(SpecialType.NullType) ? falseBranch.Type : trueBranch.Type; } trueBranch = trueBranch.ConvertTo(targetType, this); falseBranch = falseBranch.ConvertTo(targetType, this); rr = new ResolveResult(targetType); } if (rr.Type.Kind == TypeKind.ByReference) { // C# conditional ref looks like this: // ref (arr != null ? ref trueBranch : ref falseBranch); var conditionalResolveResult = new ResolveResult(((ByReferenceType)rr.Type).ElementType); return new DirectionExpression(FieldDirection.Ref, new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) .WithRR(conditionalResolveResult) ).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, ReferenceKind.Ref)); } else { return new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) .WithRR(rr); } } internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext) { TranslatedExpression value; IType governingType; // prepare expression and expected type // first try to guess a governing type if (inst.Value is StringToInt strToInt) { value = Translate(strToInt.Argument); governingType = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String); } else { strToInt = null; value = Translate(inst.Value); governingType = inst.Type ?? value.Type; // validate the governing type if (inst.Value.ResultType == StackType.I8) { if (governingType.GetStackType() != StackType.I8) governingType = FindType(StackType.I8, governingType.GetSign()); } else if (inst.Value.ResultType == StackType.I4) { if (governingType.GetStackType() != StackType.I4) governingType = FindType(StackType.I4, governingType.GetSign()); if (governingType.IsSmallIntegerType()) { var defaultSection = inst.GetDefaultSection(); int bits = 8 * governingType.GetSize(); int minValue = governingType.GetSign() == Sign.Unsigned ? 0 : -(1 << (bits - 1)); int maxValue = governingType.GetSign() == Sign.Unsigned ? (1 << bits) - 1 : (1 << (bits - 1)) - 1; foreach (var section in inst.Sections) { if (section == defaultSection) continue; LongInterval interval = section.Labels.ContainingInterval(); if (interval.Start < minValue || interval.InclusiveEnd > maxValue) { // governing type is too small to hold all case values governingType = FindType(StackType.I4, Sign.Signed); break; } } } } else { Debug.Assert(inst.Value.ResultType == StackType.O); Debug.Assert(inst.IsLifted); Debug.Assert(inst.Type == governingType); } } if (isExpressionContext) { value = value.ConvertTo(governingType, this, allowImplicitConversion: false); } else { value = value.ConvertTo(governingType, this, allowImplicitConversion: true); var csharpGoverningType = GetCSharpSwitchGoverningType(value.Type); if (!csharpGoverningType.Equals(governingType)) { value = value.ConvertTo(governingType, this, allowImplicitConversion: false); } } var caseType = strToInt != null ? compilation.FindType(KnownTypeCode.String) : governingType; return (value, caseType, strToInt); } static IType GetCSharpSwitchGoverningType(IType type) { if (IsCompatibleWithSwitch(type)) return type; var applicableImplicitConversionOperators = type.GetMethods(IsImplicitConversionOperator) .Where(m => IsCompatibleWithSwitch(m.ReturnType)) .ToArray(); if (applicableImplicitConversionOperators.Length != 1) return type; return applicableImplicitConversionOperators[0].ReturnType; static bool IsImplicitConversionOperator(IMethod operatorMethod) { if (!operatorMethod.IsOperator) return false; if (operatorMethod.Name != "op_Implicit") return false; if (operatorMethod.Parameters.Count != 1) return false; return true; } static bool IsCompatibleWithSwitch(IType type) { type = NullableType.GetUnderlyingType(type); return type.IsKnownType(KnownTypeCode.Boolean) || type.IsKnownType(KnownTypeCode.SByte) || type.IsKnownType(KnownTypeCode.Byte) || type.IsKnownType(KnownTypeCode.Int16) || type.IsKnownType(KnownTypeCode.UInt16) || type.IsKnownType(KnownTypeCode.Int32) || type.IsKnownType(KnownTypeCode.UInt32) || type.IsKnownType(KnownTypeCode.Int64) || type.IsKnownType(KnownTypeCode.UInt64) || type.IsKnownType(KnownTypeCode.Char) || type.IsKnownType(KnownTypeCode.String); } } protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context) { // switch-expression does not support implicit conversions var (value, type, strToInt) = TranslateSwitchValue(inst, true); IL.SwitchSection defaultSection = inst.GetDefaultSection(); SwitchExpression switchExpr = new SwitchExpression(); switchExpr.Expression = value; IType resultType; if (context.TypeHint.Kind != TypeKind.Unknown && context.TypeHint.GetStackType() == inst.ResultType) { resultType = context.TypeHint; } else { resultType = compilation.FindType(inst.ResultType); } foreach (var section in inst.Sections) { if (section == defaultSection) continue; var ses = new SwitchExpressionSection(); if (section.HasNullLabel) { Debug.Assert(section.Labels.IsEmpty); ses.Pattern = new NullReferenceExpression(); } else { long val = section.Labels.Values.Single(); var rr = statementBuilder.CreateTypedCaseLabel(val, type, strToInt?.Map).Single(); ses.Pattern = astBuilder.ConvertConstantValue(rr); } ses.Body = TranslateSectionBody(section); switchExpr.SwitchSections.Add(ses); } var defaultSES = new SwitchExpressionSection(); defaultSES.Pattern = new IdentifierExpression("_"); defaultSES.Body = TranslateSectionBody(defaultSection); switchExpr.SwitchSections.Add(defaultSES); return switchExpr.WithILInstruction(inst).WithRR(new ResolveResult(resultType)); Expression TranslateSectionBody(IL.SwitchSection section) { var body = Translate(section.Body, resultType); return body.ConvertTo(resultType, this, allowImplicitConversion: true); } } protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context) { var classification = ILInlining.ClassifyExpression(inst.Value); var value = Translate(inst.Value, inst.Type); value = value.ConvertTo(inst.Type, this); // ILAst AddressOf copies the value to a temporary, but when invoking a method in C# // on a mutable lvalue, we would end up modifying the original lvalue, not just the copy. // We solve this by introducing a "redundant" cast. Casts are classified as rvalue // and ensure that the C# compiler will also create a copy. if (classification == ExpressionClassification.MutableLValue && !CanIgnoreCopy() && value.Expression is not CastExpression) { value = new CastExpression(ConvertType(inst.Type), value.Expression) .WithoutILInstruction() .WithRR(new ConversionResolveResult(inst.Type, value.ResolveResult, Conversion.IdentityConversion)); } return new DirectionExpression(FieldDirection.Ref, value) .WithILInstruction(inst) .WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); bool CanIgnoreCopy() { ILInstruction loadAddress = inst; while (loadAddress.Parent is LdFlda parent) { loadAddress = parent; } if (loadAddress.Parent is LdObj) { // Ignore copy, never introduce a cast return true; } return false; } } protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context) { IType expectedType = null; if (inst.GetAwaiterMethod != null) { if (inst.GetAwaiterMethod.IsStatic) { expectedType = inst.GetAwaiterMethod.Parameters.FirstOrDefault()?.Type; } else { expectedType = inst.GetAwaiterMethod.DeclaringType; } } var value = Translate(inst.Value, typeHint: expectedType); if (value.Expression is DirectionExpression) { // we can deference the managed reference by stripping away the 'ref' value = value.UnwrapChild(((DirectionExpression)value.Expression).Expression); } if (expectedType != null) { value = value.ConvertTo(expectedType, this, allowImplicitConversion: true); } return new UnaryOperatorExpression(UnaryOperatorType.Await, value.Expression) .WithILInstruction(inst) .WithRR(new ResolveResult(inst.GetResultMethod?.ReturnType ?? SpecialType.UnknownType)); } protected internal override TranslatedExpression VisitNullableRewrap(NullableRewrap inst, TranslationContext context) { var arg = Translate(inst.Argument); IType type = arg.Type; if (NullableType.IsNonNullableValueType(type)) { type = NullableType.Create(compilation, type); } return new UnaryOperatorExpression(UnaryOperatorType.NullConditionalRewrap, arg) .WithILInstruction(inst) .WithRR(new ResolveResult(type)); } protected internal override TranslatedExpression VisitNullableUnwrap(NullableUnwrap inst, TranslationContext context) { var arg = Translate(inst.Argument); if (inst.RefInput && !inst.RefOutput && arg.Expression is DirectionExpression dir) { arg = arg.UnwrapChild(dir.Expression); } return new UnaryOperatorExpression(UnaryOperatorType.NullConditional, arg) .WithILInstruction(inst) .WithRR(new ResolveResult(NullableType.GetUnderlyingType(arg.Type))); } protected internal override TranslatedExpression VisitDynamicConvertInstruction(DynamicConvertInstruction inst, TranslationContext context) { var operand = Translate(inst.Argument).ConvertTo(SpecialType.Dynamic, this); var result = new CastExpression(ConvertType(inst.Type), operand) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( inst.Type, operand.ResolveResult, inst.IsExplicit ? Conversion.ExplicitDynamicConversion : Conversion.ImplicitDynamicConversion )); result.Expression.AddAnnotation(inst.IsChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); return result; } protected internal override TranslatedExpression VisitDynamicGetIndexInstruction(DynamicGetIndexInstruction inst, TranslationContext context) { var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); return new IndexerExpression(target, arguments.Select(a => a.Expression)) .WithILInstruction(inst) .WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Indexing, arguments.Select(a => a.ResolveResult).ToArray())); } protected internal override TranslatedExpression VisitDynamicGetMemberInstruction(DynamicGetMemberInstruction inst, TranslationContext context) { var target = TranslateDynamicTarget(inst.Target, inst.TargetArgumentInfo); return new MemberReferenceExpression(target, inst.Name) .WithILInstruction(inst) .WithRR(new DynamicMemberResolveResult(target.ResolveResult, inst.Name)); } protected internal override TranslatedExpression VisitDynamicInvokeConstructorInstruction(DynamicInvokeConstructorInstruction inst, TranslationContext context) { if (!(inst.ArgumentInfo[0].HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst.Arguments[0], out var constructorType))) return ErrorExpression("Could not detect static type for DynamicInvokeConstructorInstruction"); var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); //var names = inst.ArgumentInfo.Skip(1).Select(a => a.Name).ToArray(); return new ObjectCreateExpression(ConvertType(constructorType), arguments.Select(a => a.Expression)) .WithILInstruction(inst).WithRR(new ResolveResult(constructorType)); } protected internal override TranslatedExpression VisitDynamicInvokeMemberInstruction(DynamicInvokeMemberInstruction inst, TranslationContext context) { Expression targetExpr; var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); if (inst.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSimpleName) && target.Expression is ThisReferenceExpression) { targetExpr = new IdentifierExpression(inst.Name); ((IdentifierExpression)targetExpr).TypeArguments.AddRange(inst.TypeArguments.Select(ConvertType)); } else { targetExpr = new MemberReferenceExpression(target, inst.Name, inst.TypeArguments.Select(ConvertType)); } var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); return new InvocationExpression(targetExpr, arguments.Select(a => a.Expression)) .WithILInstruction(inst) .WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Invocation, arguments.Select(a => a.ResolveResult).ToArray())); } protected internal override TranslatedExpression VisitDynamicInvokeInstruction(DynamicInvokeInstruction inst, TranslationContext context) { var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); return new InvocationExpression(target, arguments.Select(a => a.Expression)) .WithILInstruction(inst) .WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Invocation, arguments.Select(a => a.ResolveResult).ToArray())); } TranslatedExpression TranslateDynamicTarget(ILInstruction inst, CSharpArgumentInfo argumentInfo) { Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.NamedArgument)); Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsOut)); if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst, out var callTargetType)) { return new TypeReferenceExpression(ConvertType(callTargetType)) .WithoutILInstruction() .WithRR(new TypeResolveResult(callTargetType)); } IType targetType = SpecialType.Dynamic; if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) { targetType = argumentInfo.CompileTimeType; } var translatedTarget = Translate(inst, targetType).ConvertTo(targetType, this); if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsRef) && translatedTarget.Expression is DirectionExpression) { // (ref x).member => x.member translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); } return translatedTarget; } IEnumerable TranslateDynamicArguments(IEnumerable arguments, IEnumerable argumentInfo) { foreach (var (argument, info) in arguments.Zip(argumentInfo)) { yield return TranslateDynamicArgument(argument, info); } } TranslatedExpression TranslateDynamicArgument(ILInstruction argument, CSharpArgumentInfo info) { Debug.Assert(!info.HasFlag(CSharpArgumentInfoFlags.IsStaticType)); IType typeHint = SpecialType.Dynamic; if (info.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) { typeHint = info.CompileTimeType; } var translatedExpression = Translate(argument, typeHint); if (!(typeHint.Equals(SpecialType.Dynamic) && translatedExpression.Type.Equals(SpecialType.NullType))) { translatedExpression = translatedExpression.ConvertTo(typeHint, this); } if (info.HasFlag(CSharpArgumentInfoFlags.IsOut)) { translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out, argument is AddressOf); } if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name)) { translatedExpression = new TranslatedExpression(new NamedArgumentExpression(info.Name, translatedExpression.Expression)); } return translatedExpression; } internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind, bool isAddressOf) { if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr)) return input; if (isAddressOf && kind is ReferenceKind.In or ReferenceKind.RefReadOnly) { return input.UnwrapChild(dirExpr.Expression); } dirExpr.FieldDirection = kind switch { ReferenceKind.Ref => FieldDirection.Ref, ReferenceKind.Out => FieldDirection.Out, ReferenceKind.In => FieldDirection.In, ReferenceKind.RefReadOnly => FieldDirection.In, _ => throw new NotSupportedException("Unsupported reference kind: " + kind) }; dirExpr.RemoveAnnotations(); if (brrr.ElementResult == null) brrr = new ByReferenceResolveResult(brrr.ElementType, kind); else brrr = new ByReferenceResolveResult(brrr.ElementResult, kind); dirExpr.AddAnnotation(brrr); return new TranslatedExpression(dirExpr); } protected internal override TranslatedExpression VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst, TranslationContext context) { Debug.Assert(inst.Arguments.Count >= 3); var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); var value = new TranslatedExpression(arguments.Last()); var indexer = new IndexerExpression(target, arguments.SkipLast(1).Select(a => a.Expression)) .WithoutILInstruction() .WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Indexing, arguments.SkipLast(1).Select(a => a.ResolveResult).ToArray())); return Assignment(indexer, value).WithILInstruction(inst); } protected internal override TranslatedExpression VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst, TranslationContext context) { var target = TranslateDynamicTarget(inst.Target, inst.TargetArgumentInfo); var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); var member = new MemberReferenceExpression(target, inst.Name) .WithoutILInstruction() .WithRR(new DynamicMemberResolveResult(target.ResolveResult, inst.Name)); return Assignment(member, value).WithILInstruction(inst); } protected internal override TranslatedExpression VisitDynamicBinaryOperatorInstruction(DynamicBinaryOperatorInstruction inst, TranslationContext context) { switch (inst.Operation) { case ExpressionType.Add: case ExpressionType.AddAssign: return CreateBinaryOperator(BinaryOperatorType.Add, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.AddChecked: case ExpressionType.AddAssignChecked: return CreateBinaryOperator(BinaryOperatorType.Add, isChecked: true); case ExpressionType.Subtract: case ExpressionType.SubtractAssign: return CreateBinaryOperator(BinaryOperatorType.Subtract, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.SubtractChecked: case ExpressionType.SubtractAssignChecked: return CreateBinaryOperator(BinaryOperatorType.Subtract, isChecked: true); case ExpressionType.Multiply: case ExpressionType.MultiplyAssign: return CreateBinaryOperator(BinaryOperatorType.Multiply, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.MultiplyChecked: case ExpressionType.MultiplyAssignChecked: return CreateBinaryOperator(BinaryOperatorType.Multiply, isChecked: true); case ExpressionType.Divide: case ExpressionType.DivideAssign: return CreateBinaryOperator(BinaryOperatorType.Divide); case ExpressionType.Modulo: case ExpressionType.ModuloAssign: return CreateBinaryOperator(BinaryOperatorType.Modulus); case ExpressionType.Equal: return CreateBinaryOperator(BinaryOperatorType.Equality); case ExpressionType.NotEqual: return CreateBinaryOperator(BinaryOperatorType.InEquality); case ExpressionType.LessThan: return CreateBinaryOperator(BinaryOperatorType.LessThan); case ExpressionType.LessThanOrEqual: return CreateBinaryOperator(BinaryOperatorType.LessThanOrEqual); case ExpressionType.GreaterThan: return CreateBinaryOperator(BinaryOperatorType.GreaterThan); case ExpressionType.GreaterThanOrEqual: return CreateBinaryOperator(BinaryOperatorType.GreaterThanOrEqual); case ExpressionType.And: case ExpressionType.AndAssign: return CreateBinaryOperator(BinaryOperatorType.BitwiseAnd); case ExpressionType.Or: case ExpressionType.OrAssign: return CreateBinaryOperator(BinaryOperatorType.BitwiseOr); case ExpressionType.ExclusiveOr: case ExpressionType.ExclusiveOrAssign: return CreateBinaryOperator(BinaryOperatorType.ExclusiveOr); case ExpressionType.LeftShift: case ExpressionType.LeftShiftAssign: return CreateBinaryOperator(BinaryOperatorType.ShiftLeft); case ExpressionType.RightShift: case ExpressionType.RightShiftAssign: return CreateBinaryOperator(BinaryOperatorType.ShiftRight); default: return base.VisitDynamicBinaryOperatorInstruction(inst, context); } TranslatedExpression CreateBinaryOperator(BinaryOperatorType operatorType, bool? isChecked = null) { var left = TranslateDynamicArgument(inst.Left, inst.LeftArgumentInfo); var right = TranslateDynamicArgument(inst.Right, inst.RightArgumentInfo); var boe = new BinaryOperatorExpression(left.Expression, operatorType, right.Expression); if (isChecked == true) boe.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); else if (isChecked == false) boe.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); return boe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); } } protected internal override TranslatedExpression VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst, TranslationContext context) { BinaryOperatorType operatorType; if (inst.Operation == ExpressionType.AndAlso) { operatorType = BinaryOperatorType.ConditionalAnd; } else if (inst.Operation == ExpressionType.OrElse) { operatorType = BinaryOperatorType.ConditionalOr; } else { Debug.Fail("Unknown operation for DynamicLogicOperatorInstruction"); return base.VisitDynamicLogicOperatorInstruction(inst, context); } var left = TranslateDynamicArgument(inst.Left, inst.LeftArgumentInfo); var right = TranslateDynamicArgument(inst.Right, inst.RightArgumentInfo); var boe = new BinaryOperatorExpression(left.Expression, operatorType, right.Expression); return boe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); } protected internal override TranslatedExpression VisitDynamicUnaryOperatorInstruction(DynamicUnaryOperatorInstruction inst, TranslationContext context) { switch (inst.Operation) { case ExpressionType.Not: return CreateUnaryOperator(UnaryOperatorType.Not); case ExpressionType.Decrement: return CreateUnaryOperator(UnaryOperatorType.Decrement, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.Increment: return CreateUnaryOperator(UnaryOperatorType.Increment, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.Negate: return CreateUnaryOperator(UnaryOperatorType.Minus, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.NegateChecked: return CreateUnaryOperator(UnaryOperatorType.Minus, isChecked: true); case ExpressionType.UnaryPlus: return CreateUnaryOperator(UnaryOperatorType.Plus, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); case ExpressionType.IsTrue: var operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); Expression expr; if (inst.SlotInfo == IfInstruction.ConditionSlot) { // We rely on the context implicitly invoking "operator true". expr = new UnaryOperatorExpression(UnaryOperatorType.IsTrue, operand); } else { // Create a dummy conditional to ensure "operator true" will be invoked. expr = new ConditionalExpression(operand, new PrimitiveExpression(true), new PrimitiveExpression(false)); } return expr.WithILInstruction(inst) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); case ExpressionType.IsFalse: operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); // Create a dummy conditional to ensure "operator false" will be invoked. expr = new ConditionalExpression(operand, new PrimitiveExpression(false), new PrimitiveExpression(true)); return expr.WithILInstruction(inst) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); default: return base.VisitDynamicUnaryOperatorInstruction(inst, context); } TranslatedExpression CreateUnaryOperator(UnaryOperatorType operatorType, bool? isChecked = null) { var operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); var uoe = new UnaryOperatorExpression(operatorType, operand.Expression); if (isChecked == true) uoe.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); else if (isChecked == false) uoe.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); return uoe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); } } protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context) { ExpressionWithResolveResult target; if (inst.TargetKind == CompoundTargetKind.Address) { target = LdObj(inst.Target, SpecialType.Dynamic); } else { target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); } var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value); if (inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)) ae.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); else ae.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); return ae.WithILInstruction(inst) .WithRR(new OperatorResolveResult(SpecialType.Dynamic, inst.Operation, new[] { target.ResolveResult, value.ResolveResult })); } protected internal override TranslatedExpression VisitLdFtn(LdFtn inst, TranslationContext context) { ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: false); if (!inst.Method.IsStatic) { // C# 9 function pointers don't support instance methods return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef) .WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void)))) .WithILInstruction(inst); } // C# 9 function pointer SignatureCallingConvention callingConvention = SignatureCallingConvention.Default; ImmutableArray customCallingConventions; var unmanagedCallersOnlyAttribute = inst.Method.GetAttributes().FirstOrDefault(m => m.AttributeType.IsKnownType(KnownAttribute.UnmanagedCallersOnly)); if (unmanagedCallersOnlyAttribute != null) { callingConvention = SignatureCallingConvention.Unmanaged; var callingConventionsArgument = unmanagedCallersOnlyAttribute.NamedArguments.FirstOrDefault(a => a.Name == "CallConvs"); if (callingConventionsArgument.Value is ImmutableArray> array) { var builder = ImmutableArray.CreateBuilder(array.Length); foreach (var type in array.Select(a => a.Value).OfType()) { SignatureCallingConvention? foundCallingConvention = type.Namespace is not "System.Runtime.CompilerServices" || callingConvention != SignatureCallingConvention.Unmanaged ? null : type.Name switch { "CallConvCdecl" => SignatureCallingConvention.CDecl, "CallConvFastcall" => SignatureCallingConvention.FastCall, "CallConvStdcall" => SignatureCallingConvention.StdCall, "CallConvThiscall" => SignatureCallingConvention.ThisCall, _ => null, }; if (foundCallingConvention is not null) { callingConvention = foundCallingConvention.Value; } else { builder.Add(type); } } customCallingConventions = builder.ToImmutable(); } else { customCallingConventions = []; } } else { callingConvention = SignatureCallingConvention.Default; customCallingConventions = []; } var ftp = new FunctionPointerType( typeSystem.MainModule, callingConvention, customCallingConventions, inst.Method.ReturnType, inst.Method.ReturnTypeIsRefReadOnly, inst.Method.Parameters.SelectImmutableArray(p => p.Type), inst.Method.Parameters.SelectImmutableArray(p => p.ReferenceKind) ); ExpressionWithResolveResult addressOf = new UnaryOperatorExpression( UnaryOperatorType.AddressOf, delegateRef ).WithRR(new ResolveResult(SpecialType.NoType)).WithILInstruction(inst); var conversion = Conversion.MethodGroupConversion( inst.Method, isVirtualMethodLookup: false, delegateCapturesFirstArgument: false); return new CastExpression(ConvertType(ftp), addressOf) .WithRR(new ConversionResolveResult(ftp, addressOf.ResolveResult, conversion)) .WithoutILInstruction(); } protected internal override TranslatedExpression VisitLdVirtFtn(LdVirtFtn inst, TranslationContext context) { // C# 9 function pointers don't support instance methods ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: true); return new InvocationExpression(new IdentifierExpression("__ldvirtftn"), delegateRef) .WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void)))) .WithILInstruction(inst); } protected internal override TranslatedExpression VisitCallIndirect(CallIndirect inst, TranslationContext context) { if (inst.IsInstance) { return ErrorExpression("calli with instance method signature not supportd"); } var functionPointer = Translate(inst.FunctionPointer, typeHint: inst.FunctionPointerType); if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(functionPointer.Type, inst.FunctionPointerType)) { functionPointer = functionPointer.ConvertTo(inst.FunctionPointerType, this); } var fpt = (FunctionPointerType)functionPointer.Type.SkipModifiers(); var invocation = new InvocationExpression(); invocation.Target = functionPointer; foreach (var (argInst, (paramType, paramRefKind)) in inst.Arguments.Zip(fpt.ParameterTypes.Zip(fpt.ParameterReferenceKinds))) { var arg = Translate(argInst, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true); if (paramRefKind != ReferenceKind.None) { arg = ChangeDirectionExpressionTo(arg, paramRefKind, argInst is AddressOf); } invocation.Arguments.Add(arg); } if (fpt.ReturnType.SkipModifiers() is ByReferenceType brt) { var rr = new ResolveResult(brt.ElementType); return new DirectionExpression( FieldDirection.Ref, invocation.WithRR(rr).WithILInstruction(inst) ).WithRR(new ByReferenceResolveResult(rr, ReferenceKind.Ref)).WithoutILInstruction(); } else { return invocation.WithRR(new ResolveResult(fpt.ReturnType)).WithILInstruction(inst); } } protected internal override TranslatedExpression VisitDeconstructInstruction(DeconstructInstruction inst, TranslationContext context) { IType rhsType = inst.Pattern.Variable.Type; var rhs = Translate(inst.Pattern.TestedOperand, rhsType); rhs = rhs.ConvertTo(rhsType, this); // TODO allowImplicitConversion var assignments = inst.Assignments.Instructions; int assignmentPos = 0; var inits = inst.Init; int initPos = 0; Dictionary conversionMapping = new Dictionary(); foreach (var conv in inst.Conversions.Instructions) { if (!DeconstructInstruction.IsConversionStLoc(conv, out var outputVariable, out var inputVariable)) continue; conversionMapping.Add(inputVariable, outputVariable); } var lhs = ConstructTuple(inst.Pattern); return new AssignmentExpression(lhs, rhs) .WithILInstruction(inst) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Void))); TupleExpression ConstructTuple(MatchInstruction matchInstruction) { var expr = new TupleExpression(); foreach (var subPattern in matchInstruction.SubPatterns.Cast()) { if (subPattern.IsVar) { if (subPattern.HasDesignator) { if (!conversionMapping.TryGetValue(subPattern.Variable, out ILVariable value)) { value = subPattern.Variable; } expr.Elements.Add(ConstructAssignmentTarget(assignments[assignmentPos], value)); assignmentPos++; } else expr.Elements.Add(new IdentifierExpression("_")); } else { expr.Elements.Add(ConstructTuple(subPattern)); } } expr.AddAnnotation(matchInstruction); return expr; } TranslatedExpression ConstructAssignmentTarget(ILInstruction assignment, ILVariable value) { switch (assignment) { case StLoc stloc: Debug.Assert(stloc.Value.MatchLdLoc(value)); break; case CallInstruction call: for (int i = 0; i < call.Arguments.Count - 1; i++) { ReplaceAssignmentTarget(call.Arguments[i]); } Debug.Assert(call.Arguments.Last().MatchLdLoc(value)); break; case StObj stobj: var target = stobj.Target; while (target.MatchLdFlda(out var nestedTarget, out _)) target = nestedTarget; ReplaceAssignmentTarget(target); Debug.Assert(stobj.Value.MatchLdLoc(value)); break; default: throw new NotSupportedException(); } var expr = Translate(assignment); return expr.UnwrapChild(((AssignmentExpression)expr).Left); } void ReplaceAssignmentTarget(ILInstruction target) { if (target.MatchLdLoc(out var v) && v.Kind == VariableKind.DeconstructionInitTemporary) { Debug.Assert(inits[initPos].Variable == v); target.ReplaceWith(inits[initPos].Value); initPos++; } } } protected internal override TranslatedExpression VisitMatchInstruction(MatchInstruction inst, TranslationContext context) { var left = Translate(inst.TestedOperand); // remove boxing conversion if possible, however, we still need a cast in // test case PatternMatching.GenericValueTypePatternStringRequiresCastToObject if (left.ResolveResult is ConversionResolveResult crr && crr.Conversion.IsBoxingConversion && left.Expression is CastExpression castExpr && (crr.Input.Type.IsReferenceType != false || inst.Variable.Type.IsReferenceType == false)) { left = left.UnwrapChild(castExpr.Expression); } var right = TranslatePattern(inst, left.Type); return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))) .WithILInstruction(inst); } ExpressionWithILInstruction TranslatePattern(ILInstruction pattern, IType leftHandType) { switch (pattern) { case MatchInstruction matchInstruction: if (matchInstruction.IsDeconstructCall) throw new NotImplementedException(); if (matchInstruction.IsDeconstructTuple) throw new NotImplementedException(); if (matchInstruction.SubPatterns.Count > 0 || (matchInstruction.CheckNotNull && !matchInstruction.CheckType)) { RecursivePatternExpression recursivePatternExpression = new(); if (matchInstruction.CheckType) { recursivePatternExpression.Type = ConvertType(matchInstruction.Variable.Type); } else { Debug.Assert(matchInstruction.CheckNotNull || matchInstruction.Variable.Type.IsReferenceType == false); } foreach (var subPattern in matchInstruction.SubPatterns) { if (!MatchInstruction.IsPatternMatch(subPattern, out var testedOperand, settings)) { Debug.Fail("Invalid sub pattern"); continue; } IMember member; if (testedOperand is CallInstruction call) { member = call.Method.AccessorOwner; } else if (testedOperand.MatchLdFld(out _, out var f)) { member = f; } else { Debug.Fail("Invalid sub pattern"); continue; } recursivePatternExpression.SubPatterns.Add( new NamedArgumentExpression { Name = member.Name, Expression = TranslatePattern(subPattern, member.ReturnType) } .WithRR(new MemberResolveResult(null, member)) ); } if (matchInstruction.HasDesignator) { SingleVariableDesignation designator = new SingleVariableDesignation { Identifier = matchInstruction.Variable.Name }; designator.AddAnnotation(new ILVariableResolveResult(matchInstruction.Variable)); recursivePatternExpression.Designation = designator; } return recursivePatternExpression.WithILInstruction(matchInstruction); } else if (matchInstruction.HasDesignator || !matchInstruction.CheckType) { SingleVariableDesignation designator = new SingleVariableDesignation { Identifier = matchInstruction.Variable.Name }; designator.AddAnnotation(new ILVariableResolveResult(matchInstruction.Variable)); AstType type; if (matchInstruction.CheckType) { type = ConvertType(matchInstruction.Variable.Type); } else { Debug.Assert(matchInstruction.IsVar); type = new SimpleType("var"); } return new DeclarationExpression { Type = type, Designation = designator }.WithILInstruction(matchInstruction); } else { Debug.Assert(matchInstruction.CheckType); return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type)) .WithILInstruction(matchInstruction); } case Comp comp: var constantValue = Translate(comp.Right, leftHandType); switch (comp.Kind) { case ComparisonKind.Equality: return constantValue .WithILInstruction(comp); case ComparisonKind.Inequality: return new UnaryOperatorExpression(UnaryOperatorType.PatternNot, constantValue) .WithILInstruction(comp); case ComparisonKind.LessThan: return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThan, constantValue) .WithILInstruction(comp); case ComparisonKind.LessThanOrEqual: return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThanOrEqual, constantValue) .WithILInstruction(comp); case ComparisonKind.GreaterThan: return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThan, constantValue) .WithILInstruction(comp); case ComparisonKind.GreaterThanOrEqual: return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThanOrEqual, constantValue) .WithILInstruction(comp); default: throw new InvalidOperationException("Unexpected comparison kind: " + comp.Kind); } case Call call when MatchInstruction.IsCallToOpEquality(call, KnownTypeCode.String): return Translate(call.Arguments[1]); case Call call when MatchInstruction.IsCallToOpEquality(call, KnownTypeCode.Decimal): return Translate(call.Arguments[1]); default: throw new NotImplementedException(); } } protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context) { string message = "Error"; if (inst.StartILOffset != 0) { message += $" near IL_{inst.StartILOffset:x4}"; } if (!string.IsNullOrEmpty(inst.Message)) { message += ": " + inst.Message; } return ErrorExpression(message); } protected internal override TranslatedExpression VisitInvalidExpression(InvalidExpression inst, TranslationContext context) { string message = inst.Severity; if (inst.StartILOffset != 0) { message += $" near IL_{inst.StartILOffset:x4}"; } if (!string.IsNullOrEmpty(inst.Message)) { message += ": " + inst.Message; } return ErrorExpression(message); } protected override TranslatedExpression Default(ILInstruction inst, TranslationContext context) { return ErrorExpression("OpCode not supported: " + inst.OpCode); } static TranslatedExpression ErrorExpression(string message) { var e = new ErrorExpression(); e.AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); return e.WithoutILInstruction().WithRR(ErrorResolveResult.UnknownError); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.IO; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// C# ambience. Used to convert type system symbols to text (usually for displaying the symbol to the user; e.g. in editor tooltips) /// public class CSharpAmbience : IAmbience { public ConversionFlags ConversionFlags { get; set; } #region ConvertSymbol public string ConvertSymbol(ISymbol symbol) { if (symbol == null) throw new ArgumentNullException(nameof(symbol)); StringWriter writer = new StringWriter(); ConvertSymbol(symbol, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateEmpty()); return writer.ToString(); } public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if (symbol == null) throw new ArgumentNullException(nameof(symbol)); if (writer == null) throw new ArgumentNullException(nameof(writer)); if (formattingPolicy == null) throw new ArgumentNullException(nameof(formattingPolicy)); TypeSystemAstBuilder astBuilder = CreateAstBuilder(); AstNode node = astBuilder.ConvertSymbol(symbol); writer.StartNode(node); if (node is EntityDeclaration entityDecl) PrintModifiers(entityDecl.Modifiers, writer); if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) { if (node is TypeDeclaration) { switch (((TypeDeclaration)node).ClassType) { case ClassType.Class: writer.WriteKeyword(Roles.ClassKeyword, "class"); break; case ClassType.Struct: writer.WriteKeyword(Roles.StructKeyword, "struct"); break; case ClassType.Interface: writer.WriteKeyword(Roles.InterfaceKeyword, "interface"); break; case ClassType.Enum: writer.WriteKeyword(Roles.EnumKeyword, "enum"); break; case ClassType.RecordClass: writer.WriteKeyword(Roles.RecordKeyword, "record"); break; case ClassType.RecordStruct: writer.WriteKeyword(Roles.RecordKeyword, "record"); writer.Space(); writer.WriteKeyword(Roles.StructKeyword, "struct"); break; default: throw new Exception("Invalid value for ClassType"); } writer.Space(); } else if (node is DelegateDeclaration) { writer.WriteKeyword(Roles.DelegateKeyword, "delegate"); writer.Space(); } else if (node is EventDeclaration) { writer.WriteKeyword(EventDeclaration.EventKeywordRole, "event"); writer.Space(); } else if (node is NamespaceDeclaration) { writer.WriteKeyword(Roles.NamespaceKeyword, "namespace"); writer.Space(); } } if ((ConversionFlags & ConversionFlags.PlaceReturnTypeAfterParameterList) != ConversionFlags.PlaceReturnTypeAfterParameterList && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); if (!rt.IsNull) { rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); writer.Space(); } } if (symbol is ITypeDefinition) WriteTypeDeclarationName((ITypeDefinition)symbol, writer, formattingPolicy); else if (symbol is IMember) WriteMemberDeclarationName((IMember)symbol, writer, formattingPolicy); else writer.WriteIdentifier(Identifier.Create(symbol.Name)); if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(symbol)) { writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, symbol.SymbolKind == SymbolKind.Indexer ? "[" : "("); bool first = true; foreach (var param in node.GetChildrenByRole(Roles.Parameter)) { if ((ConversionFlags & ConversionFlags.ShowParameterModifiers) == 0) { param.ParameterModifier = ReferenceKind.None; param.IsScopedRef = false; param.IsParams = false; } if ((ConversionFlags & ConversionFlags.ShowParameterDefaultValues) == 0) { param.DefaultExpression.Detach(); } if (first) { first = false; } else { writer.WriteToken(Roles.Comma, ","); writer.Space(); } param.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.RBracket : Roles.RPar, symbol.SymbolKind == SymbolKind.Indexer ? "]" : ")"); } if ((ConversionFlags & ConversionFlags.PlaceReturnTypeAfterParameterList) == ConversionFlags.PlaceReturnTypeAfterParameterList && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); if (!rt.IsNull) { writer.Space(); writer.WriteToken(Roles.Colon, ":"); writer.Space(); if (symbol is IField f && CSharpDecompiler.IsFixedField(f, out var type, out int elementCount)) { rt = astBuilder.ConvertType(type); new IndexerExpression(new TypeReferenceExpression(rt), astBuilder.ConvertConstantValue(f.Compilation.FindType(KnownTypeCode.Int32), elementCount)) .AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } else { rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } } } if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) { if (symbol is IProperty property) { writer.Space(); writer.WriteToken(Roles.LBrace, "{"); writer.Space(); if (property.CanGet) { writer.WriteKeyword(PropertyDeclaration.GetKeywordRole, "get"); writer.WriteToken(Roles.Semicolon, ";"); writer.Space(); } if (property.CanSet) { if ((ConversionFlags & ConversionFlags.SupportInitAccessors) != 0 && property.Setter.IsInitOnly) writer.WriteKeyword(PropertyDeclaration.InitKeywordRole, "init"); else writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); writer.WriteToken(Roles.Semicolon, ";"); writer.Space(); } writer.WriteToken(Roles.RBrace, "}"); } else { writer.WriteToken(Roles.Semicolon, ";"); } writer.EndNode(node); } } static bool HasParameters(ISymbol e) { switch (e.SymbolKind) { case SymbolKind.TypeDefinition: return ((ITypeDefinition)e).Kind == TypeKind.Delegate; case SymbolKind.Indexer: case SymbolKind.Method: case SymbolKind.Operator: case SymbolKind.Constructor: case SymbolKind.Destructor: return true; default: return false; } } TypeSystemAstBuilder CreateAstBuilder() { TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(); astBuilder.AddResolveResultAnnotations = true; astBuilder.ShowTypeParametersForUnboundTypes = true; astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; astBuilder.UsePrivateProtectedAccessibility = (ConversionFlags & ConversionFlags.UsePrivateProtectedAccessibility) == ConversionFlags.UsePrivateProtectedAccessibility; astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; astBuilder.UseNullableSpecifierForValueTypes = (ConversionFlags & ConversionFlags.UseNullableSpecifierForValueTypes) != 0; astBuilder.SupportInitAccessors = (ConversionFlags & ConversionFlags.SupportInitAccessors) != 0; astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0; astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0; astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0; astBuilder.SupportOperatorChecked = (ConversionFlags & ConversionFlags.SupportOperatorChecked) != 0; return astBuilder; } void WriteTypeDeclarationName(ITypeDefinition typeDef, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); EntityDeclaration node = astBuilder.ConvertEntity(typeDef); if (typeDef.DeclaringTypeDefinition != null && ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType || (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) == ConversionFlags.UseFullyQualifiedEntityNames)) { WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) == ConversionFlags.UseFullyQualifiedEntityNames) { if (!string.IsNullOrEmpty(typeDef.Namespace)) { WriteQualifiedName(typeDef.Namespace, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } } writer.WriteIdentifier(node.NameToken); WriteTypeParameters(node, writer, formattingPolicy); } void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); EntityDeclaration node = astBuilder.ConvertEntity(member); if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null && !(member is LocalFunctionMethod)) { ConvertType(member.DeclaringType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } IType? explicitInterfaceType = GetExplicitInterfaceType(member); string name = member.Name; if (explicitInterfaceType != null) { name = name.Substring(name.LastIndexOf('.') + 1); } switch (member.SymbolKind) { case SymbolKind.Indexer: if (explicitInterfaceType != null) { ConvertType(explicitInterfaceType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } writer.WriteKeyword(Roles.Identifier, "this"); break; case SymbolKind.Constructor: WriteQualifiedName(member.DeclaringType!.Name, writer, formattingPolicy); break; case SymbolKind.Destructor: writer.WriteToken(DestructorDeclaration.TildeRole, "~"); WriteQualifiedName(member.DeclaringType!.Name, writer, formattingPolicy); break; case SymbolKind.Operator: switch (name) { case "op_Implicit": writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit"); writer.Space(); if (explicitInterfaceType != null) { ConvertType(explicitInterfaceType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); ConvertType(member.ReturnType, writer, formattingPolicy); break; case "op_Explicit": case "op_CheckedExplicit": writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit"); writer.Space(); if (explicitInterfaceType != null) { ConvertType(explicitInterfaceType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); if (name == "op_CheckedExplicit") { writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked"); writer.Space(); } ConvertType(member.ReturnType, writer, formattingPolicy); break; default: if (explicitInterfaceType != null) { ConvertType(explicitInterfaceType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); var operatorType = OperatorDeclaration.GetOperatorType(name); if (operatorType.HasValue && !((ConversionFlags & ConversionFlags.SupportOperatorChecked) == 0 && OperatorDeclaration.IsChecked(operatorType.Value))) { if (OperatorDeclaration.IsChecked(operatorType.Value)) { writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked"); writer.Space(); } writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value)); } else { writer.WriteIdentifier(node.NameToken); } break; } break; default: if (explicitInterfaceType != null) { ConvertType(explicitInterfaceType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } writer.WriteIdentifier(Identifier.Create(name)); break; } WriteTypeParameters(node, writer, formattingPolicy); } void WriteTypeParameters(EntityDeclaration node, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) { var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); IEnumerable typeParameters = node.GetChildrenByRole(Roles.TypeParameter); if ((ConversionFlags & ConversionFlags.ShowTypeParameterVarianceModifier) == 0) { typeParameters = typeParameters.Select(RemoveVarianceModifier); } outputVisitor.WriteTypeParameters(typeParameters); } TypeParameterDeclaration RemoveVarianceModifier(TypeParameterDeclaration decl) { decl.Variance = VarianceModifier.Invariant; return decl; } } void PrintModifiers(Modifiers modifiers, TokenWriter writer) { foreach (var m in CSharpModifierToken.AllModifiers) { if ((modifiers & m) == m) { writer.WriteKeyword(EntityDeclaration.ModifierRole, CSharpModifierToken.GetModifierName(m)); writer.Space(); } } } void WriteQualifiedName(string name, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { var node = AstType.Create(name); var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); node.AcceptVisitor(outputVisitor); } #endregion public string ConvertVariable(IVariable v) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); AstNode astNode = astBuilder.ConvertVariable(v); return astNode.ToString().TrimEnd(';', '\r', '\n', (char)8232); } public string ConvertType(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); TypeSystemAstBuilder astBuilder = CreateAstBuilder(); astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; AstType astType = astBuilder.ConvertType(type); return astType.ToString(); } void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; AstType astType = astBuilder.ConvertType(type); astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } IType? GetExplicitInterfaceType(IMember member) { if (member.IsExplicitInterfaceImplementation) { var baseMember = member.ExplicitlyImplementedInterfaceMembers.FirstOrDefault(); if (baseMember != null) return baseMember.DeclaringType; } return null; } public string ConvertConstantValue(object constantValue) { return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); } public string WrapComment(string comment) { return "// " + comment; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpFormattingOptions.cs ================================================ // // CSharpFormattingOptions.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.ComponentModel; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { public enum BraceStyle { EndOfLine, EndOfLineWithoutSpace, NextLine, NextLineShifted, NextLineShifted2, BannerStyle } public enum PropertyFormatting { SingleLine, MultipleLines } public enum Wrapping { DoNotWrap, WrapAlways, WrapIfTooLong } public enum NewLinePlacement { DoNotCare, NewLine, SameLine } public enum UsingPlacement { TopOfFile, InsideNamespace } public enum EmptyLineFormatting { DoNotChange, Indent, DoNotIndent } [TypeConverter(typeof(ExpandableObjectConverter))] public class CSharpFormattingOptions { public string Name { get; set; } public bool IsBuiltIn { get; set; } public CSharpFormattingOptions Clone() { return (CSharpFormattingOptions)MemberwiseClone(); } #region Indentation public string IndentationString { get; set; } = "\t"; public bool IndentNamespaceBody { // tested get; set; } public bool IndentClassBody { // tested get; set; } public bool IndentInterfaceBody { // tested get; set; } public bool IndentStructBody { // tested get; set; } public bool IndentEnumBody { // tested get; set; } public bool IndentMethodBody { // tested get; set; } public bool IndentPropertyBody { // tested get; set; } public bool IndentEventBody { // tested get; set; } public bool IndentBlocks { // tested get; set; } public bool IndentSwitchBody { // tested get; set; } public bool IndentCaseBody { // tested get; set; } public bool IndentBreakStatements { // tested get; set; } public bool AlignEmbeddedStatements { // tested get; set; } public bool AlignElseInIfStatements { get; set; } public PropertyFormatting AutoPropertyFormatting { get; set; } public PropertyFormatting SimplePropertyFormatting { // tested get; set; } public EmptyLineFormatting EmptyLineFormatting { get; set; } public bool IndentPreprocessorDirectives { // tested get; set; } public bool AlignToMemberReferenceDot { // TODO! get; set; } public bool IndentBlocksInsideExpressions { get; set; } #endregion #region Braces public BraceStyle NamespaceBraceStyle { // tested get; set; } public BraceStyle ClassBraceStyle { // tested get; set; } public BraceStyle InterfaceBraceStyle { // tested get; set; } public BraceStyle StructBraceStyle { // tested get; set; } public BraceStyle EnumBraceStyle { // tested get; set; } public BraceStyle MethodBraceStyle { // tested get; set; } public BraceStyle AnonymousMethodBraceStyle { get; set; } public BraceStyle ConstructorBraceStyle { // tested get; set; } public BraceStyle DestructorBraceStyle { // tested get; set; } public BraceStyle PropertyBraceStyle { // tested get; set; } public BraceStyle PropertyGetBraceStyle { // tested get; set; } public BraceStyle PropertySetBraceStyle { // tested get; set; } public PropertyFormatting SimpleGetBlockFormatting { // tested get; set; } public PropertyFormatting SimpleSetBlockFormatting { // tested get; set; } public BraceStyle EventBraceStyle { // tested get; set; } public BraceStyle EventAddBraceStyle { // tested get; set; } public BraceStyle EventRemoveBraceStyle { // tested get; set; } public bool AllowEventAddBlockInline { // tested get; set; } public bool AllowEventRemoveBlockInline { // tested get; set; } public BraceStyle StatementBraceStyle { // tested get; set; } public bool AllowIfBlockInline { get; set; } bool allowOneLinedArrayInitialziers = true; public bool AllowOneLinedArrayInitialziers { get { return allowOneLinedArrayInitialziers; } set { allowOneLinedArrayInitialziers = value; } } #endregion #region NewLines public NewLinePlacement ElseNewLinePlacement { // tested get; set; } public NewLinePlacement ElseIfNewLinePlacement { // tested get; set; } public NewLinePlacement CatchNewLinePlacement { // tested get; set; } public NewLinePlacement FinallyNewLinePlacement { // tested get; set; } public NewLinePlacement WhileNewLinePlacement { // tested get; set; } NewLinePlacement embeddedStatementPlacement = NewLinePlacement.NewLine; public NewLinePlacement EmbeddedStatementPlacement { get { return embeddedStatementPlacement; } set { embeddedStatementPlacement = value; } } #endregion #region Spaces public bool SpaceBetweenParameterAttributeSections { get; set; } // Methods public bool SpaceBeforeMethodDeclarationParentheses { // tested get; set; } public bool SpaceBetweenEmptyMethodDeclarationParentheses { get; set; } public bool SpaceBeforeMethodDeclarationParameterComma { // tested get; set; } public bool SpaceAfterMethodDeclarationParameterComma { // tested get; set; } public bool SpaceWithinMethodDeclarationParentheses { // tested get; set; } // Method calls public bool SpaceBeforeMethodCallParentheses { // tested get; set; } public bool SpaceBetweenEmptyMethodCallParentheses { // tested get; set; } public bool SpaceBeforeMethodCallParameterComma { // tested get; set; } public bool SpaceAfterMethodCallParameterComma { // tested get; set; } public bool SpaceWithinMethodCallParentheses { // tested get; set; } // fields public bool SpaceBeforeFieldDeclarationComma { // tested get; set; } public bool SpaceAfterFieldDeclarationComma { // tested get; set; } // local variables public bool SpaceBeforeLocalVariableDeclarationComma { // tested get; set; } public bool SpaceAfterLocalVariableDeclarationComma { // tested get; set; } // constructors public bool SpaceBeforeConstructorDeclarationParentheses { // tested get; set; } public bool SpaceBetweenEmptyConstructorDeclarationParentheses { // tested get; set; } public bool SpaceBeforeConstructorDeclarationParameterComma { // tested get; set; } public bool SpaceAfterConstructorDeclarationParameterComma { // tested get; set; } public bool SpaceWithinConstructorDeclarationParentheses { // tested get; set; } public NewLinePlacement NewLineBeforeConstructorInitializerColon { get; set; } public NewLinePlacement NewLineAfterConstructorInitializerColon { get; set; } // indexer public bool SpaceBeforeIndexerDeclarationBracket { // tested get; set; } public bool SpaceWithinIndexerDeclarationBracket { // tested get; set; } public bool SpaceBeforeIndexerDeclarationParameterComma { get; set; } public bool SpaceAfterIndexerDeclarationParameterComma { get; set; } // delegates public bool SpaceBeforeDelegateDeclarationParentheses { get; set; } public bool SpaceBetweenEmptyDelegateDeclarationParentheses { get; set; } public bool SpaceBeforeDelegateDeclarationParameterComma { get; set; } public bool SpaceAfterDelegateDeclarationParameterComma { get; set; } public bool SpaceWithinDelegateDeclarationParentheses { get; set; } public bool SpaceBeforeNewParentheses { // tested get; set; } public bool SpaceBeforeIfParentheses { // tested get; set; } public bool SpaceBeforeWhileParentheses { // tested get; set; } public bool SpaceBeforeForParentheses { // tested get; set; } public bool SpaceBeforeForeachParentheses { // tested get; set; } public bool SpaceBeforeCatchParentheses { // tested get; set; } public bool SpaceBeforeSwitchParentheses { // tested get; set; } public bool SpaceBeforeLockParentheses { // tested get; set; } public bool SpaceBeforeUsingParentheses { // tested get; set; } public bool SpaceAroundAssignment { // tested get; set; } public bool SpaceAroundLogicalOperator { // tested get; set; } public bool SpaceAroundEqualityOperator { // tested get; set; } public bool SpaceAroundRelationalOperator { // tested get; set; } public bool SpaceAroundBitwiseOperator { // tested get; set; } public bool SpaceAroundAdditiveOperator { // tested get; set; } public bool SpaceAroundMultiplicativeOperator { // tested get; set; } public bool SpaceAroundShiftOperator { // tested get; set; } public bool SpaceAroundNullCoalescingOperator { // Tested get; set; } public bool SpaceAfterUnsafeAddressOfOperator { // Tested get; set; } public bool SpaceAfterUnsafeAsteriskOfOperator { // Tested get; set; } public bool SpaceAroundUnsafeArrowOperator { // Tested get; set; } public bool SpacesWithinParentheses { // tested get; set; } public bool SpacesWithinIfParentheses { // tested get; set; } public bool SpacesWithinWhileParentheses { // tested get; set; } public bool SpacesWithinForParentheses { // tested get; set; } public bool SpacesWithinForeachParentheses { // tested get; set; } public bool SpacesWithinCatchParentheses { // tested get; set; } public bool SpacesWithinSwitchParentheses { // tested get; set; } public bool SpacesWithinLockParentheses { // tested get; set; } public bool SpacesWithinUsingParentheses { // tested get; set; } public bool SpacesWithinCastParentheses { // tested get; set; } public bool SpacesWithinSizeOfParentheses { // tested get; set; } public bool SpaceBeforeSizeOfParentheses { // tested get; set; } public bool SpacesWithinTypeOfParentheses { // tested get; set; } public bool SpacesWithinNewParentheses { // tested get; set; } public bool SpacesBetweenEmptyNewParentheses { // tested get; set; } public bool SpaceBeforeNewParameterComma { // tested get; set; } public bool SpaceAfterNewParameterComma { // tested get; set; } public bool SpaceBeforeTypeOfParentheses { // tested get; set; } public bool SpacesWithinCheckedExpressionParantheses { // tested get; set; } public bool SpaceBeforeConditionalOperatorCondition { // tested get; set; } public bool SpaceAfterConditionalOperatorCondition { // tested get; set; } public bool SpaceBeforeConditionalOperatorSeparator { // tested get; set; } public bool SpaceAfterConditionalOperatorSeparator { // tested get; set; } public bool SpaceBeforeAnonymousMethodParentheses { get; set; } public bool SpaceWithinAnonymousMethodParentheses { get; set; } // brackets public bool SpacesWithinBrackets { // tested get; set; } public bool SpacesBeforeBrackets { // tested get; set; } public bool SpaceBeforeBracketComma { // tested get; set; } public bool SpaceAfterBracketComma { // tested get; set; } public bool SpaceBeforeForSemicolon { // tested get; set; } public bool SpaceAfterForSemicolon { // tested get; set; } public bool SpaceAfterTypecast { // tested get; set; } public bool SpaceBeforeArrayDeclarationBrackets { // tested get; set; } public bool SpaceInNamedArgumentAfterDoubleColon { get; set; } public bool RemoveEndOfLineWhiteSpace { get; set; } public bool SpaceBeforeSemicolon { get; set; } #endregion #region Blank Lines public int MinimumBlankLinesBeforeUsings { get; set; } public int MinimumBlankLinesAfterUsings { get; set; } public int MinimumBlankLinesBeforeFirstDeclaration { get; set; } public int MinimumBlankLinesBetweenTypes { get; set; } public int MinimumBlankLinesBetweenFields { get; set; } public int MinimumBlankLinesBetweenEventFields { get; set; } public int MinimumBlankLinesBetweenMembers { get; set; } public int MinimumBlankLinesAroundRegion { get; set; } public int MinimumBlankLinesInsideRegion { get; set; } #endregion #region Keep formatting public bool KeepCommentsAtFirstColumn { get; set; } #endregion #region Wrapping public Wrapping ArrayInitializerWrapping { get; set; } public BraceStyle ArrayInitializerBraceStyle { get; set; } public Wrapping ChainedMethodCallWrapping { get; set; } public Wrapping MethodCallArgumentWrapping { get; set; } public NewLinePlacement NewLineAferMethodCallOpenParentheses { get; set; } public NewLinePlacement MethodCallClosingParenthesesOnNewLine { get; set; } public Wrapping IndexerArgumentWrapping { get; set; } public NewLinePlacement NewLineAferIndexerOpenBracket { get; set; } public NewLinePlacement IndexerClosingBracketOnNewLine { get; set; } public Wrapping MethodDeclarationParameterWrapping { get; set; } public NewLinePlacement NewLineAferMethodDeclarationOpenParentheses { get; set; } public NewLinePlacement MethodDeclarationClosingParenthesesOnNewLine { get; set; } public Wrapping IndexerDeclarationParameterWrapping { get; set; } public NewLinePlacement NewLineAferIndexerDeclarationOpenBracket { get; set; } public NewLinePlacement IndexerDeclarationClosingBracketOnNewLine { get; set; } public bool AlignToFirstIndexerArgument { get; set; } public bool AlignToFirstIndexerDeclarationParameter { get; set; } public bool AlignToFirstMethodCallArgument { get; set; } public bool AlignToFirstMethodDeclarationParameter { get; set; } public NewLinePlacement NewLineBeforeNewQueryClause { get; set; } #endregion #region Using Declarations public UsingPlacement UsingPlacement { get; set; } #endregion internal CSharpFormattingOptions() { } /*public static CSharpFormattingOptions Load (FilePath selectedFile) { using (var stream = System.IO.File.OpenRead (selectedFile)) { return Load (stream); } } public static CSharpFormattingOptions Load (System.IO.Stream input) { CSharpFormattingOptions result = FormattingOptionsFactory.CreateMonoOptions (); result.Name = "noname"; using (XmlTextReader reader = new XmlTextReader (input)) { while (reader.Read ()) { if (reader.NodeType == XmlNodeType.Element) { if (reader.LocalName == "Property") { var info = typeof(CSharpFormattingOptions).GetProperty (reader.GetAttribute ("name")); string valString = reader.GetAttribute ("value"); object value; if (info.PropertyType == typeof(bool)) { value = Boolean.Parse (valString); } else if (info.PropertyType == typeof(int)) { value = Int32.Parse (valString); } else { value = Enum.Parse (info.PropertyType, valString); } info.SetValue (result, value, null); } else if (reader.LocalName == "FormattingProfile") { result.Name = reader.GetAttribute ("name"); } } else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "FormattingProfile") { //Console.WriteLine ("result:" + result.Name); return result; } } } return result; } public void Save (string fileName) { using (var writer = new XmlTextWriter (fileName, Encoding.Default)) { writer.Formatting = System.Xml.Formatting.Indented; writer.Indentation = 1; writer.IndentChar = '\t'; writer.WriteStartElement ("FormattingProfile"); writer.WriteAttributeString ("name", Name); foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { writer.WriteStartElement ("Property"); writer.WriteAttributeString ("name", info.Name); writer.WriteAttributeString ("value", info.GetValue (this, null).ToString ()); writer.WriteEndElement (); } } writer.WriteEndElement (); } } public bool Equals (CSharpFormattingOptions other) { foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { object val = info.GetValue (this, null); object otherVal = info.GetValue (other, null); if (val == null) { if (otherVal == null) continue; return false; } if (!val.Equals (otherVal)) { //Console.WriteLine ("!equal"); return false; } } } //Console.WriteLine ("== equal"); return true; }*/ } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs ================================================ // Copyright (c) 2010-2020 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 // 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.IO; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using Attribute = ICSharpCode.Decompiler.CSharp.Syntax.Attribute; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// Outputs the AST. /// public class CSharpOutputVisitor : IAstVisitor { readonly protected TokenWriter writer; readonly protected CSharpFormattingOptions policy; readonly protected Stack containerStack = new Stack(); public CSharpOutputVisitor(TextWriter textWriter, CSharpFormattingOptions formattingPolicy) { if (textWriter == null) { throw new ArgumentNullException(nameof(textWriter)); } if (formattingPolicy == null) { throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = TokenWriter.Create(textWriter, formattingPolicy.IndentationString); this.policy = formattingPolicy; } public CSharpOutputVisitor(TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (formattingPolicy == null) { throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); this.policy = formattingPolicy; } #region StartNode/EndNode protected virtual void StartNode(AstNode node) { // Ensure that nodes are visited in the proper nested order. // Jumps to different subtrees are allowed only for the child of a placeholder node. Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern); containerStack.Push(node); writer.StartNode(node); } protected virtual void EndNode(AstNode node) { Debug.Assert(node == containerStack.Peek()); containerStack.Pop(); writer.EndNode(node); } #endregion #region Comma /// /// Writes a comma. /// /// The next node after the comma. /// When set prevents printing a space after comma. protected virtual void Comma(AstNode nextNode, bool noSpaceAfterComma = false) { Space(policy.SpaceBeforeBracketComma); // TODO: Comma policy has changed. writer.WriteToken(Roles.Comma, ","); isAfterSpace = false; Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); // TODO: Comma policy has changed. } /// /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer /// protected virtual void OptionalComma(AstNode pos) { // Look if there's a comma after the current node, and insert it if it exists. while (pos != null && pos.NodeType == NodeType.Whitespace) { pos = pos.NextSibling; } if (pos != null && pos.Role == Roles.Comma) { Comma(null, noSpaceAfterComma: true); } } /// /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. /// protected virtual void OptionalSemicolon(AstNode pos) { // Look if there's a semicolon after the current node, and insert it if it exists. while (pos != null && pos.NodeType == NodeType.Whitespace) { pos = pos.PrevSibling; } if (pos != null && pos.Role == Roles.Semicolon) { Semicolon(); } } protected virtual void WriteCommaSeparatedList(IEnumerable list) { bool isFirst = true; foreach (AstNode node in list) { if (isFirst) { isFirst = false; } else { Comma(node); } node.AcceptVisitor(this); } } protected virtual void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { LPar(); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } RPar(); } protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { WriteToken(Roles.LBracket); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } WriteToken(Roles.RBracket); } protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list) { WriteToken(Roles.LBracket); if (list.Any()) { Space(policy.SpacesWithinBrackets); WriteCommaSeparatedList(list); Space(policy.SpacesWithinBrackets); } WriteToken(Roles.RBracket); } #endregion #region Write tokens protected bool isAtStartOfLine = true; protected bool isAfterSpace; /// /// Writes a keyword, and all specials up to /// protected virtual void WriteKeyword(TokenRole tokenRole) { WriteKeyword(tokenRole.Token, tokenRole); } protected virtual void WriteKeyword(string token, Role tokenRole = null) { writer.WriteKeyword(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteIdentifier(Identifier identifier) { writer.WriteIdentifier(identifier); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteIdentifier(string identifier) { AstType.Create(identifier).AcceptVisitor(this); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteToken(TokenRole tokenRole) { WriteToken(tokenRole.Token, tokenRole); } protected virtual void WriteToken(string token, Role tokenRole) { writer.WriteToken(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void LPar() { WriteToken(Roles.LPar); } protected virtual void RPar() { WriteToken(Roles.RPar); } /// /// Marks the end of a statement /// protected virtual void Semicolon() { // get the role of the current node Role role = containerStack.Peek().Role; if (!SkipToken()) { WriteToken(Roles.Semicolon); if (!SkipNewLine()) NewLine(); else Space(); } bool SkipToken() { return role == ForStatement.InitializerRole || role == ForStatement.IteratorRole || role == UsingStatement.ResourceAcquisitionRole; } bool SkipNewLine() { if (containerStack.Peek() is not Accessor accessor) return false; if (!(role == PropertyDeclaration.GetterRole || role == PropertyDeclaration.SetterRole)) return false; bool isAutoProperty = accessor.Body.IsNull && !accessor.Attributes.Any() && policy.AutoPropertyFormatting == PropertyFormatting.SingleLine; return isAutoProperty; } } /// /// Writes a space depending on policy. /// protected virtual void Space(bool addSpace = true) { if (addSpace && !isAfterSpace) { writer.Space(); isAfterSpace = true; } } protected virtual void NewLine() { writer.NewLine(); isAtStartOfLine = true; isAfterSpace = false; } int GetCallChainLengthLimited(MemberReferenceExpression expr) { int callChainLength = 0; var node = expr; while (node.Target is InvocationExpression invocation && invocation.Target is MemberReferenceExpression mre && callChainLength < 4) { node = mre; callChainLength++; } return callChainLength; } int ShouldInsertNewLineWhenInMethodCallChain(MemberReferenceExpression expr) { int callChainLength = GetCallChainLengthLimited(expr); if (callChainLength < 3) return 0; if (expr.GetParent(n => n is Statement || n is LambdaExpression || n is InterpolatedStringContent) is InterpolatedStringContent) return 0; return callChainLength; } protected virtual bool InsertNewLineWhenInMethodCallChain(MemberReferenceExpression expr) { int callChainLength = ShouldInsertNewLineWhenInMethodCallChain(expr); if (callChainLength == 0) return false; if (callChainLength == 3) writer.Indent(); writer.NewLine(); isAtStartOfLine = true; isAfterSpace = false; return true; } protected virtual void OpenBrace(BraceStyle style, bool newLine = true) { switch (style) { case BraceStyle.EndOfLine: case BraceStyle.BannerStyle: if (!isAtStartOfLine) Space(); WriteToken("{", Roles.LBrace); break; case BraceStyle.EndOfLineWithoutSpace: WriteToken("{", Roles.LBrace); break; case BraceStyle.NextLine: if (!isAtStartOfLine) NewLine(); WriteToken("{", Roles.LBrace); break; case BraceStyle.NextLineShifted: NewLine(); writer.Indent(); WriteToken("{", Roles.LBrace); NewLine(); return; case BraceStyle.NextLineShifted2: NewLine(); writer.Indent(); WriteToken("{", Roles.LBrace); break; default: throw new ArgumentOutOfRangeException(); } if (newLine) { writer.Indent(); NewLine(); } } protected virtual void CloseBrace(BraceStyle style, bool unindent = true) { switch (style) { case BraceStyle.EndOfLine: case BraceStyle.EndOfLineWithoutSpace: case BraceStyle.NextLine: if (unindent) writer.Unindent(); WriteToken("}", Roles.RBrace); break; case BraceStyle.BannerStyle: case BraceStyle.NextLineShifted: WriteToken("}", Roles.RBrace); if (unindent) writer.Unindent(); break; case BraceStyle.NextLineShifted2: if (unindent) writer.Unindent(); WriteToken("}", Roles.RBrace); if (unindent) writer.Unindent(); break; default: throw new ArgumentOutOfRangeException(); } } #endregion #region IsKeyword Test static readonly HashSet unconditionalKeywords = new HashSet { "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while" }; static readonly HashSet queryKeywords = new HashSet { "from", "where", "join", "on", "equals", "into", "let", "orderby", "ascending", "descending", "select", "group", "by" }; static readonly int maxKeywordLength = unconditionalKeywords.Concat(queryKeywords).Max(s => s.Length); /// /// Determines whether the specified identifier is a keyword in the given context. /// If is all keywords are treated as unconditional. /// public static bool IsKeyword(string identifier, AstNode context = null) { // only 2-10 char lower-case identifiers can be keywords if (identifier.Length > maxKeywordLength || identifier.Length < 2 || identifier[0] < 'a') { return false; } if (unconditionalKeywords.Contains(identifier)) { return true; } if (queryKeywords.Contains(identifier)) { return context == null || context.Ancestors.Any(ancestor => ancestor is QueryExpression); } if (identifier == "await") { if (context == null) return true; foreach (AstNode ancestor in context.Ancestors) { // with lambdas/anonymous methods, if (ancestor is LambdaExpression) { return ((LambdaExpression)ancestor).IsAsync; } if (ancestor is AnonymousMethodExpression) { return ((AnonymousMethodExpression)ancestor).IsAsync; } if (ancestor is EntityDeclaration) { return (((EntityDeclaration)ancestor).Modifiers & Modifiers.Async) == Modifiers.Async; } } } return false; } #endregion #region Write constructs protected virtual void WriteTypeArguments(IEnumerable typeArguments) { if (typeArguments.Any()) { WriteToken(Roles.LChevron); WriteCommaSeparatedList(typeArguments); WriteToken(Roles.RChevron); } } public virtual void WriteTypeParameters(IEnumerable typeParameters) { if (typeParameters.Any()) { WriteToken(Roles.LChevron); WriteCommaSeparatedList(typeParameters); WriteToken(Roles.RChevron); } } protected virtual void WriteModifiers(IEnumerable modifierTokens) { foreach (CSharpModifierToken modifier in modifierTokens) { modifier.AcceptVisitor(this); Space(); } } protected virtual void WriteQualifiedIdentifier(IEnumerable identifiers) { bool first = true; foreach (Identifier ident in identifiers) { if (first) { first = false; } else { writer.WriteToken(Roles.Dot, "."); } writer.WriteIdentifier(ident); } } /// /// Writes an embedded statement. /// /// The statement to write. /// Determines whether a trailing newline should be written following a block. /// Non-blocks always write a trailing newline. /// /// Blocks may or may not write a leading newline depending on StatementBraceStyle. /// Non-blocks always write a leading newline. /// protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine) { if (embeddedStatement.IsNull) { NewLine(); return; } BlockStatement block = embeddedStatement as BlockStatement; if (block != null) { WriteBlock(block, policy.StatementBraceStyle); if (nlp == NewLinePlacement.SameLine) { Space(); // if not a trailing newline, then at least a trailing space } else { NewLine(); } } else { NewLine(); writer.Indent(); embeddedStatement.AcceptVisitor(this); writer.Unindent(); } } protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true) { if (body.IsNull) { Semicolon(); } else { WriteBlock(body, style); NewLine(); } } protected virtual void WriteAttributes(IEnumerable attributes) { foreach (AttributeSection attr in attributes) { attr.AcceptVisitor(this); } } protected virtual void WritePrivateImplementationType(AstType privateImplementationType) { if (!privateImplementationType.IsNull) { privateImplementationType.AcceptVisitor(this); WriteToken(Roles.Dot); } } #endregion #region Expressions public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { StartNode(anonymousMethodExpression); if (anonymousMethodExpression.IsAsync) { WriteKeyword(AnonymousMethodExpression.AsyncModifierRole); Space(); } WriteKeyword(AnonymousMethodExpression.DelegateKeywordRole); if (anonymousMethodExpression.HasParameterList) { Space(policy.SpaceBeforeAnonymousMethodParentheses); WriteCommaSeparatedListInParenthesis(anonymousMethodExpression.Parameters, policy.SpaceWithinAnonymousMethodParentheses); } WriteBlock(anonymousMethodExpression.Body, policy.AnonymousMethodBraceStyle); EndNode(anonymousMethodExpression); } public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) { StartNode(undocumentedExpression); switch (undocumentedExpression.UndocumentedExpressionType) { case UndocumentedExpressionType.ArgList: case UndocumentedExpressionType.ArgListAccess: WriteKeyword(UndocumentedExpression.ArglistKeywordRole); break; case UndocumentedExpressionType.MakeRef: WriteKeyword(UndocumentedExpression.MakerefKeywordRole); break; case UndocumentedExpressionType.RefType: WriteKeyword(UndocumentedExpression.ReftypeKeywordRole); break; case UndocumentedExpressionType.RefValue: WriteKeyword(UndocumentedExpression.RefvalueKeywordRole); break; } if (undocumentedExpression.UndocumentedExpressionType != UndocumentedExpressionType.ArgListAccess) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(undocumentedExpression.Arguments, policy.SpaceWithinMethodCallParentheses); } EndNode(undocumentedExpression); } public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { StartNode(arrayCreateExpression); WriteKeyword(ArrayCreateExpression.NewKeywordRole); arrayCreateExpression.Type.AcceptVisitor(this); if (arrayCreateExpression.Arguments.Count > 0) { WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); } foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { specifier.AcceptVisitor(this); } arrayCreateExpression.Initializer.AcceptVisitor(this); EndNode(arrayCreateExpression); } public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { StartNode(arrayInitializerExpression); // "new List { { 1 } }" and "new List { 1 }" are the same semantically. // We also use the same AST for both: we always use two nested ArrayInitializerExpressions // for collection initializers, even if the user did not write nested brackets. // The output visitor will output nested braces only if they are necessary, // or if the braces tokens exist in the AST. bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1 && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent) && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single()); if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) { arrayInitializerExpression.Elements.Single().AcceptVisitor(this); } else { PrintInitializerElements(arrayInitializerExpression.Elements); } EndNode(arrayInitializerExpression); } protected bool CanBeConfusedWithObjectInitializer(Expression expr) { // "int a; new List { a = 1 };" is an object initalizers and invalid, but // "int a; new List { { a = 1 } };" is a valid collection initializer. AssignmentExpression ae = expr as AssignmentExpression; return ae != null && ae.Operator == AssignmentOperatorType.Assign; } protected bool IsObjectOrCollectionInitializer(AstNode node) { if (!(node is ArrayInitializerExpression)) { return false; } if (node.Parent is ObjectCreateExpression) { return node.Role == ObjectCreateExpression.InitializerRole; } if (node.Parent is NamedExpression) { return node.Role == Roles.Expression; } return false; } protected virtual void PrintInitializerElements(AstNodeCollection elements) { bool wrapAlways = policy.ArrayInitializerWrapping == Wrapping.WrapAlways || (elements.Count > 1 && elements.Any(e => !IsSimpleExpression(e))) || elements.Any(IsComplexExpression); bool wrap = wrapAlways || elements.Count > 10; OpenBrace(wrap ? policy.ArrayInitializerBraceStyle : BraceStyle.EndOfLine, newLine: wrap); if (!wrap) Space(); AstNode last = null; foreach (var (idx, node) in elements.WithIndex()) { if (idx > 0) { Comma(node, noSpaceAfterComma: true); if (wrapAlways || idx % 10 == 0) NewLine(); else Space(); } last = node; node.AcceptVisitor(this); } if (last != null) OptionalComma(last.NextSibling); if (wrap) NewLine(); else Space(); CloseBrace(wrap ? policy.ArrayInitializerBraceStyle : BraceStyle.EndOfLine, unindent: wrap); bool IsSimpleExpression(Expression ex) { switch (ex) { case NullReferenceExpression _: case ThisReferenceExpression _: case PrimitiveExpression _: case IdentifierExpression _: case MemberReferenceExpression { Target: ThisReferenceExpression or IdentifierExpression or BaseReferenceExpression } _: return true; default: return false; } } bool IsComplexExpression(Expression ex) { switch (ex) { case AnonymousMethodExpression _: case LambdaExpression _: case AnonymousTypeCreateExpression _: case ObjectCreateExpression _: case NamedExpression _: return true; default: return false; } } } public virtual void VisitAsExpression(AsExpression asExpression) { StartNode(asExpression); asExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(AsExpression.AsKeywordRole); Space(); asExpression.Type.AcceptVisitor(this); EndNode(asExpression); } public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { StartNode(assignmentExpression); assignmentExpression.Left.AcceptVisitor(this); Space(policy.SpaceAroundAssignment); WriteToken(AssignmentExpression.GetOperatorRole(assignmentExpression.Operator)); Space(policy.SpaceAroundAssignment); assignmentExpression.Right.AcceptVisitor(this); EndNode(assignmentExpression); } public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) { StartNode(baseReferenceExpression); WriteKeyword("base", baseReferenceExpression.Role); EndNode(baseReferenceExpression); } public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { StartNode(binaryOperatorExpression); binaryOperatorExpression.Left.AcceptVisitor(this); bool spacePolicy; switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: spacePolicy = policy.SpaceAroundBitwiseOperator; break; case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: spacePolicy = policy.SpaceAroundLogicalOperator; break; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: case BinaryOperatorType.LessThanOrEqual: case BinaryOperatorType.LessThan: spacePolicy = policy.SpaceAroundRelationalOperator; break; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: spacePolicy = policy.SpaceAroundEqualityOperator; break; case BinaryOperatorType.Add: case BinaryOperatorType.Subtract: spacePolicy = policy.SpaceAroundAdditiveOperator; break; case BinaryOperatorType.Multiply: case BinaryOperatorType.Divide: case BinaryOperatorType.Modulus: spacePolicy = policy.SpaceAroundMultiplicativeOperator; break; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: case BinaryOperatorType.UnsignedShiftRight: spacePolicy = policy.SpaceAroundShiftOperator; break; case BinaryOperatorType.NullCoalescing: case BinaryOperatorType.IsPattern: spacePolicy = true; break; case BinaryOperatorType.Range: spacePolicy = false; break; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); } Space(spacePolicy); TokenRole tokenRole = BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator); if (tokenRole == BinaryOperatorExpression.IsKeywordRole) { WriteKeyword(tokenRole); } else { WriteToken(tokenRole); } Space(spacePolicy); binaryOperatorExpression.Right.AcceptVisitor(this); EndNode(binaryOperatorExpression); } public virtual void VisitCastExpression(CastExpression castExpression) { StartNode(castExpression); LPar(); Space(policy.SpacesWithinCastParentheses); castExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinCastParentheses); RPar(); Space(policy.SpaceAfterTypecast); castExpression.Expression.AcceptVisitor(this); EndNode(castExpression); } public virtual void VisitCheckedExpression(CheckedExpression checkedExpression) { StartNode(checkedExpression); WriteKeyword(CheckedExpression.CheckedKeywordRole); LPar(); Space(policy.SpacesWithinCheckedExpressionParantheses); checkedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinCheckedExpressionParantheses); RPar(); EndNode(checkedExpression); } public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) { StartNode(conditionalExpression); conditionalExpression.Condition.AcceptVisitor(this); Space(policy.SpaceBeforeConditionalOperatorCondition); WriteToken(ConditionalExpression.QuestionMarkRole); Space(policy.SpaceAfterConditionalOperatorCondition); conditionalExpression.TrueExpression.AcceptVisitor(this); Space(policy.SpaceBeforeConditionalOperatorSeparator); WriteToken(ConditionalExpression.ColonRole); Space(policy.SpaceAfterConditionalOperatorSeparator); conditionalExpression.FalseExpression.AcceptVisitor(this); EndNode(conditionalExpression); } public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { StartNode(defaultValueExpression); WriteKeyword(DefaultValueExpression.DefaultKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); defaultValueExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); EndNode(defaultValueExpression); } public virtual void VisitDirectionExpression(DirectionExpression directionExpression) { StartNode(directionExpression); switch (directionExpression.FieldDirection) { case FieldDirection.Out: WriteKeyword(DirectionExpression.OutKeywordRole); break; case FieldDirection.Ref: WriteKeyword(DirectionExpression.RefKeywordRole); break; case FieldDirection.In: WriteKeyword(DirectionExpression.InKeywordRole); break; default: throw new NotSupportedException("Invalid value for FieldDirection"); } Space(); directionExpression.Expression.AcceptVisitor(this); EndNode(directionExpression); } public virtual void VisitDeclarationExpression(DeclarationExpression declarationExpression) { StartNode(declarationExpression); declarationExpression.Type.AcceptVisitor(this); Space(); declarationExpression.Designation.AcceptVisitor(this); EndNode(declarationExpression); } public virtual void VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression) { StartNode(recursivePatternExpression); recursivePatternExpression.Type.AcceptVisitor(this); Space(); if (recursivePatternExpression.IsPositional) { WriteToken(Roles.LPar); } else { WriteToken(Roles.LBrace); } Space(); WriteCommaSeparatedList(recursivePatternExpression.SubPatterns); Space(); if (recursivePatternExpression.IsPositional) { WriteToken(Roles.RPar); } else { WriteToken(Roles.RBrace); } if (!recursivePatternExpression.Designation.IsNull) { Space(); recursivePatternExpression.Designation.AcceptVisitor(this); } EndNode(recursivePatternExpression); } public virtual void VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression) { StartNode(outVarDeclarationExpression); WriteKeyword(OutVarDeclarationExpression.OutKeywordRole); Space(); outVarDeclarationExpression.Type.AcceptVisitor(this); Space(); outVarDeclarationExpression.Variable.AcceptVisitor(this); EndNode(outVarDeclarationExpression); } public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression) { StartNode(identifierExpression); WriteIdentifier(identifierExpression.IdentifierToken); WriteTypeArguments(identifierExpression.TypeArguments); EndNode(identifierExpression); } public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) { StartNode(indexerExpression); indexerExpression.Target.AcceptVisitor(this); Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); EndNode(indexerExpression); } public virtual void VisitInvocationExpression(InvocationExpression invocationExpression) { StartNode(invocationExpression); invocationExpression.Target.AcceptVisitor(this); Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, policy.SpaceWithinMethodCallParentheses); if (!(invocationExpression.Parent is MemberReferenceExpression)) { if (invocationExpression.Target is MemberReferenceExpression mre) { if (ShouldInsertNewLineWhenInMethodCallChain(mre) >= 3) writer.Unindent(); } } EndNode(invocationExpression); } public virtual void VisitIsExpression(IsExpression isExpression) { StartNode(isExpression); isExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(IsExpression.IsKeywordRole); isExpression.Type.AcceptVisitor(this); EndNode(isExpression); } public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression) { StartNode(lambdaExpression); WriteAttributes(lambdaExpression.Attributes); if (lambdaExpression.IsAsync) { WriteKeyword(LambdaExpression.AsyncModifierRole); Space(); } if (LambdaNeedsParenthesis(lambdaExpression)) { WriteCommaSeparatedListInParenthesis(lambdaExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } else { lambdaExpression.Parameters.Single().AcceptVisitor(this); } Space(); WriteToken(Roles.Arrow); if (lambdaExpression.Body is BlockStatement) { WriteBlock((BlockStatement)lambdaExpression.Body, policy.AnonymousMethodBraceStyle); } else { Space(); lambdaExpression.Body.AcceptVisitor(this); } EndNode(lambdaExpression); } protected bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) { if (lambdaExpression.Parameters.Count != 1) { return true; } var p = lambdaExpression.Parameters.Single(); return !(p.Type.IsNull && p.ParameterModifier == ReferenceKind.None && !p.IsParams); } public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { StartNode(memberReferenceExpression); memberReferenceExpression.Target.AcceptVisitor(this); bool insertedNewLine = InsertNewLineWhenInMethodCallChain(memberReferenceExpression); WriteToken(Roles.Dot); WriteIdentifier(memberReferenceExpression.MemberNameToken); WriteTypeArguments(memberReferenceExpression.TypeArguments); if (insertedNewLine && !(memberReferenceExpression.Parent is InvocationExpression)) { writer.Unindent(); } EndNode(memberReferenceExpression); } public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { StartNode(namedArgumentExpression); WriteIdentifier(namedArgumentExpression.NameToken); WriteToken(Roles.Colon); Space(); namedArgumentExpression.Expression.AcceptVisitor(this); EndNode(namedArgumentExpression); } public virtual void VisitNamedExpression(NamedExpression namedExpression) { StartNode(namedExpression); WriteIdentifier(namedExpression.NameToken); Space(); WriteToken(Roles.Assign); Space(); namedExpression.Expression.AcceptVisitor(this); EndNode(namedExpression); } public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { StartNode(nullReferenceExpression); writer.WritePrimitiveValue(null); isAfterSpace = false; EndNode(nullReferenceExpression); } public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { StartNode(objectCreateExpression); WriteKeyword(ObjectCreateExpression.NewKeywordRole); objectCreateExpression.Type.AcceptVisitor(this); bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; // also use parenthesis if there is an '(' token if (!objectCreateExpression.LParToken.IsNull) { useParenthesis = true; } if (useParenthesis) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); } objectCreateExpression.Initializer.AcceptVisitor(this); EndNode(objectCreateExpression); } public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) { StartNode(anonymousTypeCreateExpression); WriteKeyword(AnonymousTypeCreateExpression.NewKeywordRole); PrintInitializerElements(anonymousTypeCreateExpression.Initializers); EndNode(anonymousTypeCreateExpression); } public virtual void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { StartNode(parenthesizedExpression); LPar(); Space(policy.SpacesWithinParentheses); parenthesizedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinParentheses); RPar(); EndNode(parenthesizedExpression); } public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { StartNode(pointerReferenceExpression); pointerReferenceExpression.Target.AcceptVisitor(this); WriteToken(PointerReferenceExpression.ArrowRole); WriteIdentifier(pointerReferenceExpression.MemberNameToken); WriteTypeArguments(pointerReferenceExpression.TypeArguments); EndNode(pointerReferenceExpression); } #region VisitPrimitiveExpression public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { StartNode(primitiveExpression); writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.Format); isAfterSpace = false; EndNode(primitiveExpression); } public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression) { StartNode(interpolatedStringExpression); writer.WriteToken(InterpolatedStringExpression.OpenQuote, "$\""); foreach (var element in interpolatedStringExpression.Content) { element.AcceptVisitor(this); } writer.WriteToken(InterpolatedStringExpression.CloseQuote, "\""); isAfterSpace = false; EndNode(interpolatedStringExpression); } public virtual void VisitInterpolation(Interpolation interpolation) { StartNode(interpolation); writer.WriteToken(Interpolation.LBrace, "{"); interpolation.Expression.AcceptVisitor(this); if (interpolation.Alignment != 0) { writer.WriteToken(Roles.Comma, ","); writer.WritePrimitiveValue(interpolation.Alignment); } if (interpolation.Suffix != null) { writer.WriteToken(Roles.Colon, ":"); writer.WriteInterpolatedText(interpolation.Suffix); } writer.WriteToken(Interpolation.RBrace, "}"); EndNode(interpolation); } public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText) { StartNode(interpolatedStringText); writer.WriteInterpolatedText(interpolatedStringText.Text); EndNode(interpolatedStringText); } #endregion public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { StartNode(sizeOfExpression); WriteKeyword(SizeOfExpression.SizeofKeywordRole); LPar(); Space(policy.SpacesWithinSizeOfParentheses); sizeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinSizeOfParentheses); RPar(); EndNode(sizeOfExpression); } public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { StartNode(stackAllocExpression); WriteKeyword(StackAllocExpression.StackallocKeywordRole); stackAllocExpression.Type.AcceptVisitor(this); WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); stackAllocExpression.Initializer.AcceptVisitor(this); EndNode(stackAllocExpression); } public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) { StartNode(thisReferenceExpression); WriteKeyword("this", thisReferenceExpression.Role); EndNode(thisReferenceExpression); } public virtual void VisitThrowExpression(ThrowExpression throwExpression) { StartNode(throwExpression); WriteKeyword(ThrowExpression.ThrowKeywordRole); Space(); throwExpression.Expression.AcceptVisitor(this); EndNode(throwExpression); } public virtual void VisitTupleExpression(TupleExpression tupleExpression) { Debug.Assert(tupleExpression.Elements.Count >= 2); StartNode(tupleExpression); LPar(); WriteCommaSeparatedList(tupleExpression.Elements); RPar(); EndNode(tupleExpression); } public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression) { StartNode(typeOfExpression); WriteKeyword(TypeOfExpression.TypeofKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); typeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); EndNode(typeOfExpression); } public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { StartNode(typeReferenceExpression); typeReferenceExpression.Type.AcceptVisitor(this); EndNode(typeReferenceExpression); } public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { StartNode(unaryOperatorExpression); UnaryOperatorType opType = unaryOperatorExpression.Operator; var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); if (opType is UnaryOperatorType.Await or UnaryOperatorType.PatternNot) { WriteKeyword(opSymbol); Space(); } else if (!IsPostfixOperator(opType) && opSymbol != null) { WriteToken(opSymbol); } unaryOperatorExpression.Expression.AcceptVisitor(this); if (IsPostfixOperator(opType)) { WriteToken(opSymbol); } EndNode(unaryOperatorExpression); } static bool IsPostfixOperator(UnaryOperatorType op) { return op == UnaryOperatorType.PostIncrement || op == UnaryOperatorType.PostDecrement || op == UnaryOperatorType.NullConditional || op == UnaryOperatorType.SuppressNullableWarning; } public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { StartNode(uncheckedExpression); WriteKeyword(UncheckedExpression.UncheckedKeywordRole); LPar(); Space(policy.SpacesWithinCheckedExpressionParantheses); uncheckedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinCheckedExpressionParantheses); RPar(); EndNode(uncheckedExpression); } public virtual void VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression) { StartNode(withInitializerExpression); withInitializerExpression.Expression.AcceptVisitor(this); WriteKeyword("with", WithInitializerExpression.WithKeywordRole); withInitializerExpression.Initializer.AcceptVisitor(this); EndNode(withInitializerExpression); } #endregion #region Query Expressions public virtual void VisitQueryExpression(QueryExpression queryExpression) { StartNode(queryExpression); if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) writer.Indent(); bool first = true; foreach (var clause in queryExpression.Clauses) { if (first) { first = false; } else { if (!(clause is QueryContinuationClause)) { NewLine(); } } clause.AcceptVisitor(this); } if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) writer.Unindent(); EndNode(queryExpression); } public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { StartNode(queryContinuationClause); queryContinuationClause.PrecedingQuery.AcceptVisitor(this); Space(); WriteKeyword(QueryContinuationClause.IntoKeywordRole); Space(); WriteIdentifier(queryContinuationClause.IdentifierToken); EndNode(queryContinuationClause); } public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) { StartNode(queryFromClause); WriteKeyword(QueryFromClause.FromKeywordRole); queryFromClause.Type.AcceptVisitor(this); Space(); WriteIdentifier(queryFromClause.IdentifierToken); Space(); WriteKeyword(QueryFromClause.InKeywordRole); Space(); queryFromClause.Expression.AcceptVisitor(this); EndNode(queryFromClause); } public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) { StartNode(queryLetClause); WriteKeyword(QueryLetClause.LetKeywordRole); Space(); WriteIdentifier(queryLetClause.IdentifierToken); Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); queryLetClause.Expression.AcceptVisitor(this); EndNode(queryLetClause); } public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { StartNode(queryWhereClause); WriteKeyword(QueryWhereClause.WhereKeywordRole); Space(); queryWhereClause.Condition.AcceptVisitor(this); EndNode(queryWhereClause); } public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { StartNode(queryJoinClause); WriteKeyword(QueryJoinClause.JoinKeywordRole); queryJoinClause.Type.AcceptVisitor(this); Space(); WriteIdentifier(queryJoinClause.JoinIdentifierToken); Space(); WriteKeyword(QueryJoinClause.InKeywordRole); Space(); queryJoinClause.InExpression.AcceptVisitor(this); Space(); WriteKeyword(QueryJoinClause.OnKeywordRole); Space(); queryJoinClause.OnExpression.AcceptVisitor(this); Space(); WriteKeyword(QueryJoinClause.EqualsKeywordRole); Space(); queryJoinClause.EqualsExpression.AcceptVisitor(this); if (queryJoinClause.IsGroupJoin) { Space(); WriteKeyword(QueryJoinClause.IntoKeywordRole); WriteIdentifier(queryJoinClause.IntoIdentifierToken); } EndNode(queryJoinClause); } public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) { StartNode(queryOrderClause); WriteKeyword(QueryOrderClause.OrderbyKeywordRole); Space(); WriteCommaSeparatedList(queryOrderClause.Orderings); EndNode(queryOrderClause); } public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) { StartNode(queryOrdering); queryOrdering.Expression.AcceptVisitor(this); switch (queryOrdering.Direction) { case QueryOrderingDirection.Ascending: Space(); WriteKeyword(QueryOrdering.AscendingKeywordRole); break; case QueryOrderingDirection.Descending: Space(); WriteKeyword(QueryOrdering.DescendingKeywordRole); break; } EndNode(queryOrdering); } public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) { StartNode(querySelectClause); WriteKeyword(QuerySelectClause.SelectKeywordRole); Space(); querySelectClause.Expression.AcceptVisitor(this); EndNode(querySelectClause); } public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { StartNode(queryGroupClause); WriteKeyword(QueryGroupClause.GroupKeywordRole); Space(); queryGroupClause.Projection.AcceptVisitor(this); Space(); WriteKeyword(QueryGroupClause.ByKeywordRole); Space(); queryGroupClause.Key.AcceptVisitor(this); EndNode(queryGroupClause); } #endregion #region GeneralScope public virtual void VisitAttribute(Attribute attribute) { StartNode(attribute); attribute.Type.AcceptVisitor(this); if (attribute.Arguments.Count != 0 || attribute.HasArgumentList) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(attribute.Arguments, policy.SpaceWithinMethodCallParentheses); } EndNode(attribute); } public virtual void VisitAttributeSection(AttributeSection attributeSection) { StartNode(attributeSection); WriteToken(Roles.LBracket); if (!string.IsNullOrEmpty(attributeSection.AttributeTarget)) { WriteKeyword(attributeSection.AttributeTarget, Roles.Identifier); WriteToken(Roles.Colon); Space(); } WriteCommaSeparatedList(attributeSection.Attributes); WriteToken(Roles.RBracket); switch (attributeSection.Parent) { case ParameterDeclaration pd: if (pd.Attributes.Last() != attributeSection) Space(policy.SpaceBetweenParameterAttributeSections); else Space(); break; case TypeParameterDeclaration _: case ComposedType _: case LambdaExpression _: Space(); break; default: NewLine(); break; } EndNode(attributeSection); } public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { StartNode(delegateDeclaration); WriteAttributes(delegateDeclaration.Attributes); WriteModifiers(delegateDeclaration.ModifierTokens); WriteKeyword(Roles.DelegateKeyword); delegateDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteIdentifier(delegateDeclaration.NameToken); WriteTypeParameters(delegateDeclaration.TypeParameters); Space(policy.SpaceBeforeDelegateDeclarationParentheses); WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); foreach (Constraint constraint in delegateDeclaration.Constraints) { constraint.AcceptVisitor(this); } Semicolon(); EndNode(delegateDeclaration); } public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { StartNode(namespaceDeclaration); WriteKeyword(Roles.NamespaceKeyword); namespaceDeclaration.NamespaceName.AcceptVisitor(this); if (namespaceDeclaration.IsFileScoped) { Semicolon(); NewLine(); } else { OpenBrace(policy.NamespaceBraceStyle); } foreach (var member in namespaceDeclaration.Members) { member.AcceptVisitor(this); MaybeNewLinesAfterUsings(member); } if (!namespaceDeclaration.IsFileScoped) { CloseBrace(policy.NamespaceBraceStyle); OptionalSemicolon(namespaceDeclaration.LastChild); NewLine(); } EndNode(namespaceDeclaration); } public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { StartNode(typeDeclaration); WriteAttributes(typeDeclaration.Attributes); WriteModifiers(typeDeclaration.ModifierTokens); BraceStyle braceStyle; switch (typeDeclaration.ClassType) { case ClassType.Enum: WriteKeyword(Roles.EnumKeyword); braceStyle = policy.EnumBraceStyle; break; case ClassType.Interface: WriteKeyword(Roles.InterfaceKeyword); braceStyle = policy.InterfaceBraceStyle; break; case ClassType.Struct: WriteKeyword(Roles.StructKeyword); braceStyle = policy.StructBraceStyle; break; case ClassType.RecordClass: WriteKeyword(Roles.RecordKeyword); braceStyle = policy.ClassBraceStyle; break; case ClassType.RecordStruct: WriteKeyword(Roles.RecordStructKeyword); WriteKeyword(Roles.StructKeyword); braceStyle = policy.StructBraceStyle; break; default: WriteKeyword(Roles.ClassKeyword); braceStyle = policy.ClassBraceStyle; break; } WriteIdentifier(typeDeclaration.NameToken); WriteTypeParameters(typeDeclaration.TypeParameters); if (typeDeclaration.HasPrimaryConstructor) { Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(typeDeclaration.PrimaryConstructorParameters, policy.SpaceWithinMethodDeclarationParentheses); } if (typeDeclaration.BaseTypes.Any()) { Space(); WriteToken(Roles.Colon); Space(); WriteCommaSeparatedList(typeDeclaration.BaseTypes); } foreach (Constraint constraint in typeDeclaration.Constraints) { constraint.AcceptVisitor(this); } if (typeDeclaration.ClassType is (ClassType.RecordClass or ClassType.RecordStruct) && typeDeclaration.Members.Count == 0) { Semicolon(); } else { OpenBrace(braceStyle); if (typeDeclaration.ClassType == ClassType.Enum) { bool first = true; AstNode last = null; foreach (var member in typeDeclaration.Members) { if (first) { first = false; } else { Comma(member, noSpaceAfterComma: true); NewLine(); } last = member; member.AcceptVisitor(this); } if (last != null) OptionalComma(last.NextSibling); NewLine(); } else { bool first = true; foreach (var member in typeDeclaration.Members) { if (!first) { for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) NewLine(); } first = false; member.AcceptVisitor(this); } } CloseBrace(braceStyle); OptionalSemicolon(typeDeclaration.LastChild); NewLine(); } EndNode(typeDeclaration); } public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) { StartNode(usingAliasDeclaration); WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); Space(policy.SpaceAroundEqualityOperator); WriteToken(Roles.Assign); Space(policy.SpaceAroundEqualityOperator); usingAliasDeclaration.Import.AcceptVisitor(this); Semicolon(); EndNode(usingAliasDeclaration); } public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { StartNode(usingDeclaration); WriteKeyword(UsingDeclaration.UsingKeywordRole); usingDeclaration.Import.AcceptVisitor(this); Semicolon(); EndNode(usingDeclaration); } public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { StartNode(externAliasDeclaration); WriteKeyword(Roles.ExternKeyword); Space(); WriteKeyword(Roles.AliasKeyword); Space(); WriteIdentifier(externAliasDeclaration.NameToken); Semicolon(); EndNode(externAliasDeclaration); } #endregion #region Statements public virtual void VisitBlockStatement(BlockStatement blockStatement) { WriteBlock(blockStatement, policy.StatementBraceStyle); NewLine(); } /// /// Writes a block statement. /// Similar to VisitBlockStatement() except that: /// 1) it allows customizing the BraceStyle /// 2) it does not write a trailing newline after the '}' (this job is left to the caller) /// protected virtual void WriteBlock(BlockStatement blockStatement, BraceStyle style) { StartNode(blockStatement); OpenBrace(style); foreach (var node in blockStatement.Statements) { node.AcceptVisitor(this); } CloseBrace(style); EndNode(blockStatement); } public virtual void VisitBreakStatement(BreakStatement breakStatement) { StartNode(breakStatement); WriteKeyword("break", BreakStatement.BreakKeywordRole); Semicolon(); EndNode(breakStatement); } public virtual void VisitCheckedStatement(CheckedStatement checkedStatement) { StartNode(checkedStatement); WriteKeyword(CheckedStatement.CheckedKeywordRole); checkedStatement.Body.AcceptVisitor(this); EndNode(checkedStatement); } public virtual void VisitContinueStatement(ContinueStatement continueStatement) { StartNode(continueStatement); WriteKeyword("continue", ContinueStatement.ContinueKeywordRole); Semicolon(); EndNode(continueStatement); } public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { StartNode(doWhileStatement); WriteKeyword(DoWhileStatement.DoKeywordRole); WriteEmbeddedStatement(doWhileStatement.EmbeddedStatement, policy.WhileNewLinePlacement); WriteKeyword(DoWhileStatement.WhileKeywordRole); Space(policy.SpaceBeforeWhileParentheses); LPar(); Space(policy.SpacesWithinWhileParentheses); doWhileStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinWhileParentheses); RPar(); Semicolon(); EndNode(doWhileStatement); } public virtual void VisitEmptyStatement(EmptyStatement emptyStatement) { StartNode(emptyStatement); Semicolon(); EndNode(emptyStatement); } public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) { StartNode(expressionStatement); expressionStatement.Expression.AcceptVisitor(this); Semicolon(); EndNode(expressionStatement); } public virtual void VisitFixedStatement(FixedStatement fixedStatement) { StartNode(fixedStatement); WriteKeyword(FixedStatement.FixedKeywordRole); Space(policy.SpaceBeforeUsingParentheses); LPar(); Space(policy.SpacesWithinUsingParentheses); fixedStatement.Type.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fixedStatement.Variables); Space(policy.SpacesWithinUsingParentheses); RPar(); WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); EndNode(fixedStatement); } public virtual void VisitForeachStatement(ForeachStatement foreachStatement) { StartNode(foreachStatement); if (foreachStatement.IsAsync) WriteKeyword(ForeachStatement.AwaitRole); WriteKeyword(ForeachStatement.ForeachKeywordRole); Space(policy.SpaceBeforeForeachParentheses); LPar(); Space(policy.SpacesWithinForeachParentheses); foreachStatement.VariableType.AcceptVisitor(this); Space(); foreachStatement.VariableDesignation.AcceptVisitor(this); Space(); WriteKeyword(ForeachStatement.InKeywordRole); Space(); foreachStatement.InExpression.AcceptVisitor(this); Space(policy.SpacesWithinForeachParentheses); RPar(); WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); EndNode(foreachStatement); } public virtual void VisitForStatement(ForStatement forStatement) { StartNode(forStatement); WriteKeyword(ForStatement.ForKeywordRole); Space(policy.SpaceBeforeForParentheses); LPar(); Space(policy.SpacesWithinForParentheses); WriteCommaSeparatedList(forStatement.Initializers); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); Space(policy.SpaceAfterForSemicolon); forStatement.Condition.AcceptVisitor(this); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); if (forStatement.Iterators.Any()) { Space(policy.SpaceAfterForSemicolon); WriteCommaSeparatedList(forStatement.Iterators); } Space(policy.SpacesWithinForParentheses); RPar(); WriteEmbeddedStatement(forStatement.EmbeddedStatement); EndNode(forStatement); } public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) { StartNode(gotoCaseStatement); WriteKeyword(GotoCaseStatement.GotoKeywordRole); WriteKeyword(GotoCaseStatement.CaseKeywordRole); Space(); gotoCaseStatement.LabelExpression.AcceptVisitor(this); Semicolon(); EndNode(gotoCaseStatement); } public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) { StartNode(gotoDefaultStatement); WriteKeyword(GotoDefaultStatement.GotoKeywordRole); WriteKeyword(GotoDefaultStatement.DefaultKeywordRole); Semicolon(); EndNode(gotoDefaultStatement); } public virtual void VisitGotoStatement(GotoStatement gotoStatement) { StartNode(gotoStatement); WriteKeyword(GotoStatement.GotoKeywordRole); WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); Semicolon(); EndNode(gotoStatement); } public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement) { StartNode(ifElseStatement); WriteKeyword(IfElseStatement.IfKeywordRole); Space(policy.SpaceBeforeIfParentheses); LPar(); Space(policy.SpacesWithinIfParentheses); ifElseStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinIfParentheses); RPar(); if (ifElseStatement.FalseStatement.IsNull) { WriteEmbeddedStatement(ifElseStatement.TrueStatement); } else { WriteEmbeddedStatement(ifElseStatement.TrueStatement, policy.ElseNewLinePlacement); WriteKeyword(IfElseStatement.ElseKeywordRole); if (ifElseStatement.FalseStatement is IfElseStatement) { // don't put newline between 'else' and 'if' ifElseStatement.FalseStatement.AcceptVisitor(this); } else { WriteEmbeddedStatement(ifElseStatement.FalseStatement); } } EndNode(ifElseStatement); } public virtual void VisitLabelStatement(LabelStatement labelStatement) { StartNode(labelStatement); WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier)); WriteToken(Roles.Colon); bool foundLabelledStatement = false; for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { if (tmp.Role == labelStatement.Role) { foundLabelledStatement = true; } } if (!foundLabelledStatement) { // introduce an EmptyStatement so that the output becomes syntactically valid WriteToken(Roles.Semicolon); } NewLine(); EndNode(labelStatement); } public virtual void VisitLockStatement(LockStatement lockStatement) { StartNode(lockStatement); WriteKeyword(LockStatement.LockKeywordRole); Space(policy.SpaceBeforeLockParentheses); LPar(); Space(policy.SpacesWithinLockParentheses); lockStatement.Expression.AcceptVisitor(this); Space(policy.SpacesWithinLockParentheses); RPar(); WriteEmbeddedStatement(lockStatement.EmbeddedStatement); EndNode(lockStatement); } public virtual void VisitReturnStatement(ReturnStatement returnStatement) { StartNode(returnStatement); WriteKeyword(ReturnStatement.ReturnKeywordRole); if (!returnStatement.Expression.IsNull) { Space(); returnStatement.Expression.AcceptVisitor(this); } Semicolon(); EndNode(returnStatement); } public virtual void VisitSwitchStatement(SwitchStatement switchStatement) { StartNode(switchStatement); WriteKeyword(SwitchStatement.SwitchKeywordRole); Space(policy.SpaceBeforeSwitchParentheses); LPar(); Space(policy.SpacesWithinSwitchParentheses); switchStatement.Expression.AcceptVisitor(this); Space(policy.SpacesWithinSwitchParentheses); RPar(); OpenBrace(policy.StatementBraceStyle); if (!policy.IndentSwitchBody) { writer.Unindent(); } foreach (var section in switchStatement.SwitchSections) { section.AcceptVisitor(this); } if (!policy.IndentSwitchBody) { writer.Indent(); } CloseBrace(policy.StatementBraceStyle); NewLine(); EndNode(switchStatement); } public virtual void VisitSwitchSection(SwitchSection switchSection) { StartNode(switchSection); bool first = true; foreach (var label in switchSection.CaseLabels) { if (!first) { NewLine(); } label.AcceptVisitor(this); first = false; } bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement; if (policy.IndentCaseBody && !isBlock) { writer.Indent(); } if (!isBlock) NewLine(); foreach (var statement in switchSection.Statements) { statement.AcceptVisitor(this); } if (policy.IndentCaseBody && !isBlock) { writer.Unindent(); } EndNode(switchSection); } public virtual void VisitCaseLabel(CaseLabel caseLabel) { StartNode(caseLabel); if (caseLabel.Expression.IsNull) { WriteKeyword(CaseLabel.DefaultKeywordRole); } else { WriteKeyword(CaseLabel.CaseKeywordRole); Space(); caseLabel.Expression.AcceptVisitor(this); } WriteToken(Roles.Colon); EndNode(caseLabel); } public virtual void VisitSwitchExpression(SwitchExpression switchExpression) { StartNode(switchExpression); switchExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(SwitchExpression.SwitchKeywordRole); OpenBrace(policy.ArrayInitializerBraceStyle); foreach (AstNode node in switchExpression.SwitchSections) { node.AcceptVisitor(this); Comma(node); NewLine(); } CloseBrace(policy.ArrayInitializerBraceStyle); EndNode(switchExpression); } public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection) { StartNode(switchExpressionSection); switchExpressionSection.Pattern.AcceptVisitor(this); Space(); WriteToken(Roles.Arrow); Space(); switchExpressionSection.Body.AcceptVisitor(this); EndNode(switchExpressionSection); } public virtual void VisitThrowStatement(ThrowStatement throwStatement) { StartNode(throwStatement); WriteKeyword(ThrowStatement.ThrowKeywordRole); if (!throwStatement.Expression.IsNull) { Space(); throwStatement.Expression.AcceptVisitor(this); } Semicolon(); EndNode(throwStatement); } public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { StartNode(tryCatchStatement); WriteKeyword(TryCatchStatement.TryKeywordRole); WriteBlock(tryCatchStatement.TryBlock, policy.StatementBraceStyle); foreach (var catchClause in tryCatchStatement.CatchClauses) { if (policy.CatchNewLinePlacement == NewLinePlacement.SameLine) Space(); else NewLine(); catchClause.AcceptVisitor(this); } if (!tryCatchStatement.FinallyBlock.IsNull) { if (policy.FinallyNewLinePlacement == NewLinePlacement.SameLine) Space(); else NewLine(); WriteKeyword(TryCatchStatement.FinallyKeywordRole); WriteBlock(tryCatchStatement.FinallyBlock, policy.StatementBraceStyle); } NewLine(); EndNode(tryCatchStatement); } public virtual void VisitCatchClause(CatchClause catchClause) { StartNode(catchClause); WriteKeyword(CatchClause.CatchKeywordRole); if (!catchClause.Type.IsNull) { Space(policy.SpaceBeforeCatchParentheses); LPar(); Space(policy.SpacesWithinCatchParentheses); catchClause.Type.AcceptVisitor(this); if (!string.IsNullOrEmpty(catchClause.VariableName)) { Space(); WriteIdentifier(catchClause.VariableNameToken); } Space(policy.SpacesWithinCatchParentheses); RPar(); } if (!catchClause.Condition.IsNull) { Space(); WriteKeyword(CatchClause.WhenKeywordRole); Space(policy.SpaceBeforeIfParentheses); WriteToken(CatchClause.CondLPar); Space(policy.SpacesWithinIfParentheses); catchClause.Condition.AcceptVisitor(this); Space(policy.SpacesWithinIfParentheses); WriteToken(CatchClause.CondRPar); } WriteBlock(catchClause.Body, policy.StatementBraceStyle); EndNode(catchClause); } public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { StartNode(uncheckedStatement); WriteKeyword(UncheckedStatement.UncheckedKeywordRole); uncheckedStatement.Body.AcceptVisitor(this); EndNode(uncheckedStatement); } public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement) { StartNode(unsafeStatement); WriteKeyword(UnsafeStatement.UnsafeKeywordRole); unsafeStatement.Body.AcceptVisitor(this); EndNode(unsafeStatement); } public virtual void VisitUsingStatement(UsingStatement usingStatement) { StartNode(usingStatement); if (usingStatement.IsAsync) { WriteKeyword(UsingStatement.AwaitRole); } WriteKeyword(UsingStatement.UsingKeywordRole); if (usingStatement.IsEnhanced) { Space(); } else { Space(policy.SpaceBeforeUsingParentheses); LPar(); Space(policy.SpacesWithinUsingParentheses); } usingStatement.ResourceAcquisition.AcceptVisitor(this); if (usingStatement.IsEnhanced) { Semicolon(); } else { Space(policy.SpacesWithinUsingParentheses); RPar(); } if (usingStatement.IsEnhanced) { if (usingStatement.EmbeddedStatement is BlockStatement blockStatement) { StartNode(blockStatement); foreach (var node in blockStatement.Statements) { node.AcceptVisitor(this); } EndNode(blockStatement); } else { usingStatement.EmbeddedStatement.AcceptVisitor(this); } } else { WriteEmbeddedStatement(usingStatement.EmbeddedStatement); } EndNode(usingStatement); } public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { StartNode(variableDeclarationStatement); WriteModifiers(variableDeclarationStatement.GetChildrenByRole(VariableDeclarationStatement.ModifierRole)); variableDeclarationStatement.Type.AcceptVisitor(this); Space(); WriteCommaSeparatedList(variableDeclarationStatement.Variables); Semicolon(); EndNode(variableDeclarationStatement); } public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) { StartNode(localFunctionDeclarationStatement); localFunctionDeclarationStatement.Declaration.AcceptVisitor(this); EndNode(localFunctionDeclarationStatement); } public virtual void VisitWhileStatement(WhileStatement whileStatement) { StartNode(whileStatement); WriteKeyword(WhileStatement.WhileKeywordRole); Space(policy.SpaceBeforeWhileParentheses); LPar(); Space(policy.SpacesWithinWhileParentheses); whileStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinWhileParentheses); RPar(); WriteEmbeddedStatement(whileStatement.EmbeddedStatement); EndNode(whileStatement); } public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { StartNode(yieldBreakStatement); WriteKeyword(YieldBreakStatement.YieldKeywordRole); WriteKeyword(YieldBreakStatement.BreakKeywordRole); Semicolon(); EndNode(yieldBreakStatement); } public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) { StartNode(yieldReturnStatement); WriteKeyword(YieldReturnStatement.YieldKeywordRole); WriteKeyword(YieldReturnStatement.ReturnKeywordRole); Space(); yieldReturnStatement.Expression.AcceptVisitor(this); Semicolon(); EndNode(yieldReturnStatement); } #endregion #region TypeMembers public virtual void VisitAccessor(Accessor accessor) { StartNode(accessor); WriteAttributes(accessor.Attributes); WriteModifiers(accessor.ModifierTokens); BraceStyle style = policy.StatementBraceStyle; if (accessor.Role == PropertyDeclaration.GetterRole) { WriteKeyword("get", PropertyDeclaration.GetKeywordRole); style = policy.PropertyGetBraceStyle; } else if (accessor.Role == PropertyDeclaration.SetterRole) { if (accessor.Keyword.Role == PropertyDeclaration.InitKeywordRole) { WriteKeyword("init", PropertyDeclaration.InitKeywordRole); } else { WriteKeyword("set", PropertyDeclaration.SetKeywordRole); } style = policy.PropertySetBraceStyle; } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); style = policy.EventAddBraceStyle; } else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) { WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); style = policy.EventRemoveBraceStyle; } WriteMethodBody(accessor.Body, style); EndNode(accessor); } public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { StartNode(constructorDeclaration); WriteAttributes(constructorDeclaration.Attributes); WriteModifiers(constructorDeclaration.ModifierTokens); TypeDeclaration type = constructorDeclaration.Parent as TypeDeclaration; if (type != null && type.Name != constructorDeclaration.Name) WriteIdentifier((Identifier)type.NameToken.Clone()); else WriteIdentifier(constructorDeclaration.NameToken); Space(policy.SpaceBeforeConstructorDeclarationParentheses); WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); if (!constructorDeclaration.Initializer.IsNull) { NewLine(); writer.Indent(); constructorDeclaration.Initializer.AcceptVisitor(this); writer.Unindent(); } WriteMethodBody(constructorDeclaration.Body, policy.ConstructorBraceStyle); EndNode(constructorDeclaration); } public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) { StartNode(constructorInitializer); WriteToken(Roles.Colon); Space(); if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.This) { WriteKeyword(ConstructorInitializer.ThisKeywordRole); } else { WriteKeyword(ConstructorInitializer.BaseKeywordRole); } Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); EndNode(constructorInitializer); } public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { StartNode(destructorDeclaration); WriteAttributes(destructorDeclaration.Attributes); WriteModifiers(destructorDeclaration.ModifierTokens); if (destructorDeclaration.ModifierTokens.Any()) { Space(); } WriteToken(DestructorDeclaration.TildeRole); TypeDeclaration type = destructorDeclaration.Parent as TypeDeclaration; if (type != null && type.Name != destructorDeclaration.Name) WriteIdentifier((Identifier)type.NameToken.Clone()); else WriteIdentifier(destructorDeclaration.NameToken); Space(policy.SpaceBeforeConstructorDeclarationParentheses); LPar(); RPar(); WriteMethodBody(destructorDeclaration.Body, policy.DestructorBraceStyle); EndNode(destructorDeclaration); } public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { StartNode(enumMemberDeclaration); WriteAttributes(enumMemberDeclaration.Attributes); WriteModifiers(enumMemberDeclaration.ModifierTokens); WriteIdentifier(enumMemberDeclaration.NameToken); if (!enumMemberDeclaration.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); enumMemberDeclaration.Initializer.AcceptVisitor(this); } EndNode(enumMemberDeclaration); } public virtual void VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration) { StartNode(extensionDeclaration); WriteAttributes(extensionDeclaration.Attributes); WriteModifiers(extensionDeclaration.ModifierTokens); WriteKeyword(ExtensionDeclaration.ExtensionKeywordRole); WriteTypeParameters(extensionDeclaration.TypeParameters); Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(extensionDeclaration.ReceiverParameters, policy.SpaceWithinMethodDeclarationParentheses); foreach (Constraint constraint in extensionDeclaration.Constraints) { constraint.AcceptVisitor(this); } OpenBrace(policy.ClassBraceStyle); bool first = true; foreach (var member in extensionDeclaration.Members) { if (!first) { for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) NewLine(); } first = false; member.AcceptVisitor(this); } CloseBrace(policy.ClassBraceStyle); NewLine(); EndNode(extensionDeclaration); } public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration) { StartNode(eventDeclaration); WriteAttributes(eventDeclaration.Attributes); WriteModifiers(eventDeclaration.ModifierTokens); WriteKeyword(EventDeclaration.EventKeywordRole); eventDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(eventDeclaration.Variables); Semicolon(); EndNode(eventDeclaration); } public virtual void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) { StartNode(customEventDeclaration); WriteAttributes(customEventDeclaration.Attributes); WriteModifiers(customEventDeclaration.ModifierTokens); WriteKeyword(CustomEventDeclaration.EventKeywordRole); customEventDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); WriteIdentifier(customEventDeclaration.NameToken); OpenBrace(policy.EventBraceStyle); // output add/remove in their original order foreach (AstNode node in customEventDeclaration.Children) { if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) { node.AcceptVisitor(this); } } CloseBrace(policy.EventBraceStyle); NewLine(); EndNode(customEventDeclaration); } public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { StartNode(fieldDeclaration); WriteAttributes(fieldDeclaration.Attributes); WriteModifiers(fieldDeclaration.ModifierTokens); fieldDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fieldDeclaration.Variables); Semicolon(); EndNode(fieldDeclaration); } public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { StartNode(fixedFieldDeclaration); WriteAttributes(fixedFieldDeclaration.Attributes); WriteModifiers(fixedFieldDeclaration.ModifierTokens); WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); Space(); fixedFieldDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fixedFieldDeclaration.Variables); Semicolon(); EndNode(fixedFieldDeclaration); } public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { StartNode(fixedVariableInitializer); WriteIdentifier(fixedVariableInitializer.NameToken); if (!fixedVariableInitializer.CountExpression.IsNull) { WriteToken(Roles.LBracket); Space(policy.SpacesWithinBrackets); fixedVariableInitializer.CountExpression.AcceptVisitor(this); Space(policy.SpacesWithinBrackets); WriteToken(Roles.RBracket); } EndNode(fixedVariableInitializer); } public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { StartNode(indexerDeclaration); WriteAttributes(indexerDeclaration.Attributes); WriteModifiers(indexerDeclaration.ModifierTokens); indexerDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WriteKeyword(IndexerDeclaration.ThisKeywordRole); Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); if (indexerDeclaration.ExpressionBody.IsNull) { bool isSingleLine = (policy.AutoPropertyFormatting == PropertyFormatting.SingleLine) && (indexerDeclaration.Getter.IsNull || indexerDeclaration.Getter.Body.IsNull) && (indexerDeclaration.Setter.IsNull || indexerDeclaration.Setter.Body.IsNull) && !indexerDeclaration.Getter.Attributes.Any() && !indexerDeclaration.Setter.Attributes.Any(); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); // output get/set in their original order foreach (AstNode node in indexerDeclaration.Children) { if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { node.AcceptVisitor(this); } } CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); NewLine(); } else { Space(); WriteToken(Roles.Arrow); Space(); indexerDeclaration.ExpressionBody.AcceptVisitor(this); Semicolon(); } EndNode(indexerDeclaration); } public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { StartNode(methodDeclaration); WriteAttributes(methodDeclaration.Attributes); WriteModifiers(methodDeclaration.ModifierTokens); methodDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); WriteIdentifier(methodDeclaration.NameToken); WriteTypeParameters(methodDeclaration.TypeParameters); Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); foreach (Constraint constraint in methodDeclaration.Constraints) { constraint.AcceptVisitor(this); } WriteMethodBody(methodDeclaration.Body, policy.MethodBraceStyle); EndNode(methodDeclaration); } public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { StartNode(operatorDeclaration); WriteAttributes(operatorDeclaration.Attributes); WriteModifiers(operatorDeclaration.ModifierTokens); if (operatorDeclaration.OperatorType == OperatorType.Explicit || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit) { WriteKeyword(OperatorDeclaration.ExplicitRole); } else if (operatorDeclaration.OperatorType == OperatorType.Implicit) { WriteKeyword(OperatorDeclaration.ImplicitRole); } else { operatorDeclaration.ReturnType.AcceptVisitor(this); } Space(); WritePrivateImplementationType(operatorDeclaration.PrivateImplementationType); WriteKeyword(OperatorDeclaration.OperatorKeywordRole); Space(); if (OperatorDeclaration.IsChecked(operatorDeclaration.OperatorType)) { WriteKeyword(OperatorDeclaration.CheckedKeywordRole); Space(); } if (operatorDeclaration.OperatorType == OperatorType.Explicit || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit || operatorDeclaration.OperatorType == OperatorType.Implicit) { operatorDeclaration.ReturnType.AcceptVisitor(this); } else { WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); } Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); WriteMethodBody(operatorDeclaration.Body, policy.MethodBraceStyle); EndNode(operatorDeclaration); } public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { StartNode(parameterDeclaration); WriteAttributes(parameterDeclaration.Attributes); if (parameterDeclaration.HasThisModifier) { WriteKeyword(ParameterDeclaration.ThisModifierRole); Space(); } if (parameterDeclaration.IsParams) { WriteKeyword(ParameterDeclaration.ParamsModifierRole); Space(); } if (parameterDeclaration.IsScopedRef) { WriteKeyword(ParameterDeclaration.ScopedRefRole); Space(); } switch (parameterDeclaration.ParameterModifier) { case ReferenceKind.Ref: WriteKeyword(ParameterDeclaration.RefModifierRole); Space(); break; case ReferenceKind.RefReadOnly: WriteKeyword(ParameterDeclaration.RefModifierRole); WriteKeyword(ParameterDeclaration.ReadonlyModifierRole); Space(); break; case ReferenceKind.Out: WriteKeyword(ParameterDeclaration.OutModifierRole); Space(); break; case ReferenceKind.In: WriteKeyword(ParameterDeclaration.InModifierRole); Space(); break; } parameterDeclaration.Type.AcceptVisitor(this); if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { Space(); } if (!string.IsNullOrEmpty(parameterDeclaration.Name)) { WriteIdentifier(parameterDeclaration.NameToken); } if (!parameterDeclaration.DefaultExpression.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); parameterDeclaration.DefaultExpression.AcceptVisitor(this); } EndNode(parameterDeclaration); } public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { StartNode(propertyDeclaration); WriteAttributes(propertyDeclaration.Attributes); WriteModifiers(propertyDeclaration.ModifierTokens); propertyDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WriteIdentifier(propertyDeclaration.NameToken); if (propertyDeclaration.ExpressionBody.IsNull) { bool isSingleLine = (policy.AutoPropertyFormatting == PropertyFormatting.SingleLine) && (propertyDeclaration.Getter.IsNull || propertyDeclaration.Getter.Body.IsNull) && (propertyDeclaration.Setter.IsNull || propertyDeclaration.Setter.Body.IsNull) && !propertyDeclaration.Getter.Attributes.Any() && !propertyDeclaration.Setter.Attributes.Any(); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); // output get/set in their original order foreach (AstNode node in propertyDeclaration.Children) { if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { node.AcceptVisitor(this); } } CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); if (!propertyDeclaration.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); propertyDeclaration.Initializer.AcceptVisitor(this); Semicolon(); } else { // The call to Semicolon() above prints a newline too NewLine(); } } else { Space(); WriteToken(Roles.Arrow); Space(); propertyDeclaration.ExpressionBody.AcceptVisitor(this); Semicolon(); } EndNode(propertyDeclaration); } #endregion #region Other nodes public virtual void VisitVariableInitializer(VariableInitializer variableInitializer) { StartNode(variableInitializer); WriteIdentifier(variableInitializer.NameToken); if (!variableInitializer.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); variableInitializer.Initializer.AcceptVisitor(this); } EndNode(variableInitializer); } void MaybeNewLinesAfterUsings(AstNode node) { var nextSibling = node.NextSibling; if ((node is UsingDeclaration || node is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { for (int i = 0; i < policy.MinimumBlankLinesAfterUsings; i++) NewLine(); } } public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) { // don't do node tracking as we visit all children directly foreach (AstNode node in syntaxTree.Children) { node.AcceptVisitor(this); MaybeNewLinesAfterUsings(node); } } public virtual void VisitSimpleType(SimpleType simpleType) { StartNode(simpleType); WriteIdentifier(simpleType.IdentifierToken); WriteTypeArguments(simpleType.TypeArguments); EndNode(simpleType); } public virtual void VisitMemberType(MemberType memberType) { StartNode(memberType); memberType.Target.AcceptVisitor(this); if (memberType.IsDoubleColon) { WriteToken(Roles.DoubleColon); } else { WriteToken(Roles.Dot); } WriteIdentifier(memberType.MemberNameToken); WriteTypeArguments(memberType.TypeArguments); EndNode(memberType); } public virtual void VisitTupleType(TupleAstType tupleType) { Debug.Assert(tupleType.Elements.Count >= 2); StartNode(tupleType); LPar(); WriteCommaSeparatedList(tupleType.Elements); RPar(); EndNode(tupleType); } public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement) { StartNode(tupleTypeElement); tupleTypeElement.Type.AcceptVisitor(this); if (!tupleTypeElement.NameToken.IsNull) { Space(); tupleTypeElement.NameToken.AcceptVisitor(this); } EndNode(tupleTypeElement); } public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType) { StartNode(functionPointerType); WriteKeyword(Roles.DelegateKeyword); WriteToken(FunctionPointerAstType.PointerRole); if (functionPointerType.HasUnmanagedCallingConvention) { Space(); WriteKeyword("unmanaged"); } if (functionPointerType.CallingConventions.Any()) { WriteToken(Roles.LBracket); WriteCommaSeparatedList(functionPointerType.CallingConventions); WriteToken(Roles.RBracket); } WriteToken(Roles.LChevron); WriteCommaSeparatedList( functionPointerType.Parameters.Concat(new[] { functionPointerType.ReturnType })); WriteToken(Roles.RChevron); EndNode(functionPointerType); } public virtual void VisitInvocationType(InvocationAstType invocationType) { StartNode(invocationType); invocationType.BaseType.AcceptVisitor(this); WriteToken(Roles.LPar); WriteCommaSeparatedList(invocationType.Arguments); WriteToken(Roles.RPar); EndNode(invocationType); } public virtual void VisitComposedType(ComposedType composedType) { StartNode(composedType); if (composedType.Attributes.Any()) { foreach (var attr in composedType.Attributes) { attr.AcceptVisitor(this); } } if (composedType.HasRefSpecifier) { WriteKeyword(ComposedType.RefRole); } if (composedType.HasReadOnlySpecifier) { WriteKeyword(ComposedType.ReadonlyRole); } composedType.BaseType.AcceptVisitor(this); if (composedType.HasNullableSpecifier) { WriteToken(ComposedType.NullableRole); } for (int i = 0; i < composedType.PointerRank; i++) { WriteToken(ComposedType.PointerRole); } foreach (var node in composedType.ArraySpecifiers) { node.AcceptVisitor(this); } EndNode(composedType); } public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier) { StartNode(arraySpecifier); WriteToken(Roles.LBracket); foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) { writer.WriteToken(Roles.Comma, ","); } WriteToken(Roles.RBracket); EndNode(arraySpecifier); } public virtual void VisitPrimitiveType(PrimitiveType primitiveType) { StartNode(primitiveType); writer.WritePrimitiveType(primitiveType.Keyword); isAfterSpace = false; EndNode(primitiveType); } public virtual void VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation) { StartNode(singleVariableDesignation); WriteIdentifier(singleVariableDesignation.IdentifierToken); EndNode(singleVariableDesignation); } public virtual void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation) { StartNode(parenthesizedVariableDesignation); LPar(); WriteCommaSeparatedList(parenthesizedVariableDesignation.VariableDesignations); RPar(); EndNode(parenthesizedVariableDesignation); } public virtual void VisitComment(Comment comment) { writer.StartNode(comment); writer.WriteComment(comment.CommentType, comment.Content); writer.EndNode(comment); } public virtual void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) { writer.StartNode(preProcessorDirective); writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); writer.EndNode(preProcessorDirective); } public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) { StartNode(typeParameterDeclaration); WriteAttributes(typeParameterDeclaration.Attributes); switch (typeParameterDeclaration.Variance) { case VarianceModifier.Invariant: break; case VarianceModifier.Covariant: WriteKeyword(TypeParameterDeclaration.OutVarianceKeywordRole); break; case VarianceModifier.Contravariant: WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); break; default: throw new NotSupportedException("Invalid value for VarianceModifier"); } WriteIdentifier(typeParameterDeclaration.NameToken); EndNode(typeParameterDeclaration); } public virtual void VisitConstraint(Constraint constraint) { StartNode(constraint); Space(); WriteKeyword(Roles.WhereKeyword); constraint.TypeParameter.AcceptVisitor(this); Space(); WriteToken(Roles.Colon); Space(); WriteCommaSeparatedList(constraint.BaseTypes); EndNode(constraint); } public virtual void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) { CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; if (mod != null) { // ITokenWriter assumes that each node processed between a // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); } else { throw new NotSupportedException("Should never visit individual tokens"); } } public virtual void VisitIdentifier(Identifier identifier) { // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. // ITokenWriter assumes that each node processed between a // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. WriteIdentifier(identifier); } void IAstVisitor.VisitNullNode(AstNode nullNode) { } void IAstVisitor.VisitErrorNode(AstNode errorNode) { StartNode(errorNode); EndNode(errorNode); } #endregion #region Pattern Nodes public virtual void VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) { StartNode(placeholder); VisitNodeInPattern(pattern); EndNode(placeholder); } void VisitAnyNode(AnyNode anyNode) { if (!string.IsNullOrEmpty(anyNode.GroupName)) { WriteIdentifier(anyNode.GroupName); WriteToken(Roles.Colon); } } void VisitBackreference(Backreference backreference) { WriteKeyword("backreference"); LPar(); WriteIdentifier(backreference.ReferencedGroupName); RPar(); } void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) { WriteKeyword("identifierBackreference"); LPar(); WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); RPar(); } void VisitChoice(Choice choice) { WriteKeyword("choice"); Space(); LPar(); NewLine(); writer.Indent(); foreach (INode alternative in choice) { VisitNodeInPattern(alternative); if (alternative != choice.Last()) { WriteToken(Roles.Comma); } NewLine(); } writer.Unindent(); RPar(); } void VisitNamedNode(NamedNode namedNode) { if (!string.IsNullOrEmpty(namedNode.GroupName)) { WriteIdentifier(namedNode.GroupName); WriteToken(Roles.Colon); } VisitNodeInPattern(namedNode.ChildNode); } void VisitRepeat(Repeat repeat) { WriteKeyword("repeat"); LPar(); if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { WriteIdentifier(repeat.MinCount.ToString()); WriteToken(Roles.Comma); WriteIdentifier(repeat.MaxCount.ToString()); WriteToken(Roles.Comma); } VisitNodeInPattern(repeat.ChildNode); RPar(); } void VisitOptionalNode(OptionalNode optionalNode) { WriteKeyword("optional"); LPar(); VisitNodeInPattern(optionalNode.ChildNode); RPar(); } void VisitNodeInPattern(INode childNode) { if (childNode is AstNode) { ((AstNode)childNode).AcceptVisitor(this); } else if (childNode is IdentifierExpressionBackreference) { VisitIdentifierExpressionBackreference((IdentifierExpressionBackreference)childNode); } else if (childNode is Choice) { VisitChoice((Choice)childNode); } else if (childNode is AnyNode) { VisitAnyNode((AnyNode)childNode); } else if (childNode is Backreference) { VisitBackreference((Backreference)childNode); } else if (childNode is NamedNode) { VisitNamedNode((NamedNode)childNode); } else if (childNode is OptionalNode) { VisitOptionalNode((OptionalNode)childNode); } else if (childNode is Repeat) { VisitRepeat((Repeat)childNode); } else { writer.WritePrimitiveValue(childNode); } } #endregion #region Documentation Reference public virtual void VisitDocumentationReference(DocumentationReference documentationReference) { StartNode(documentationReference); if (!documentationReference.DeclaringType.IsNull) { documentationReference.DeclaringType.AcceptVisitor(this); if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) { WriteToken(Roles.Dot); } } switch (documentationReference.SymbolKind) { case SymbolKind.TypeDefinition: // we already printed the DeclaringType break; case SymbolKind.Indexer: WriteKeyword(IndexerDeclaration.ThisKeywordRole); break; case SymbolKind.Operator: var opType = documentationReference.OperatorType; if (opType == OperatorType.Explicit || opType == OperatorType.CheckedExplicit) { WriteKeyword(OperatorDeclaration.ExplicitRole); } else if (opType == OperatorType.Implicit) { WriteKeyword(OperatorDeclaration.ImplicitRole); } WriteKeyword(OperatorDeclaration.OperatorKeywordRole); Space(); if (OperatorDeclaration.IsChecked(opType)) { WriteKeyword(OperatorDeclaration.CheckedKeywordRole); Space(); } if (opType == OperatorType.Explicit || opType == OperatorType.Implicit || opType == OperatorType.CheckedExplicit) { documentationReference.ConversionOperatorReturnType.AcceptVisitor(this); } else { WriteToken(OperatorDeclaration.GetToken(opType), OperatorDeclaration.GetRole(opType)); } break; default: WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); break; } WriteTypeArguments(documentationReference.TypeArguments); if (documentationReference.HasParameterList) { Space(policy.SpaceBeforeMethodDeclarationParentheses); if (documentationReference.SymbolKind == SymbolKind.Indexer) { WriteCommaSeparatedListInBrackets(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } else { WriteCommaSeparatedListInParenthesis(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } } EndNode(documentationReference); } #endregion /// /// Converts special characters to escape sequences within the given string. /// public static string ConvertString(string text) { return TextWriterTokenWriter.ConvertString(text); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs ================================================ // // FormattingOptionsFactory.cs // // Author: // Mike Krüger // // Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// The formatting options factory creates pre defined formatting option styles. /// public static class FormattingOptionsFactory { /// /// Creates empty CSharpFormatting options. /// public static CSharpFormattingOptions CreateEmpty() { return new CSharpFormattingOptions(); } /// /// Creates mono indent style CSharpFormatting options. /// public static CSharpFormattingOptions CreateMono() { return new CSharpFormattingOptions { IndentNamespaceBody = true, IndentClassBody = true, IndentInterfaceBody = true, IndentStructBody = true, IndentEnumBody = true, IndentMethodBody = true, IndentPropertyBody = true, IndentEventBody = true, IndentBlocks = true, IndentSwitchBody = false, IndentCaseBody = true, IndentBreakStatements = true, IndentPreprocessorDirectives = true, IndentBlocksInsideExpressions = false, NamespaceBraceStyle = BraceStyle.NextLine, ClassBraceStyle = BraceStyle.NextLine, InterfaceBraceStyle = BraceStyle.NextLine, StructBraceStyle = BraceStyle.NextLine, EnumBraceStyle = BraceStyle.NextLine, MethodBraceStyle = BraceStyle.NextLine, ConstructorBraceStyle = BraceStyle.NextLine, DestructorBraceStyle = BraceStyle.NextLine, AnonymousMethodBraceStyle = BraceStyle.EndOfLine, PropertyBraceStyle = BraceStyle.EndOfLine, PropertyGetBraceStyle = BraceStyle.EndOfLine, PropertySetBraceStyle = BraceStyle.EndOfLine, SimpleGetBlockFormatting = PropertyFormatting.SingleLine, SimpleSetBlockFormatting = PropertyFormatting.SingleLine, EventBraceStyle = BraceStyle.EndOfLine, EventAddBraceStyle = BraceStyle.EndOfLine, EventRemoveBraceStyle = BraceStyle.EndOfLine, AllowEventAddBlockInline = true, AllowEventRemoveBlockInline = true, StatementBraceStyle = BraceStyle.EndOfLine, ElseNewLinePlacement = NewLinePlacement.SameLine, ElseIfNewLinePlacement = NewLinePlacement.SameLine, CatchNewLinePlacement = NewLinePlacement.SameLine, FinallyNewLinePlacement = NewLinePlacement.SameLine, WhileNewLinePlacement = NewLinePlacement.SameLine, ArrayInitializerWrapping = Wrapping.WrapIfTooLong, ArrayInitializerBraceStyle = BraceStyle.EndOfLine, AllowOneLinedArrayInitialziers = true, SpaceBeforeMethodCallParentheses = true, SpaceBeforeMethodDeclarationParentheses = true, SpaceBeforeConstructorDeclarationParentheses = true, SpaceBeforeDelegateDeclarationParentheses = true, SpaceAfterMethodCallParameterComma = true, SpaceAfterConstructorDeclarationParameterComma = true, SpaceBeforeNewParentheses = true, SpacesWithinNewParentheses = false, SpacesBetweenEmptyNewParentheses = false, SpaceBeforeNewParameterComma = false, SpaceAfterNewParameterComma = true, SpaceBeforeIfParentheses = true, SpaceBeforeWhileParentheses = true, SpaceBeforeForParentheses = true, SpaceBeforeForeachParentheses = true, SpaceBeforeCatchParentheses = true, SpaceBeforeSwitchParentheses = true, SpaceBeforeLockParentheses = true, SpaceBeforeUsingParentheses = true, SpaceAroundAssignment = true, SpaceAroundLogicalOperator = true, SpaceAroundEqualityOperator = true, SpaceAroundRelationalOperator = true, SpaceAroundBitwiseOperator = true, SpaceAroundAdditiveOperator = true, SpaceAroundMultiplicativeOperator = true, SpaceAroundShiftOperator = true, SpaceAroundNullCoalescingOperator = true, SpacesWithinParentheses = false, SpaceWithinMethodCallParentheses = false, SpaceWithinMethodDeclarationParentheses = false, SpacesWithinIfParentheses = false, SpacesWithinWhileParentheses = false, SpacesWithinForParentheses = false, SpacesWithinForeachParentheses = false, SpacesWithinCatchParentheses = false, SpacesWithinSwitchParentheses = false, SpacesWithinLockParentheses = false, SpacesWithinUsingParentheses = false, SpacesWithinCastParentheses = false, SpacesWithinSizeOfParentheses = false, SpacesWithinTypeOfParentheses = false, SpacesWithinCheckedExpressionParantheses = false, SpaceBeforeConditionalOperatorCondition = true, SpaceAfterConditionalOperatorCondition = true, SpaceBeforeConditionalOperatorSeparator = true, SpaceAfterConditionalOperatorSeparator = true, SpacesWithinBrackets = false, SpacesBeforeBrackets = true, SpaceBeforeBracketComma = false, SpaceAfterBracketComma = true, SpaceBeforeForSemicolon = false, SpaceAfterForSemicolon = true, SpaceAfterTypecast = false, AlignEmbeddedStatements = true, SimplePropertyFormatting = PropertyFormatting.SingleLine, AutoPropertyFormatting = PropertyFormatting.SingleLine, EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, SpaceBeforeMethodDeclarationParameterComma = false, SpaceAfterMethodDeclarationParameterComma = true, SpaceAfterDelegateDeclarationParameterComma = true, SpaceBeforeFieldDeclarationComma = false, SpaceAfterFieldDeclarationComma = true, SpaceBeforeLocalVariableDeclarationComma = false, SpaceAfterLocalVariableDeclarationComma = true, SpaceBeforeIndexerDeclarationBracket = true, SpaceWithinIndexerDeclarationBracket = false, SpaceBeforeIndexerDeclarationParameterComma = false, SpaceInNamedArgumentAfterDoubleColon = true, RemoveEndOfLineWhiteSpace = true, SpaceAfterIndexerDeclarationParameterComma = true, MinimumBlankLinesBeforeUsings = 0, MinimumBlankLinesAfterUsings = 1, UsingPlacement = UsingPlacement.TopOfFile, MinimumBlankLinesBeforeFirstDeclaration = 0, MinimumBlankLinesBetweenTypes = 1, MinimumBlankLinesBetweenFields = 0, MinimumBlankLinesBetweenEventFields = 0, MinimumBlankLinesBetweenMembers = 1, MinimumBlankLinesAroundRegion = 1, MinimumBlankLinesInsideRegion = 1, AlignToFirstIndexerArgument = false, AlignToFirstIndexerDeclarationParameter = true, AlignToFirstMethodCallArgument = false, AlignToFirstMethodDeclarationParameter = true, KeepCommentsAtFirstColumn = true, ChainedMethodCallWrapping = Wrapping.DoNotWrap, MethodCallArgumentWrapping = Wrapping.DoNotWrap, NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, IndexerArgumentWrapping = Wrapping.DoNotWrap, NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, NewLineBeforeNewQueryClause = NewLinePlacement.NewLine }; } /// /// Creates sharp develop indent style CSharpFormatting options. /// public static CSharpFormattingOptions CreateSharpDevelop() { var baseOptions = CreateKRStyle(); return baseOptions; } /// /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, /// is commonly used in C. It is less common for C++, C#, and others. /// public static CSharpFormattingOptions CreateKRStyle() { return new CSharpFormattingOptions() { IndentNamespaceBody = true, IndentClassBody = true, IndentInterfaceBody = true, IndentStructBody = true, IndentEnumBody = true, IndentMethodBody = true, IndentPropertyBody = true, IndentEventBody = true, IndentBlocks = true, IndentSwitchBody = true, IndentCaseBody = true, IndentBreakStatements = true, IndentPreprocessorDirectives = true, NamespaceBraceStyle = BraceStyle.NextLine, ClassBraceStyle = BraceStyle.NextLine, InterfaceBraceStyle = BraceStyle.NextLine, StructBraceStyle = BraceStyle.NextLine, EnumBraceStyle = BraceStyle.NextLine, MethodBraceStyle = BraceStyle.NextLine, ConstructorBraceStyle = BraceStyle.NextLine, DestructorBraceStyle = BraceStyle.NextLine, AnonymousMethodBraceStyle = BraceStyle.EndOfLine, PropertyBraceStyle = BraceStyle.EndOfLine, PropertyGetBraceStyle = BraceStyle.EndOfLine, PropertySetBraceStyle = BraceStyle.EndOfLine, SimpleGetBlockFormatting = PropertyFormatting.SingleLine, SimpleSetBlockFormatting = PropertyFormatting.SingleLine, EventBraceStyle = BraceStyle.EndOfLine, EventAddBraceStyle = BraceStyle.EndOfLine, EventRemoveBraceStyle = BraceStyle.EndOfLine, AllowEventAddBlockInline = true, AllowEventRemoveBlockInline = true, StatementBraceStyle = BraceStyle.EndOfLine, ElseNewLinePlacement = NewLinePlacement.SameLine, ElseIfNewLinePlacement = NewLinePlacement.SameLine, CatchNewLinePlacement = NewLinePlacement.SameLine, FinallyNewLinePlacement = NewLinePlacement.SameLine, WhileNewLinePlacement = NewLinePlacement.SameLine, ArrayInitializerWrapping = Wrapping.WrapIfTooLong, ArrayInitializerBraceStyle = BraceStyle.EndOfLine, SpaceBeforeMethodCallParentheses = false, SpaceBeforeMethodDeclarationParentheses = false, SpaceBeforeConstructorDeclarationParentheses = false, SpaceBeforeDelegateDeclarationParentheses = false, SpaceBeforeIndexerDeclarationBracket = false, SpaceAfterMethodCallParameterComma = true, SpaceAfterConstructorDeclarationParameterComma = true, NewLineBeforeConstructorInitializerColon = NewLinePlacement.NewLine, NewLineAfterConstructorInitializerColon = NewLinePlacement.SameLine, SpaceBeforeNewParentheses = false, SpacesWithinNewParentheses = false, SpacesBetweenEmptyNewParentheses = false, SpaceBeforeNewParameterComma = false, SpaceAfterNewParameterComma = true, SpaceBeforeIfParentheses = true, SpaceBeforeWhileParentheses = true, SpaceBeforeForParentheses = true, SpaceBeforeForeachParentheses = true, SpaceBeforeCatchParentheses = true, SpaceBeforeSwitchParentheses = true, SpaceBeforeLockParentheses = true, SpaceBeforeUsingParentheses = true, SpaceAroundAssignment = true, SpaceAroundLogicalOperator = true, SpaceAroundEqualityOperator = true, SpaceAroundRelationalOperator = true, SpaceAroundBitwiseOperator = true, SpaceAroundAdditiveOperator = true, SpaceAroundMultiplicativeOperator = true, SpaceAroundShiftOperator = true, SpaceAroundNullCoalescingOperator = true, SpacesWithinParentheses = false, SpaceWithinMethodCallParentheses = false, SpaceWithinMethodDeclarationParentheses = false, SpacesWithinIfParentheses = false, SpacesWithinWhileParentheses = false, SpacesWithinForParentheses = false, SpacesWithinForeachParentheses = false, SpacesWithinCatchParentheses = false, SpacesWithinSwitchParentheses = false, SpacesWithinLockParentheses = false, SpacesWithinUsingParentheses = false, SpacesWithinCastParentheses = false, SpacesWithinSizeOfParentheses = false, SpacesWithinTypeOfParentheses = false, SpacesWithinCheckedExpressionParantheses = false, SpaceBeforeConditionalOperatorCondition = true, SpaceAfterConditionalOperatorCondition = true, SpaceBeforeConditionalOperatorSeparator = true, SpaceAfterConditionalOperatorSeparator = true, SpaceBeforeArrayDeclarationBrackets = false, SpacesWithinBrackets = false, SpacesBeforeBrackets = false, SpaceBeforeBracketComma = false, SpaceAfterBracketComma = true, SpaceBeforeForSemicolon = false, SpaceAfterForSemicolon = true, SpaceAfterTypecast = false, AlignEmbeddedStatements = true, SimplePropertyFormatting = PropertyFormatting.SingleLine, AutoPropertyFormatting = PropertyFormatting.SingleLine, EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, SpaceBeforeMethodDeclarationParameterComma = false, SpaceAfterMethodDeclarationParameterComma = true, SpaceAfterDelegateDeclarationParameterComma = true, SpaceBeforeFieldDeclarationComma = false, SpaceAfterFieldDeclarationComma = true, SpaceBeforeLocalVariableDeclarationComma = false, SpaceAfterLocalVariableDeclarationComma = true, SpaceWithinIndexerDeclarationBracket = false, SpaceBeforeIndexerDeclarationParameterComma = false, SpaceInNamedArgumentAfterDoubleColon = true, SpaceAfterIndexerDeclarationParameterComma = true, RemoveEndOfLineWhiteSpace = true, MinimumBlankLinesBeforeUsings = 0, MinimumBlankLinesAfterUsings = 1, MinimumBlankLinesBeforeFirstDeclaration = 0, MinimumBlankLinesBetweenTypes = 1, MinimumBlankLinesBetweenFields = 0, MinimumBlankLinesBetweenEventFields = 0, MinimumBlankLinesBetweenMembers = 1, MinimumBlankLinesAroundRegion = 1, MinimumBlankLinesInsideRegion = 1, KeepCommentsAtFirstColumn = true, ChainedMethodCallWrapping = Wrapping.DoNotWrap, MethodCallArgumentWrapping = Wrapping.DoNotWrap, NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, IndexerArgumentWrapping = Wrapping.DoNotWrap, NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, NewLineBeforeNewQueryClause = NewLinePlacement.NewLine }; } /// /// Creates allman indent style CSharpFormatting options used in Visual Studio. /// public static CSharpFormattingOptions CreateAllman() { var baseOptions = CreateKRStyle(); baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLine; baseOptions.PropertyBraceStyle = BraceStyle.NextLine; baseOptions.PropertyGetBraceStyle = BraceStyle.NextLine; baseOptions.PropertySetBraceStyle = BraceStyle.NextLine; baseOptions.EventBraceStyle = BraceStyle.NextLine; baseOptions.EventAddBraceStyle = BraceStyle.NextLine; baseOptions.EventRemoveBraceStyle = BraceStyle.NextLine; baseOptions.StatementBraceStyle = BraceStyle.NextLine; baseOptions.ArrayInitializerBraceStyle = BraceStyle.NextLine; baseOptions.CatchNewLinePlacement = NewLinePlacement.NewLine; baseOptions.ElseNewLinePlacement = NewLinePlacement.NewLine; baseOptions.ElseIfNewLinePlacement = NewLinePlacement.SameLine; baseOptions.FinallyNewLinePlacement = NewLinePlacement.NewLine; baseOptions.WhileNewLinePlacement = NewLinePlacement.DoNotCare; baseOptions.ArrayInitializerWrapping = Wrapping.DoNotWrap; baseOptions.IndentBlocksInsideExpressions = true; return baseOptions; } /// /// The Whitesmiths style, also called Wishart style to a lesser extent, is less common today than the previous three. It was originally used in the documentation for the first commercial C compiler, the Whitesmiths Compiler. /// public static CSharpFormattingOptions CreateWhitesmiths() { var baseOptions = CreateKRStyle(); baseOptions.NamespaceBraceStyle = BraceStyle.NextLineShifted; baseOptions.ClassBraceStyle = BraceStyle.NextLineShifted; baseOptions.InterfaceBraceStyle = BraceStyle.NextLineShifted; baseOptions.StructBraceStyle = BraceStyle.NextLineShifted; baseOptions.EnumBraceStyle = BraceStyle.NextLineShifted; baseOptions.MethodBraceStyle = BraceStyle.NextLineShifted; baseOptions.ConstructorBraceStyle = BraceStyle.NextLineShifted; baseOptions.DestructorBraceStyle = BraceStyle.NextLineShifted; baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLineShifted; baseOptions.PropertyBraceStyle = BraceStyle.NextLineShifted; baseOptions.PropertyGetBraceStyle = BraceStyle.NextLineShifted; baseOptions.PropertySetBraceStyle = BraceStyle.NextLineShifted; baseOptions.EventBraceStyle = BraceStyle.NextLineShifted; baseOptions.EventAddBraceStyle = BraceStyle.NextLineShifted; baseOptions.EventRemoveBraceStyle = BraceStyle.NextLineShifted; baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted; baseOptions.IndentBlocksInsideExpressions = true; return baseOptions; } /// /// Like the Allman and Whitesmiths styles, GNU style puts braces on a line by themselves, indented by 2 spaces, /// except when opening a function definition, where they are not indented. /// In either case, the contained code is indented by 2 spaces from the braces. /// Popularised by Richard Stallman, the layout may be influenced by his background of writing Lisp code. /// In Lisp the equivalent to a block (a progn) /// is a first class data entity and giving it its own indent level helps to emphasize that, /// whereas in C a block is just syntax. /// Although not directly related to indentation, GNU coding style also includes a space before the bracketed /// list of arguments to a function. /// public static CSharpFormattingOptions CreateGNU() { var baseOptions = CreateAllman(); baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted2; return baseOptions; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// Used to test for the "F(G<A,B>(7));" grammar ambiguity. /// class GenericGrammarAmbiguityVisitor : DepthFirstAstVisitor { /// /// Resolves ambiguities in the specified syntax tree. /// This method must be called after the InsertParenthesesVisitor, because the ambiguity depends on whether the /// final `>` in the possible-type-argument is followed by an opening parenthesis. /// public static void ResolveAmbiguities(AstNode rootNode) { foreach (var node in rootNode.Descendants.OfType()) { if (CausesAmbiguityWithGenerics(node)) { node.ReplaceWith(n => new ParenthesizedExpression(n)); } } } public static bool CausesAmbiguityWithGenerics(BinaryOperatorExpression binaryOperatorExpression) { if (binaryOperatorExpression.Operator != BinaryOperatorType.LessThan) return false; var v = new GenericGrammarAmbiguityVisitor(); v.genericNestingLevel = 1; for (AstNode node = binaryOperatorExpression.Right; node != null; node = node.GetNextNode()) { if (node.AcceptVisitor(v)) return v.ambiguityFound; } return false; } int genericNestingLevel; bool ambiguityFound; protected override bool VisitChildren(AstNode node) { // unhandled node: probably not syntactically valid in a typename // These are preconditions for all recursive Visit() calls. Debug.Assert(genericNestingLevel > 0); Debug.Assert(!ambiguityFound); // The return value merely indicates whether to stop visiting. return true; // stop visiting, no ambiguity found } public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { if (binaryOperatorExpression.Left.AcceptVisitor(this)) return true; Debug.Assert(genericNestingLevel > 0); switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.LessThan: genericNestingLevel += 1; break; case BinaryOperatorType.GreaterThan: genericNestingLevel--; break; case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2: genericNestingLevel -= 2; break; case BinaryOperatorType.UnsignedShiftRight when genericNestingLevel >= 3: genericNestingLevel -= 3; break; default: return true; // stop visiting, no ambiguity found } if (genericNestingLevel == 0) { // Of the all tokens that might follow `>` and trigger the ambiguity to be resolved in favor of generics, // `(` is the only one that might start an expression. ambiguityFound = binaryOperatorExpression.Right is ParenthesizedExpression; return true; // stop visiting } return binaryOperatorExpression.Right.AcceptVisitor(this); } public override bool VisitIdentifierExpression(IdentifierExpression identifierExpression) { // identifier could also be valid in a type argument return false; // keep visiting } public override bool VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { return false; // keep visiting } public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { // MRE could also be valid in a type argument return memberReferenceExpression.Target.AcceptVisitor(this); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs ================================================ // Copyright (c) 2010-2013 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 // 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.IO; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { public abstract class TokenWriter { public abstract void StartNode(AstNode node); public abstract void EndNode(AstNode node); /// /// Writes an identifier. /// public abstract void WriteIdentifier(Identifier identifier); /// /// Writes a keyword to the output. /// public abstract void WriteKeyword(Role role, string keyword); /// /// Writes a token to the output. /// public abstract void WriteToken(Role role, string token); /// /// Writes a primitive/literal value /// public abstract void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None); public abstract void WritePrimitiveType(string type); /// /// Write a piece of text in an interpolated string literal. /// public abstract void WriteInterpolatedText(string text); public abstract void Space(); public abstract void Indent(); public abstract void Unindent(); public abstract void NewLine(); public abstract void WriteComment(CommentType commentType, string content); public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); public static TokenWriter Create(TextWriter writer, string indentation = "\t") { return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); } public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") { var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target))); } public static TokenWriter InsertRequiredSpaces(TokenWriter writer) { return new InsertRequiredSpacesDecorator(writer); } public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) { if (!(writer is ILocatable)) throw new InvalidOperationException("writer does not provide locations!"); return new InsertMissingTokensDecorator(writer, (ILocatable)writer); } } public interface ILocatable { TextLocation Location { get; } int Length { get; } } public abstract class DecoratingTokenWriter : TokenWriter { readonly TokenWriter decoratedWriter; protected DecoratingTokenWriter(TokenWriter decoratedWriter) { if (decoratedWriter == null) throw new ArgumentNullException(nameof(decoratedWriter)); this.decoratedWriter = decoratedWriter; } public override void StartNode(AstNode node) { decoratedWriter.StartNode(node); } public override void EndNode(AstNode node) { decoratedWriter.EndNode(node); } public override void WriteIdentifier(Identifier identifier) { decoratedWriter.WriteIdentifier(identifier); } public override void WriteKeyword(Role role, string keyword) { decoratedWriter.WriteKeyword(role, keyword); } public override void WriteToken(Role role, string token) { decoratedWriter.WriteToken(role, token); } public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { decoratedWriter.WritePrimitiveValue(value, format); } public override void WritePrimitiveType(string type) { decoratedWriter.WritePrimitiveType(type); } public override void WriteInterpolatedText(string text) { decoratedWriter.WriteInterpolatedText(text); } public override void Space() { decoratedWriter.Space(); } public override void Indent() { decoratedWriter.Indent(); } public override void Unindent() { decoratedWriter.Unindent(); } public override void NewLine() { decoratedWriter.NewLine(); } public override void WriteComment(CommentType commentType, string content) { decoratedWriter.WriteComment(commentType, content); } public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { decoratedWriter.WritePreProcessorDirective(type, argument); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { class InsertMissingTokensDecorator : DecoratingTokenWriter { readonly Stack> nodes = new Stack>(); List currentList; readonly ILocatable locationProvider; public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) : base(writer) { this.locationProvider = locationProvider; currentList = new List(); } public override void StartNode(AstNode node) { // ignore whitespace: these don't need to be processed. // StartNode/EndNode is only called for them to support folding of comments. if (node.NodeType != NodeType.Whitespace) { currentList.Add(node); nodes.Push(currentList); currentList = new List(); } else if (node is Comment comment) { comment.SetStartLocation(locationProvider.Location); } if (node is ErrorExpression error) { error.Location = locationProvider.Location; } base.StartNode(node); } public override void EndNode(AstNode node) { // ignore whitespace: these don't need to be processed. // StartNode/EndNode is only called for them to support folding of comments. if (node.NodeType != NodeType.Whitespace) { System.Diagnostics.Debug.Assert(currentList != null); foreach (var removable in node.Children.Where(n => n is CSharpTokenNode)) { removable.Remove(); } foreach (var child in currentList) { System.Diagnostics.Debug.Assert(child.Parent == null || node == child.Parent); child.Remove(); node.AddChildWithExistingRole(child); } currentList = nodes.Pop(); } else if (node is Comment comment) { comment.SetEndLocation(locationProvider.Location); } base.EndNode(node); } public override void WriteToken(Role role, string token) { switch (nodes.Peek().LastOrDefault()) { case EmptyStatement emptyStatement: emptyStatement.Location = locationProvider.Location; break; case ErrorExpression errorExpression: errorExpression.Location = locationProvider.Location; break; default: CSharpTokenNode t = new CSharpTokenNode(locationProvider.Location, (TokenRole)role); t.Role = role; currentList.Add(t); break; } base.WriteToken(role, token); } public override void WriteKeyword(Role role, string keyword) { TextLocation start = locationProvider.Location; CSharpTokenNode t = null; if (role is TokenRole) t = new CSharpTokenNode(start, (TokenRole)role); else if (role == EntityDeclaration.ModifierRole) t = new CSharpModifierToken(start, CSharpModifierToken.GetModifierValue(keyword)); else if (keyword == "this") { ThisReferenceExpression node = nodes.Peek().LastOrDefault() as ThisReferenceExpression; if (node != null) node.Location = start; } else if (keyword == "base") { BaseReferenceExpression node = nodes.Peek().LastOrDefault() as BaseReferenceExpression; if (node != null) node.Location = start; } if (t != null) { currentList.Add(t); t.Role = role; } base.WriteKeyword(role, keyword); } public override void WriteIdentifier(Identifier identifier) { if (!identifier.IsNull) identifier.SetStartLocation(locationProvider.Location); currentList.Add(identifier); base.WriteIdentifier(identifier); } public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { Expression node = nodes.Peek().LastOrDefault() as Expression; var startLocation = locationProvider.Location; base.WritePrimitiveValue(value, format); if (node is PrimitiveExpression) { ((PrimitiveExpression)node).SetLocation(startLocation, locationProvider.Location); } if (node is NullReferenceExpression) { ((NullReferenceExpression)node).SetStartLocation(startLocation); } } public override void WritePrimitiveType(string type) { PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; if (node != null) node.SetStartLocation(locationProvider.Location); base.WritePrimitiveType(type); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs ================================================ // Copyright (c) 2010-2020 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly. /// For example, if the AST contains /// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST /// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary /// parentheses are inserted: "2 * (1 + 1)". /// public class InsertParenthesesVisitor : DepthFirstAstVisitor { /// /// Gets/Sets whether the visitor should insert parentheses to make the code better looking. /// If this property is false, it will insert parentheses only where strictly required by the language spec. /// public bool InsertParenthesesForReadability { get; set; } enum PrecedenceLevel { // Higher integer value = higher precedence. Assignment, Conditional, // ?: NullCoalescing, // ?? ConditionalOr, // || ConditionalAnd, // && BitwiseOr, // | ExclusiveOr, // binary ^ BitwiseAnd, // binary & Equality, // == != RelationalAndTypeTesting, // < <= > >= is Shift, // << >> Additive, // binary + - Multiplicative, // * / % Switch, // C# 8 switch expression Range, // .. Unary, QueryOrLambda, NullableRewrap, Primary } /// /// Gets the row number in the C# 4.0 spec operator precedence table. /// static PrecedenceLevel GetPrecedence(Expression expr) { // Note: the operator precedence table on MSDN is incorrect if (expr is QueryExpression || expr is LambdaExpression) { // Not part of the table in the C# spec, but we need to ensure that queries within // primary expressions get parenthesized. return PrecedenceLevel.QueryOrLambda; } if (expr is UnaryOperatorExpression uoe) { switch (uoe.Operator) { case UnaryOperatorType.PostDecrement: case UnaryOperatorType.PostIncrement: case UnaryOperatorType.NullConditional: case UnaryOperatorType.SuppressNullableWarning: return PrecedenceLevel.Primary; case UnaryOperatorType.NullConditionalRewrap: return PrecedenceLevel.NullableRewrap; case UnaryOperatorType.IsTrue: return PrecedenceLevel.Conditional; default: return PrecedenceLevel.Unary; } } if (expr is CastExpression) return PrecedenceLevel.Unary; if (expr is PrimitiveExpression primitive) { var value = primitive.Value; if (value is int i && i < 0) return PrecedenceLevel.Unary; if (value is long l && l < 0) return PrecedenceLevel.Unary; if (value is float f && f < 0) return PrecedenceLevel.Unary; if (value is double d && d < 0) return PrecedenceLevel.Unary; if (value is decimal de && de < 0) return PrecedenceLevel.Unary; return PrecedenceLevel.Primary; } if (expr is BinaryOperatorExpression boe) { switch (boe.Operator) { case BinaryOperatorType.Range: return PrecedenceLevel.Range; case BinaryOperatorType.Multiply: case BinaryOperatorType.Divide: case BinaryOperatorType.Modulus: return PrecedenceLevel.Multiplicative; case BinaryOperatorType.Add: case BinaryOperatorType.Subtract: return PrecedenceLevel.Additive; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: case BinaryOperatorType.UnsignedShiftRight: return PrecedenceLevel.Shift; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: case BinaryOperatorType.LessThan: case BinaryOperatorType.LessThanOrEqual: return PrecedenceLevel.RelationalAndTypeTesting; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: return PrecedenceLevel.Equality; case BinaryOperatorType.BitwiseAnd: return PrecedenceLevel.BitwiseAnd; case BinaryOperatorType.ExclusiveOr: return PrecedenceLevel.ExclusiveOr; case BinaryOperatorType.BitwiseOr: return PrecedenceLevel.BitwiseOr; case BinaryOperatorType.ConditionalAnd: return PrecedenceLevel.ConditionalAnd; case BinaryOperatorType.ConditionalOr: return PrecedenceLevel.ConditionalOr; case BinaryOperatorType.NullCoalescing: return PrecedenceLevel.NullCoalescing; case BinaryOperatorType.IsPattern: return PrecedenceLevel.RelationalAndTypeTesting; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); } } if (expr is SwitchExpression) return PrecedenceLevel.Switch; if (expr is IsExpression || expr is AsExpression) return PrecedenceLevel.RelationalAndTypeTesting; if (expr is ConditionalExpression || expr is DirectionExpression) return PrecedenceLevel.Conditional; if (expr is AssignmentExpression) return PrecedenceLevel.Assignment; // anything else: primary expression return PrecedenceLevel.Primary; } /// /// Parenthesizes the expression if it does not have the minimum required precedence. /// static void ParenthesizeIfRequired(Expression expr, PrecedenceLevel minimumPrecedence) { if (GetPrecedence(expr) < minimumPrecedence) { Parenthesize(expr); } } static void Parenthesize(Expression expr) { expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e }); } // Primary expressions public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { ParenthesizeIfRequired(memberReferenceExpression.Target, PrecedenceLevel.Primary); base.VisitMemberReferenceExpression(memberReferenceExpression); } public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { ParenthesizeIfRequired(pointerReferenceExpression.Target, PrecedenceLevel.Primary); base.VisitPointerReferenceExpression(pointerReferenceExpression); } public override void VisitInvocationExpression(InvocationExpression invocationExpression) { ParenthesizeIfRequired(invocationExpression.Target, PrecedenceLevel.Primary); base.VisitInvocationExpression(invocationExpression); } public override void VisitIndexerExpression(IndexerExpression indexerExpression) { ParenthesizeIfRequired(indexerExpression.Target, PrecedenceLevel.Primary); switch (indexerExpression.Target) { case ArrayCreateExpression ace when InsertParenthesesForReadability || ace.Initializer.IsNull: // require parentheses for "(new int[1])[0]" Parenthesize(indexerExpression.Target); break; case StackAllocExpression sae when InsertParenthesesForReadability || sae.Initializer.IsNull: // require parentheses for "(stackalloc int[1])[0]" Parenthesize(indexerExpression.Target); break; } base.VisitIndexerExpression(indexerExpression); } // Unary expressions public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { ParenthesizeIfRequired(unaryOperatorExpression.Expression, GetPrecedence(unaryOperatorExpression)); UnaryOperatorExpression child = unaryOperatorExpression.Expression as UnaryOperatorExpression; if (child != null && InsertParenthesesForReadability) Parenthesize(child); base.VisitUnaryOperatorExpression(unaryOperatorExpression); } public override void VisitCastExpression(CastExpression castExpression) { // Even in readability mode, don't parenthesize casts of casts. if (!(castExpression.Expression is CastExpression)) { ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? PrecedenceLevel.NullableRewrap : PrecedenceLevel.Unary); } // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases // "(int)-1" is fine, but "(A)-b" is not a cast. UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression; if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) { if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { Parenthesize(castExpression.Expression); } } // The above issue can also happen with PrimitiveExpressions representing negative values: PrimitiveExpression pe = castExpression.Expression as PrimitiveExpression; if (pe != null && pe.Value != null && TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { TypeCode typeCode = Type.GetTypeCode(pe.Value.GetType()); switch (typeCode) { case TypeCode.SByte: if ((sbyte)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Int16: if ((short)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Int32: if ((int)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Int64: if ((long)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Single: if ((float)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Double: if ((double)pe.Value < 0) Parenthesize(castExpression.Expression); break; case TypeCode.Decimal: if ((decimal)pe.Value < 0) Parenthesize(castExpression.Expression); break; } } base.VisitCastExpression(castExpression); } static bool TypeCanBeMisinterpretedAsExpression(AstType type) { // SimpleTypes can always be misinterpreted as IdentifierExpressions // MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon // PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions. MemberType mt = type as MemberType; if (mt != null) return !mt.IsDoubleColon; else return type is SimpleType; } // Binary Operators public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { PrecedenceLevel precedence = GetPrecedence(binaryOperatorExpression); if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { if (InsertParenthesesForReadability) { ParenthesizeIfRequired(binaryOperatorExpression.Left, PrecedenceLevel.NullableRewrap); if (GetBinaryOperatorType(binaryOperatorExpression.Right) == BinaryOperatorType.NullCoalescing) { ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); } else { ParenthesizeIfRequired(binaryOperatorExpression.Right, PrecedenceLevel.NullableRewrap); } } else { // ?? is right-associative ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1); ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); } } else { if (InsertParenthesesForReadability && precedence < PrecedenceLevel.Equality) { // In readable mode, boost the priority of the left-hand side if the operator // there isn't the same as the operator on this expression. PrecedenceLevel boostTo = IsBitwise(binaryOperatorExpression.Operator) ? PrecedenceLevel.Unary : PrecedenceLevel.Equality; if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) { ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); } else { ParenthesizeIfRequired(binaryOperatorExpression.Left, boostTo); } ParenthesizeIfRequired(binaryOperatorExpression.Right, boostTo); } else { // all other binary operators are left-associative ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1); } } base.VisitBinaryOperatorExpression(binaryOperatorExpression); } static bool IsBitwise(BinaryOperatorType op) { return op == BinaryOperatorType.BitwiseAnd || op == BinaryOperatorType.BitwiseOr || op == BinaryOperatorType.ExclusiveOr; } BinaryOperatorType? GetBinaryOperatorType(Expression expr) { BinaryOperatorExpression boe = expr as BinaryOperatorExpression; if (boe != null) return boe.Operator; else return null; } public override void VisitIsExpression(IsExpression isExpression) { if (InsertParenthesesForReadability) { // few people know the precedence of 'is', so always put parentheses in nice-looking mode. ParenthesizeIfRequired(isExpression.Expression, PrecedenceLevel.NullableRewrap); } else { ParenthesizeIfRequired(isExpression.Expression, PrecedenceLevel.RelationalAndTypeTesting); } base.VisitIsExpression(isExpression); } public override void VisitAsExpression(AsExpression asExpression) { if (InsertParenthesesForReadability) { // few people know the precedence of 'as', so always put parentheses in nice-looking mode. ParenthesizeIfRequired(asExpression.Expression, PrecedenceLevel.NullableRewrap); } else { ParenthesizeIfRequired(asExpression.Expression, PrecedenceLevel.RelationalAndTypeTesting); } base.VisitAsExpression(asExpression); } public override void VisitInterpolation(Interpolation interpolation) { // Need to do this first, in case the descendents parenthesize themselves. base.VisitInterpolation(interpolation); // If an interpolation contains global::, we need to parenthesize the expression. if (InterpolationNeedsParenthesis(interpolation)) Parenthesize(interpolation.Expression); static bool InterpolationNeedsParenthesis(AstNode node) { if (node is MemberType { IsDoubleColon: true }) return true; if (node is ParenthesizedExpression) return false; if (node is AnonymousMethodExpression or LambdaExpression { Body: BlockStatement }) return false; if (node is InvocationExpression invocation) return InterpolationNeedsParenthesis(invocation.Target); if (node is CastExpression cast) return InterpolationNeedsParenthesis(cast.Expression); foreach (var child in node.Children) { if (InterpolationNeedsParenthesis(child)) return true; } return false; } } // Conditional operator public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) { // Inside of string interpolation ?: always needs parentheses. if (conditionalExpression.Parent is Interpolation) { Parenthesize(conditionalExpression); } // Associativity here is a bit tricky: // (a ? b : c ? d : e) == (a ? b : (c ? d : e)) // (a ? b ? c : d : e) == (a ? (b ? c : d) : e) // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses if (InsertParenthesesForReadability && !IsConditionalRefExpression(conditionalExpression)) { // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. ParenthesizeIfRequired(conditionalExpression.Condition, PrecedenceLevel.NullableRewrap); ParenthesizeIfRequired(conditionalExpression.TrueExpression, PrecedenceLevel.NullableRewrap); ParenthesizeIfRequired(conditionalExpression.FalseExpression, PrecedenceLevel.NullableRewrap); } else { ParenthesizeIfRequired(conditionalExpression.Condition, PrecedenceLevel.Conditional + 1); ParenthesizeIfRequired(conditionalExpression.TrueExpression, PrecedenceLevel.Conditional); ParenthesizeIfRequired(conditionalExpression.FalseExpression, PrecedenceLevel.Conditional); } base.VisitConditionalExpression(conditionalExpression); } private bool IsConditionalRefExpression(ConditionalExpression conditionalExpression) { return conditionalExpression.TrueExpression is DirectionExpression || conditionalExpression.FalseExpression is DirectionExpression; } public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { // Assignments in initializers need additional parentheses to disambiguate assignments // to variables and assignments to members of the initialized object. // This works without access to semantic information, because the ExpressionBuilder // uses NamedExpression for `Member = value` instead of AssignmentExpression. if (assignmentExpression.Parent is ArrayInitializerExpression && assignmentExpression.Left is not IndexerExpression) { Parenthesize(assignmentExpression); } // assignment is right-associative ParenthesizeIfRequired(assignmentExpression.Left, PrecedenceLevel.Assignment + 1); HandleAssignmentRHS(assignmentExpression.Right); base.VisitAssignmentExpression(assignmentExpression); } private void HandleAssignmentRHS(Expression right) { if (InsertParenthesesForReadability && !(right is DirectionExpression)) { ParenthesizeIfRequired(right, PrecedenceLevel.Conditional + 1); } else { ParenthesizeIfRequired(right, PrecedenceLevel.Assignment); } } public override void VisitVariableInitializer(VariableInitializer variableInitializer) { if (!variableInitializer.Initializer.IsNull) HandleAssignmentRHS(variableInitializer.Initializer); base.VisitVariableInitializer(variableInitializer); } // don't need to handle lambdas, they have lowest precedence and unambiguous associativity public override void VisitQueryExpression(QueryExpression queryExpression) { // Query expressions are strange beasts: // "var a = -from b in c select d;" is valid, so queries bind stricter than unary expressions. // However, the end of the query is greedy. So their start sort of has a high precedence, // while their end has a very low precedence. We handle this by checking whether a query is used // as left part of a binary operator, and parenthesize it if required. HandleLambdaOrQuery(queryExpression); base.VisitQueryExpression(queryExpression); } public override void VisitLambdaExpression(LambdaExpression lambdaExpression) { // Lambdas are greedy in the same way as query expressions. HandleLambdaOrQuery(lambdaExpression); base.VisitLambdaExpression(lambdaExpression); } void HandleLambdaOrQuery(Expression expr) { if (expr.Role == BinaryOperatorExpression.LeftRole) Parenthesize(expr); if (expr.Parent is IsExpression || expr.Parent is AsExpression) Parenthesize(expr); if (InsertParenthesesForReadability) { // when readability is desired, always parenthesize query expressions within unary or binary operators if (expr.Parent is UnaryOperatorExpression || expr.Parent is BinaryOperatorExpression) Parenthesize(expr); } } public override void VisitNamedExpression(NamedExpression namedExpression) { if (InsertParenthesesForReadability) { ParenthesizeIfRequired(namedExpression.Expression, PrecedenceLevel.RelationalAndTypeTesting + 1); } base.VisitNamedExpression(namedExpression); } public override void VisitSwitchExpression(SwitchExpression switchExpression) { ParenthesizeIfRequired(switchExpression.Expression, PrecedenceLevel.Switch + 1); base.VisitSwitchExpression(switchExpression); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { class InsertRequiredSpacesDecorator : DecoratingTokenWriter { /// /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written. /// LastWritten lastWritten; enum LastWritten { Whitespace, Other, KeywordOrIdentifier, Plus, Minus, Ampersand, QuestionMark, Division } public InsertRequiredSpacesDecorator(TokenWriter writer) : base(writer) { } public override void WriteIdentifier(Identifier identifier) { if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { if (lastWritten == LastWritten.KeywordOrIdentifier) { // this space is not strictly required, so we call Space() Space(); } } else if (lastWritten == LastWritten.KeywordOrIdentifier) { // this space is strictly required, so we directly call the formatter base.Space(); } base.WriteIdentifier(identifier); lastWritten = LastWritten.KeywordOrIdentifier; } public override void WriteKeyword(Role role, string keyword) { if (lastWritten == LastWritten.KeywordOrIdentifier) { Space(); } base.WriteKeyword(role, keyword); lastWritten = LastWritten.KeywordOrIdentifier; } public override void WriteToken(Role role, string token) { // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token. // Note that we don't need to handle tokens like = because there's no valid // C# program that contains the single token twice in a row. // (for +, - and &, this can happen with unary operators; // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0"; // and for /, this can happen with "1/ *ptr" or "1/ //comment".) if (lastWritten == LastWritten.Plus && token[0] == '+' || lastWritten == LastWritten.Minus && token[0] == '-' || lastWritten == LastWritten.Ampersand && token[0] == '&' || lastWritten == LastWritten.QuestionMark && token[0] == '?' || lastWritten == LastWritten.Division && token[0] == '*') { base.Space(); } base.WriteToken(role, token); if (token == "+") { lastWritten = LastWritten.Plus; } else if (token == "-") { lastWritten = LastWritten.Minus; } else if (token == "&") { lastWritten = LastWritten.Ampersand; } else if (token == "?") { lastWritten = LastWritten.QuestionMark; } else if (token == "/") { lastWritten = LastWritten.Division; } else { lastWritten = LastWritten.Other; } } public override void Space() { base.Space(); lastWritten = LastWritten.Whitespace; } public override void NewLine() { base.NewLine(); lastWritten = LastWritten.Whitespace; } public override void WriteComment(CommentType commentType, string content) { if (lastWritten == LastWritten.Division) { // When there's a comment starting after a division operator // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. base.Space(); } base.WriteComment(commentType, content); lastWritten = LastWritten.Whitespace; } public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { base.WritePreProcessorDirective(type, argument); lastWritten = LastWritten.Whitespace; } public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { if (lastWritten == LastWritten.KeywordOrIdentifier) { Space(); } base.WritePrimitiveValue(value, format); if (value == null || value is bool) return; if (value is string) { if (format == LiteralFormat.VerbatimStringLiteral) lastWritten = LastWritten.KeywordOrIdentifier; else lastWritten = LastWritten.Other; } else if (value is char) { lastWritten = LastWritten.Other; } else if (value is decimal) { lastWritten = LastWritten.Other; } else if (value is float) { float f = (float)value; if (float.IsInfinity(f) || float.IsNaN(f)) return; lastWritten = LastWritten.Other; } else if (value is double) { double f = (double)value; if (double.IsInfinity(f) || double.IsNaN(f)) return; // needs space if identifier follows number; // this avoids mistaking the following identifier as type suffix lastWritten = LastWritten.KeywordOrIdentifier; } else if (value is IFormattable) { // needs space if identifier follows number; // this avoids mistaking the following identifier as type suffix lastWritten = LastWritten.KeywordOrIdentifier; } else { lastWritten = LastWritten.Other; } } public override void WritePrimitiveType(string type) { if (lastWritten == LastWritten.KeywordOrIdentifier) { Space(); } base.WritePrimitiveType(type); if (type == "new") { lastWritten = LastWritten.Other; } else { lastWritten = LastWritten.KeywordOrIdentifier; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertSpecialsDecorator.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Diagnostics; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { class InsertSpecialsDecorator : DecoratingTokenWriter { readonly Stack positionStack = new Stack(); int visitorWroteNewLine = 0; public InsertSpecialsDecorator(TokenWriter writer) : base(writer) { } public override void StartNode(AstNode node) { if (positionStack.Count > 0) { WriteSpecialsUpToNode(node); } positionStack.Push(node.FirstChild); base.StartNode(node); } public override void EndNode(AstNode node) { base.EndNode(node); AstNode pos = positionStack.Pop(); Debug.Assert(pos == null || pos.Parent == node); WriteSpecials(pos, null); } public override void WriteKeyword(Role role, string keyword) { if (role != null) { WriteSpecialsUpToRole(role); } base.WriteKeyword(role, keyword); } public override void WriteIdentifier(Identifier identifier) { WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier); base.WriteIdentifier(identifier); } public override void WriteToken(Role role, string token) { WriteSpecialsUpToRole(role); base.WriteToken(role, token); } public override void NewLine() { if (visitorWroteNewLine >= 0) base.NewLine(); visitorWroteNewLine++; } #region WriteSpecials /// /// Writes all specials from start to end (exclusive). Does not touch the positionStack. /// void WriteSpecials(AstNode start, AstNode end) { for (AstNode pos = start; pos != end; pos = pos.NextSibling) { if (pos.Role == Roles.Comment) { var node = (Comment)pos; base.StartNode(node); base.WriteComment(node.CommentType, node.Content); base.EndNode(node); } // see CSharpOutputVisitor.VisitNewLine() // if (pos.Role == Roles.NewLine) { // if (visitorWroteNewLine <= 0) // base.NewLine(); // visitorWroteNewLine--; // } if (pos.Role == Roles.PreProcessorDirective) { var node = (PreProcessorDirective)pos; base.StartNode(node); base.WritePreProcessorDirective(node.Type, node.Argument); base.EndNode(node); } } } /// /// Writes all specials between the current position (in the positionStack) and the next /// node with the specified role. Advances the current position. /// void WriteSpecialsUpToRole(Role role) { WriteSpecialsUpToRole(role, null); } void WriteSpecialsUpToRole(Role role, AstNode nextNode) { if (positionStack.Count == 0) { return; } // Look for the role between the current position and the nextNode. for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { if (pos.Role == role) { WriteSpecials(positionStack.Pop(), pos); // Push the next sibling because the node matching the role is not a special, // and should be considered to be already handled. positionStack.Push(pos.NextSibling); // This is necessary for OptionalComma() to work correctly. break; } } } /// /// Writes all specials between the current position (in the positionStack) and the specified node. /// Advances the current position. /// void WriteSpecialsUpToNode(AstNode node) { if (positionStack.Count == 0) { return; } for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { if (pos == node) { WriteSpecials(positionStack.Pop(), pos); // Push the next sibling because the node itself is not a special, // and should be considered to be already handled. positionStack.Push(pos.NextSibling); // This is necessary for OptionalComma() to work correctly. break; } } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using System.IO; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// Writes C# code into a TextWriter. /// public class TextWriterTokenWriter : TokenWriter, ILocatable { readonly TextWriter textWriter; bool needsIndent = true; bool isAtStartOfLine = true; int line, column; public int Indentation { get; set; } public TextLocation Location { get { return new TextLocation(line, column + (needsIndent ? Indentation * IndentationString.Length : 0)); } } public string IndentationString { get; set; } public int Length { get; private set; } public TextWriterTokenWriter(TextWriter textWriter) { if (textWriter == null) throw new ArgumentNullException(nameof(textWriter)); this.textWriter = textWriter; this.IndentationString = "\t"; this.line = 1; this.column = 1; } public override void WriteIdentifier(Identifier identifier) { WriteIndentation(); if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { textWriter.Write('@'); column++; Length++; } string name = EscapeIdentifier(identifier.Name); textWriter.Write(name); column += name.Length; Length += name.Length; isAtStartOfLine = false; } public override void WriteKeyword(Role role, string keyword) { WriteIndentation(); column += keyword.Length; Length += keyword.Length; textWriter.Write(keyword); isAtStartOfLine = false; } public override void WriteToken(Role role, string token) { WriteIndentation(); column += token.Length; Length += token.Length; textWriter.Write(token); isAtStartOfLine = false; } public override void Space() { WriteIndentation(); column++; Length++; textWriter.Write(' '); } protected void WriteIndentation() { if (needsIndent) { needsIndent = false; for (int i = 0; i < Indentation; i++) { textWriter.Write(this.IndentationString); } column += Indentation * IndentationString.Length; Length += Indentation * IndentationString.Length; } } public override void NewLine() { textWriter.WriteLine(); column = 1; line++; Length += textWriter.NewLine.Length; needsIndent = true; isAtStartOfLine = true; } public override void Indent() { Indentation++; } public override void Unindent() { Indentation--; } public override void WriteComment(CommentType commentType, string content) { WriteIndentation(); switch (commentType) { case CommentType.SingleLine: textWriter.Write("//"); textWriter.WriteLine(content); Length += 2 + content.Length + textWriter.NewLine.Length; column = 1; line++; needsIndent = true; isAtStartOfLine = true; break; case CommentType.MultiLine: textWriter.Write("/*"); textWriter.Write(content); textWriter.Write("*/"); Length += 4 + content.Length; column += 2; UpdateEndLocation(content, ref line, ref column); column += 2; isAtStartOfLine = false; break; case CommentType.Documentation: textWriter.Write("///"); textWriter.WriteLine(content); Length += 3 + content.Length + textWriter.NewLine.Length; column = 1; line++; needsIndent = true; isAtStartOfLine = true; break; case CommentType.MultiLineDocumentation: textWriter.Write("/**"); textWriter.Write(content); textWriter.Write("*/"); Length += 5 + content.Length; column += 3; UpdateEndLocation(content, ref line, ref column); column += 2; isAtStartOfLine = false; break; default: textWriter.Write(content); column += content.Length; Length += content.Length; break; } } static void UpdateEndLocation(string content, ref int line, ref int column) { if (string.IsNullOrEmpty(content)) return; for (int i = 0; i < content.Length; i++) { char ch = content[i]; switch (ch) { case '\r': if (i + 1 < content.Length && content[i + 1] == '\n') i++; goto case '\n'; case '\n': line++; column = 0; break; } column++; } } public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { // pre-processor directive must start on its own line if (!isAtStartOfLine) NewLine(); WriteIndentation(); textWriter.Write('#'); string directive = type.ToString().ToLowerInvariant(); textWriter.Write(directive); column += 1 + directive.Length; Length += 1 + directive.Length; if (!string.IsNullOrEmpty(argument)) { textWriter.Write(' '); textWriter.Write(argument); column += 1 + argument.Length; Length += 1 + argument.Length; } NewLine(); } public static string PrintPrimitiveValue(object value) { TextWriter writer = new StringWriter(); TextWriterTokenWriter tokenWriter = new TextWriterTokenWriter(writer); tokenWriter.WritePrimitiveValue(value); return writer.ToString(); } public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { if (value == null) { // usually NullReferenceExpression should be used for this, but we'll handle it anyways textWriter.Write("null"); column += 4; Length += 4; return; } if (value is bool) { if ((bool)value) { textWriter.Write("true"); column += 4; Length += 4; } else { textWriter.Write("false"); column += 5; Length += 5; } return; } if (value is string) { string tmp = ConvertString(value.ToString()); column += tmp.Length + 2; Length += tmp.Length + 2; textWriter.Write('"'); textWriter.Write(tmp); textWriter.Write('"'); if (format == LiteralFormat.Utf8Literal) { textWriter.Write("u8"); column += 2; Length += 2; } } else if (value is char) { string tmp = ConvertCharLiteral((char)value); column += tmp.Length + 2; Length += tmp.Length + 2; textWriter.Write('\''); textWriter.Write(tmp); textWriter.Write('\''); } else if (value is decimal) { string str = ((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m"; column += str.Length; Length += str.Length; textWriter.Write(str); } else if (value is float) { float f = (float)value; if (float.IsInfinity(f) || float.IsNaN(f)) { // Strictly speaking, these aren't PrimitiveExpressions; // but we still support writing these to make life easier for code generators. textWriter.Write("float"); column += 5; Length += 5; WriteToken(Roles.Dot, "."); if (float.IsPositiveInfinity(f)) { textWriter.Write("PositiveInfinity"); column += "PositiveInfinity".Length; Length += "PositiveInfinity".Length; } else if (float.IsNegativeInfinity(f)) { textWriter.Write("NegativeInfinity"); column += "NegativeInfinity".Length; Length += "NegativeInfinity".Length; } else { textWriter.Write("NaN"); column += 3; Length += 3; } return; } var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; if (f == 0 && 1 / f == float.NegativeInfinity && str[0] != '-') { // negative zero is a special case // (again, not a primitive expression, but it's better to handle // the special case here than to do it in all code generators) str = '-' + str; } column += str.Length; Length += str.Length; textWriter.Write(str); } else if (value is double) { double f = (double)value; if (double.IsInfinity(f) || double.IsNaN(f)) { // Strictly speaking, these aren't PrimitiveExpressions; // but we still support writing these to make life easier for code generators. textWriter.Write("double"); column += 6; Length += 6; WriteToken(Roles.Dot, "."); if (double.IsPositiveInfinity(f)) { textWriter.Write("PositiveInfinity"); column += "PositiveInfinity".Length; Length += "PositiveInfinity".Length; } else if (double.IsNegativeInfinity(f)) { textWriter.Write("NegativeInfinity"); column += "NegativeInfinity".Length; Length += "NegativeInfinity".Length; } else { textWriter.Write("NaN"); column += 3; Length += 3; } return; } string number = f.ToString("R", NumberFormatInfo.InvariantInfo); if (f == 0 && 1 / f == double.NegativeInfinity && number[0] != '-') { // negative zero is a special case // (again, not a primitive expression, but it's better to handle // the special case here than to do it in all code generators) number = '-' + number; } if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) { number += ".0"; } textWriter.Write(number); column += number.Length; Length += number.Length; } else if (value is IFormattable) { StringBuilder b = new StringBuilder(); if (format == LiteralFormat.HexadecimalNumber) { b.Append("0x"); b.Append(((IFormattable)value).ToString("X", NumberFormatInfo.InvariantInfo)); } else { b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); } if (value is uint || value is ulong) { b.Append("u"); } if (value is long || value is ulong) { b.Append("L"); } textWriter.Write(b.ToString()); column += b.Length; Length += b.Length; } else { textWriter.Write(value.ToString()); int length = value.ToString().Length; column += length; Length += length; } } public override void WriteInterpolatedText(string text) { textWriter.Write(ConvertString(text)); } /// /// Gets the escape sequence for the specified character within a char literal. /// Does not include the single quotes surrounding the char literal. /// public static string ConvertCharLiteral(char ch) { if (ch == '\'') { return "\\'"; } return ConvertChar(ch) ?? ch.ToString(); } /// /// Gets the escape sequence for the specified character. /// /// This method does not convert ' or ". static string ConvertChar(char ch) { switch (ch) { case '\\': return "\\\\"; case '\0': return "\\0"; case '\a': return "\\a"; case '\b': return "\\b"; case '\f': return "\\f"; case '\n': return "\\n"; case '\r': return "\\r"; case '\t': return "\\t"; case '\v': return "\\v"; case ' ': case '_': case '`': case '^': // ASCII characters we allow directly in the output even though we don't use // other Unicode characters of the same category. return null; case '\ufffd': return "\\u" + ((int)ch).ToString("x4"); default: switch (char.GetUnicodeCategory(ch)) { case UnicodeCategory.NonSpacingMark: case UnicodeCategory.SpacingCombiningMark: case UnicodeCategory.EnclosingMark: case UnicodeCategory.LineSeparator: case UnicodeCategory.ParagraphSeparator: case UnicodeCategory.Control: case UnicodeCategory.Format: case UnicodeCategory.Surrogate: case UnicodeCategory.PrivateUse: case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.ModifierSymbol: case UnicodeCategory.OtherNotAssigned: case UnicodeCategory.SpaceSeparator: return "\\u" + ((int)ch).ToString("x4"); default: return null; } } } /// /// Converts special characters to escape sequences within the given string. /// public static string ConvertString(string str) { StringBuilder sb = new StringBuilder(); foreach (char ch in str) { string s = ch == '"' ? "\\\"" : ConvertChar(ch); if (s != null) sb.Append(s); else sb.Append(ch); } return sb.ToString(); } public static string EscapeIdentifier(string identifier) { if (string.IsNullOrEmpty(identifier)) return identifier; StringBuilder sb = new StringBuilder(); for (int i = 0; i < identifier.Length; i++) { if (IsPrintableIdentifierChar(identifier, i)) { if (char.IsSurrogatePair(identifier, i)) { sb.Append(identifier.Substring(i, 2)); i++; } else { sb.Append(identifier[i]); } } else { if (char.IsSurrogatePair(identifier, i)) { sb.AppendFormat("\\U{0:x8}", char.ConvertToUtf32(identifier, i)); i++; } else { sb.AppendFormat("\\u{0:x4}", (int)identifier[i]); } } } return sb.ToString(); } public static bool ContainsNonPrintableIdentifierChar(string identifier) { if (string.IsNullOrEmpty(identifier)) return false; for (int i = 0; i < identifier.Length; i++) { if (char.IsWhiteSpace(identifier[i])) return true; if (!IsPrintableIdentifierChar(identifier, i)) return true; } return false; } static bool IsPrintableIdentifierChar(string identifier, int index) { switch (identifier[index]) { case '\\': return false; case ' ': case '_': case '`': case '^': return true; } switch (char.GetUnicodeCategory(identifier, index)) { case UnicodeCategory.NonSpacingMark: case UnicodeCategory.SpacingCombiningMark: case UnicodeCategory.EnclosingMark: case UnicodeCategory.LineSeparator: case UnicodeCategory.ParagraphSeparator: case UnicodeCategory.Control: case UnicodeCategory.Format: case UnicodeCategory.Surrogate: case UnicodeCategory.PrivateUse: case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.ModifierSymbol: case UnicodeCategory.OtherNotAssigned: case UnicodeCategory.SpaceSeparator: return false; default: return true; } } public override void WritePrimitiveType(string type) { textWriter.Write(type); column += type.Length; Length += type.Length; if (type == "new") { textWriter.Write("()"); column += 2; Length += 2; } } public override void StartNode(AstNode node) { // Write out the indentation, so that overrides of this method // can rely use the current output length to identify the position of the node // in the output. WriteIndentation(); } public override void EndNode(AstNode node) { } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectFileWriter.cs ================================================ // Copyright (c) 2020 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.Collections.Generic; using System.IO; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// An interface for a service that creates and writes a project file structure /// for a specific module being decompiled. /// public interface IProjectFileWriter { /// /// Writes the content of a new project file for the specified being decompiled. /// /// The target to write to. /// The information about the project being created. /// A collection of source files to be included into the project. /// The module being decompiled. void Write(TextWriter target, IProjectInfoProvider project, IEnumerable files, MetadataFile module); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectInfoProvider.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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 ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// An interface that provides common information for a project being decompiled to. /// public interface IProjectInfoProvider { /// /// Gets the assembly resolver active for the project. /// IAssemblyResolver AssemblyResolver { get; } AssemblyReferenceClassifier AssemblyReferenceClassifier { get; } /// /// Gets the C# language version of the project. /// LanguageVersion LanguageVersion { get; } /// /// Check for overflow and underflow in operators. /// bool CheckForOverflowUnderflow { get; } /// /// Gets the unique ID of the project. /// Guid ProjectGuid { get; } /// /// Gets the target directory of the project /// string TargetDirectory { get; } /// /// Gets the name of the key file being used for strong name signing. Can be null if no file is available. /// string StrongNameKeyFile { get; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs ================================================ // Copyright (c) 2020 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.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Xml; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// A implementation that creates the projects in the default format. /// sealed class ProjectFileWriterDefault : IProjectFileWriter { /// /// Creates a new instance of the class. /// /// A new instance of the class. public static IProjectFileWriter Create() => new ProjectFileWriterDefault(); /// public void Write( TextWriter target, IProjectInfoProvider project, IEnumerable files, MetadataFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = module is PEFile peFile ? TargetServices.GetPlatformName(peFile) : "AnyCPU"; var targetFramework = TargetServices.DetectTargetFramework(module); if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200) targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework); List typeGuids = new List(); if (targetFramework.IsPortableClassLibrary) typeGuids.Add(ProjectTypeGuids.PortableLibrary); typeGuids.Add(ProjectTypeGuids.CSharpWindows); using (XmlTextWriter w = new XmlTextWriter(target)) { w.Formatting = Formatting.Indented; w.WriteStartDocument(); w.WriteStartElement("Project", ns); w.WriteAttributeString("ToolsVersion", "4.0"); w.WriteAttributeString("DefaultTargets", "Build"); w.WriteStartElement("PropertyGroup"); w.WriteElementString("ProjectGuid", project.ProjectGuid.ToString("B").ToUpperInvariant()); w.WriteElementString("ProjectTypeGuids", string.Join(";", typeGuids.Select(g => g.ToString("B").ToUpperInvariant()))); w.WriteStartElement("Configuration"); w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); w.WriteValue("Debug"); w.WriteEndElement(); // w.WriteStartElement("Platform"); w.WriteAttributeString("Condition", " '$(Platform)' == '' "); w.WriteValue(platformName); w.WriteEndElement(); // string outputType; PEHeaders headers = (module as PEFile)?.Reader.PEHeaders; switch (headers?.PEHeader.Subsystem) { case Subsystem.WindowsGui when !headers.IsDll: outputType = "WinExe"; break; case Subsystem.WindowsCui when !headers.IsDll: outputType = "Exe"; break; default: outputType = "Library"; break; } w.WriteElementString("OutputType", outputType); w.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); w.WriteElementString("CheckForOverflowUnderflow", project.CheckForOverflowUnderflow ? "true" : "false"); w.WriteElementString("AssemblyName", module.Name); if (targetFramework.Identifier != null) w.WriteElementString("TargetFrameworkIdentifier", targetFramework.Identifier); if (targetFramework.VersionString != null) w.WriteElementString("TargetFrameworkVersion", targetFramework.VersionString); if (targetFramework.Profile != null) w.WriteElementString("TargetFrameworkProfile", targetFramework.Profile); w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); if (project.StrongNameKeyFile != null) { w.WriteElementString("SignAssembly", "True"); w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(project.StrongNameKeyFile)); } w.WriteEndElement(); // w.WriteStartElement("PropertyGroup"); // platform-specific w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); w.WriteElementString("PlatformTarget", platformName); if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && ((module as PEFile)?.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) { w.WriteElementString("Prefer32Bit", "false"); } w.WriteEndElement(); // (platform-specific) w.WriteStartElement("PropertyGroup"); // Debug w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' "); w.WriteElementString("OutputPath", "bin\\Debug\\"); w.WriteElementString("DebugSymbols", "true"); w.WriteElementString("DebugType", "full"); w.WriteElementString("Optimize", "false"); w.WriteEndElement(); // (Debug) w.WriteStartElement("PropertyGroup"); // Release w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' "); w.WriteElementString("OutputPath", "bin\\Release\\"); w.WriteElementString("DebugSymbols", "true"); w.WriteElementString("DebugType", "pdbonly"); w.WriteElementString("Optimize", "true"); w.WriteEndElement(); // (Release) w.WriteStartElement("ItemGroup"); // References foreach (var r in module.AssemblyReferences) { if (r.Name != "mscorlib") { w.WriteStartElement("Reference"); w.WriteAttributeString("Include", r.Name); var asm = project.AssemblyResolver.Resolve(r); if (asm != null && !project.AssemblyReferenceClassifier.IsGacAssembly(r)) { w.WriteElementString("HintPath", asm.FileName); } w.WriteEndElement(); } } w.WriteEndElement(); // (References) foreach (IGrouping gr in files.GroupBy(f => f.ItemType).OrderBy(g => g.Key)) { w.WriteStartElement("ItemGroup"); foreach (var item in gr.OrderBy(f => f.FileName, StringComparer.OrdinalIgnoreCase)) { w.WriteStartElement(gr.Key); w.WriteAttributeString("Include", item.FileName); if (item.AdditionalProperties != null) { foreach (var (key, value) in item.AdditionalProperties) w.WriteAttributeString(key, value); } w.WriteEndElement(); } w.WriteEndElement(); } if (targetFramework.IsPortableClassLibrary) { w.WriteStartElement("Import"); w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); w.WriteEndElement(); } else { w.WriteStartElement("Import"); w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); w.WriteEndElement(); } w.WriteEndDocument(); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs ================================================ // Copyright (c) 2020 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.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Xml; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// A implementation that creates the projects in the SDK style format. /// sealed class ProjectFileWriterSdkStyle : IProjectFileWriter { const string AspNetCorePrefix = "Microsoft.AspNetCore"; const string PresentationFrameworkName = "PresentationFramework"; const string WindowsFormsName = "System.Windows.Forms"; const string TrueString = "True"; const string FalseString = "False"; const string AnyCpuString = "AnyCPU"; static readonly HashSet ImplicitReferences = new HashSet { "mscorlib", "netstandard", "PresentationFramework", "System", "System.Diagnostics.Debug", "System.Diagnostics.Tools", "System.Drawing", "System.Runtime", "System.Runtime.Extensions", "System.Windows.Forms", "System.Xaml", }; enum ProjectType { Default, WinForms, Wpf, Web } /// /// Creates a new instance of the class. /// /// A new instance of the class. public static IProjectFileWriter Create() => new ProjectFileWriterSdkStyle(); /// public void Write( TextWriter target, IProjectInfoProvider project, IEnumerable files, MetadataFile module) { using (XmlTextWriter xmlWriter = new XmlTextWriter(target)) { xmlWriter.Formatting = Formatting.Indented; Write(xmlWriter, project, files, module); } } static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable files, MetadataFile module) { xml.WriteStartElement("Project"); var projectType = GetProjectType(module); xml.WriteAttributeString("Sdk", GetSdkString(projectType)); PlaceIntoTag("PropertyGroup", xml, () => WriteAssemblyInfo(xml, module, project, projectType)); PlaceIntoTag("PropertyGroup", xml, () => WriteProjectInfo(xml, project)); PlaceIntoTag("PropertyGroup", xml, () => WriteMiscellaneousPropertyGroup(xml, files)); PlaceIntoTag("ItemGroup", xml, () => WriteResources(xml, files)); PlaceIntoTag("ItemGroup", xml, () => WriteReferences(xml, module, project, projectType)); xml.WriteEndElement(); } static void PlaceIntoTag(string tagName, XmlTextWriter xml, Action content) { xml.WriteStartElement(tagName); try { content(); } finally { xml.WriteEndElement(); } } static void WriteAssemblyInfo(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType) { xml.WriteElementString("AssemblyName", module.Name); // Since we create AssemblyInfo.cs manually, we need to disable the auto-generation xml.WriteElementString("GenerateAssemblyInfo", FalseString); string platformName; CorFlags flags; if (module is PEFile { Reader.PEHeaders: var headers } peFile) { WriteOutputType(xml, headers.IsDll, headers.PEHeader.Subsystem, projectType); platformName = TargetServices.GetPlatformName(peFile); flags = headers.CorHeader.Flags; } else { WriteOutputType(xml, isDll: true, Subsystem.Unknown, projectType); platformName = AnyCpuString; flags = 0; } WriteDesktopExtensions(xml, projectType); var targetFramework = TargetServices.DetectTargetFramework(module); if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200) targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework); if (targetFramework.Moniker == null) { throw new NotSupportedException($"Cannot decompile this assembly to a SDK style project. Use default project format instead."); } xml.WriteElementString("TargetFramework", targetFramework.Moniker); // 'AnyCPU' is default, so only need to specify platform if it differs if (platformName != AnyCpuString) { xml.WriteElementString("PlatformTarget", platformName); } if (platformName == AnyCpuString && (flags & CorFlags.Prefers32Bit) != 0) { xml.WriteElementString("Prefer32Bit", TrueString); } } static void WriteOutputType(XmlTextWriter xml, bool isDll, Subsystem moduleSubsystem, ProjectType projectType) { if (!isDll) { switch (moduleSubsystem) { case Subsystem.WindowsGui: xml.WriteElementString("OutputType", "WinExe"); break; case Subsystem.WindowsCui: xml.WriteElementString("OutputType", "Exe"); break; } } else { // 'Library' is default, so only need to specify output type for executables (excludes ProjectType.Web) if (projectType == ProjectType.Web) { xml.WriteElementString("OutputType", "Library"); } } } static void WriteDesktopExtensions(XmlTextWriter xml, ProjectType projectType) { if (projectType == ProjectType.Wpf) { xml.WriteElementString("UseWPF", TrueString); } else if (projectType == ProjectType.WinForms) { xml.WriteElementString("UseWindowsForms", TrueString); } } static void WriteProjectInfo(XmlTextWriter xml, IProjectInfoProvider project) { xml.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); xml.WriteElementString("AllowUnsafeBlocks", TrueString); xml.WriteElementString("CheckForOverflowUnderflow", project.CheckForOverflowUnderflow ? TrueString : FalseString); if (project.StrongNameKeyFile != null) { xml.WriteElementString("SignAssembly", TrueString); xml.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(project.StrongNameKeyFile)); } } static void WriteMiscellaneousPropertyGroup(XmlTextWriter xml, IEnumerable files) { var (itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationIcon"); if (fileName != null) xml.WriteElementString("ApplicationIcon", fileName); (itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationManifest"); if (fileName != null) xml.WriteElementString("ApplicationManifest", fileName); if (files.Any(t => t.ItemType == "EmbeddedResource")) xml.WriteElementString("RootNamespace", string.Empty); // TODO: We should add CustomToolNamespace for resources, otherwise we should add empty RootNamespace } static void WriteResources(XmlTextWriter xml, IEnumerable files) { // remove phase foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource")) { string buildAction = Path.GetExtension(item.FileName).ToUpperInvariant() switch { ".CS" => "Compile", ".RESX" => "EmbeddedResource", _ => "None" }; if (buildAction == "EmbeddedResource") continue; xml.WriteStartElement(buildAction); xml.WriteAttributeString("Remove", item.FileName); xml.WriteEndElement(); } // include phase foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource")) { if (Path.GetExtension(item.FileName) == ".resx") continue; xml.WriteStartElement("EmbeddedResource"); xml.WriteAttributeString("Include", item.FileName); if (item.AdditionalProperties != null) { foreach (var (key, value) in item.AdditionalProperties) xml.WriteAttributeString(key, value); } xml.WriteEndElement(); } } static void WriteReferences(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType) { bool isNetCoreApp = TargetServices.DetectTargetFramework(module).Identifier == ".NETCoreApp"; var targetPacks = new HashSet(); if (isNetCoreApp) { targetPacks.Add("Microsoft.NETCore.App"); switch (projectType) { case ProjectType.WinForms: case ProjectType.Wpf: targetPacks.Add("Microsoft.WindowsDesktop.App"); break; case ProjectType.Web: targetPacks.Add("Microsoft.AspNetCore.App"); targetPacks.Add("Microsoft.AspNetCore.All"); break; } } foreach (var reference in module.AssemblyReferences.Where(r => !ImplicitReferences.Contains(r.Name))) { if (isNetCoreApp && project.AssemblyReferenceClassifier.IsSharedAssembly(reference, out string runtimePack) && targetPacks.Contains(runtimePack)) { continue; } xml.WriteStartElement("Reference"); xml.WriteAttributeString("Include", reference.Name); var asembly = project.AssemblyResolver.Resolve(reference); if (asembly != null && !project.AssemblyReferenceClassifier.IsGacAssembly(reference)) { xml.WriteElementString("HintPath", FileUtility.GetRelativePath(project.TargetDirectory, asembly.FileName)); } xml.WriteEndElement(); } } static string GetSdkString(ProjectType projectType) { switch (projectType) { case ProjectType.WinForms: case ProjectType.Wpf: return "Microsoft.NET.Sdk.WindowsDesktop"; case ProjectType.Web: return "Microsoft.NET.Sdk.Web"; default: return "Microsoft.NET.Sdk"; } } static ProjectType GetProjectType(MetadataFile module) { foreach (var referenceName in module.AssemblyReferences.Select(r => r.Name)) { if (referenceName.StartsWith(AspNetCorePrefix, StringComparison.Ordinal)) { return ProjectType.Web; } if (referenceName == PresentationFrameworkName) { return ProjectType.Wpf; } if (referenceName == WindowsFormsName) { return ProjectType.WinForms; } } return ProjectType.Default; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/TargetFramework.cs ================================================ // Copyright (c) 2020 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.Text; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// A class describing the target framework of a module. /// public class TargetFramework { const string DotNetPortableIdentifier = ".NETPortable"; /// /// Initializes a new instance of the class. /// /// The framework identifier string. Can be null. /// The framework version string. Must be greater than 100 (where 100 corresponds to v1.0). /// The framework profile. Can be null. public TargetFramework(string identifier, int version, string profile) { if (version < 100) { throw new ArgumentException("The version number must be greater than or equal to 100", nameof(version)); } Identifier = identifier; VersionNumber = version; VersionString = "v" + GetVersionString(version, withDots: true); Moniker = GetTargetFrameworkMoniker(Identifier, version); Profile = profile; IsPortableClassLibrary = identifier == DotNetPortableIdentifier; } /// /// Gets the target framework identifier. Can be null if not defined. /// public string Identifier { get; } /// /// Gets the target framework moniker. Can be null if not supported. /// public string Moniker { get; } /// /// Gets the target framework version, e.g. "v4.5". /// public string VersionString { get; } /// /// Gets the target framework version as integer (multiplied by 100), e.g. 450. /// public int VersionNumber { get; } /// /// Gets the target framework profile. Can be null if not set or not available. /// public string Profile { get; } /// /// Gets a value indicating whether the target is a portable class library (PCL). /// public bool IsPortableClassLibrary { get; } static string GetTargetFrameworkMoniker(string frameworkIdentifier, int version) { // Reference: https://docs.microsoft.com/en-us/dotnet/standard/frameworks switch (frameworkIdentifier) { case null: case ".NETFramework": return "net" + GetVersionString(version, withDots: false); case ".NETCoreApp": if (version >= 500) return "net" + GetVersionString(version, withDots: true); return "netcoreapp" + GetVersionString(version, withDots: true); case ".NETStandard": return "netstandard" + GetVersionString(version, withDots: true); case "Silverlight": return "sl" + version / 100; case ".NETCore": return "netcore" + GetVersionString(version, withDots: false); case "WindowsPhone": return "wp" + GetVersionString(version, withDots: false, omitMinorWhenZero: true); case ".NETMicroFramework": return "netmf"; default: return null; } } static string GetVersionString(int version, bool withDots, bool omitMinorWhenZero = false) { int major = version / 100; int minor = version % 100 / 10; int patch = version % 10; if (omitMinorWhenZero && minor == 0 && patch == 0) { return major.ToString(); } var versionBuilder = new StringBuilder(8); versionBuilder.Append(major); if (withDots) { versionBuilder.Append('.'); } versionBuilder.Append(minor); if (patch != 0) { if (withDots) { versionBuilder.Append('.'); } versionBuilder.Append(patch); } return versionBuilder.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/TargetServices.cs ================================================ // Copyright (c) 2020 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; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// Helper services for determining the target framework and platform of a module. /// public static class TargetServices { const string VersionToken = "Version="; const string ProfileToken = "Profile="; /// /// Gets the for the specified . /// /// The module to get the target framework description for. Cannot be null. /// A new instance of the class that describes the specified . /// public static TargetFramework DetectTargetFramework(MetadataFile module) { if (module is null) { throw new ArgumentNullException(nameof(module)); } int versionNumber; switch (module.GetRuntime()) { case TargetRuntime.Net_1_0: versionNumber = 100; break; case TargetRuntime.Net_1_1: versionNumber = 110; break; case TargetRuntime.Net_2_0: versionNumber = 200; break; default: versionNumber = 400; break; } string targetFrameworkIdentifier = null; string targetFrameworkProfile = null; string targetFramework = module.DetectTargetFrameworkId(); if (!string.IsNullOrEmpty(targetFramework)) { string[] frameworkParts = targetFramework.Split(','); targetFrameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith(VersionToken, StringComparison.OrdinalIgnoreCase) && !a.StartsWith(ProfileToken, StringComparison.OrdinalIgnoreCase)); string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith(VersionToken, StringComparison.OrdinalIgnoreCase)); if (frameworkVersion != null && Version.TryParse(frameworkVersion.Substring(VersionToken.Length).Replace("v", ""), out var version)) { versionNumber = version.Major * 100 + version.Minor * 10; if (version.Build > 0) versionNumber += version.Build; } string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith(ProfileToken, StringComparison.OrdinalIgnoreCase)); if (frameworkProfile != null) targetFrameworkProfile = frameworkProfile.Substring(ProfileToken.Length); } return new TargetFramework(targetFrameworkIdentifier, versionNumber, targetFrameworkProfile); } /// /// Gets the string representation (name) of the target platform of the specified . /// /// The module to get the target framework description for. Cannot be null. /// The platform name, e.g. "AnyCPU" or "x86". public static string GetPlatformName(PEFile module) { if (module is null) { throw new ArgumentNullException(nameof(module)); } var headers = module.Reader.PEHeaders; var architecture = headers.CoffHeader.Machine; var characteristics = headers.CoffHeader.Characteristics; var corflags = headers.CorHeader.Flags; switch (architecture) { case Machine.I386: if ((corflags & CorFlags.Prefers32Bit) != 0) return "AnyCPU"; if ((corflags & CorFlags.Requires32Bit) != 0) return "x86"; // According to ECMA-335, II.25.3.3.1 CorFlags.Requires32Bit and Characteristics.Bit32Machine must be in sync // for assemblies containing managed code. However, this is not true for C++/CLI assemblies. if ((corflags & CorFlags.ILOnly) == 0 && (characteristics & Characteristics.Bit32Machine) != 0) return "x86"; return "AnyCPU"; case Machine.Amd64: return "x64"; case Machine.IA64: return "Itanium"; default: return architecture.ToString(); } } static HashSet dotNet30Assemblies = new HashSet(StringComparer.OrdinalIgnoreCase) { "ComSvcConfig, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "infocard, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Transactions.Bridge.Dtc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "PresentationBuildTasks, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationCFFRasterizer, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Classic, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Luna, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Royale, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationUI, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "ReachFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "ServiceModelReg, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "SMSvcHost, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.IdentityModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.IO.Log, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Printing, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel.Install, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Speech, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationClient, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationClientsideProviders, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationProvider, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationTypes, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WindowsFormsIntegration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WsatConfig, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", }; static HashSet dotNet35Assemblies = new HashSet(StringComparer.OrdinalIgnoreCase) { "AddInProcess, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "AddInProcess32, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "AddInUtil, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "DataSvcUtil, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "EdmGen, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Microsoft.Build.Conversion.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Utilities.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Data.Entity.Build.Tasks, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.VisualC.STLCLR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "MSBuild, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Sentinel.v3.5Client, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.AddIn, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.AddIn.Contract, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.DataAnnotations, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Entity.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services.Client, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.DirectoryServices.AccountManagement, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Management.Instrumentation, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Net, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.DynamicData, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.DynamicData.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Web.Entity.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Extensions.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Windows.Presentation, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", }; /// /// Gets exact if is /// public static TargetFramework DetectTargetFrameworkNET20(MetadataFile module, IAssemblyResolver assemblyResolver, TargetFramework targetFramework) { var resolvedAssemblies = new HashSet(); int version = 200; GetFrameworkVersionNET20(module, assemblyResolver, resolvedAssemblies, ref version); return new TargetFramework(targetFramework.Identifier, version, targetFramework.Profile); } static void GetFrameworkVersionNET20(MetadataFile module, IAssemblyResolver assemblyResolver, HashSet resolvedAssemblies, ref int version) { foreach (var r in module.Metadata.AssemblyReferences) { var reference = new AssemblyReference(module, r); if (!resolvedAssemblies.Add(reference.FullName)) continue; if (dotNet30Assemblies.Contains(reference.FullName)) { version = 300; continue; } else if (dotNet35Assemblies.Contains(reference.FullName)) { version = 350; break; } MetadataFile resolvedReference; try { resolvedReference = assemblyResolver.Resolve(reference); } catch (ResolutionException) { resolvedReference = null; } if (resolvedReference == null) continue; resolvedAssemblies.Add(resolvedReference.FullName); GetFrameworkVersionNET20(resolvedReference, assemblyResolver, resolvedAssemblies, ref version); if (version == 350) return; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { /// /// Decompiles an assembly into a visual studio project file. /// public class WholeProjectDecompiler : IProjectInfoProvider { const int maxSegmentLength = 255; #region Settings /// /// Gets the setting this instance uses for decompiling. /// public DecompilerSettings Settings { get; } LanguageVersion? languageVersion; public LanguageVersion LanguageVersion { get { return languageVersion ?? Settings.GetMinimumRequiredVersion(); } set { var minVersion = Settings.GetMinimumRequiredVersion(); if (value < minVersion) throw new InvalidOperationException($"The chosen settings require at least {minVersion}." + $" Please change the DecompilerSettings accordingly."); languageVersion = value; } } bool IProjectInfoProvider.CheckForOverflowUnderflow => Settings.CheckForOverflowUnderflow; public IAssemblyResolver AssemblyResolver { get; } public AssemblyReferenceClassifier AssemblyReferenceClassifier { get; } public IDebugInfoProvider DebugInfoProvider { get; } /// /// The MSBuild ProjectGuid to use for the new project. /// public Guid ProjectGuid { get; } /// /// The target directory that the decompiled files are written to. /// /// /// This property is set by DecompileProject() and protected so that overridden protected members /// can access it. /// public string TargetDirectory { get; protected set; } /// /// Path to the snk file to use for signing. /// null to not sign. /// public string StrongNameKeyFile { get; set; } public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount; public IProgress ProgressIndicator { get; set; } #endregion public WholeProjectDecompiler(IAssemblyResolver assemblyResolver) : this(new DecompilerSettings(), assemblyResolver, projectWriter: null, assemblyReferenceClassifier: null, debugInfoProvider: null) { } public WholeProjectDecompiler( DecompilerSettings settings, IAssemblyResolver assemblyResolver, IProjectFileWriter projectWriter, AssemblyReferenceClassifier assemblyReferenceClassifier, IDebugInfoProvider debugInfoProvider) : this(settings, Guid.NewGuid(), assemblyResolver, projectWriter, assemblyReferenceClassifier, debugInfoProvider) { } protected WholeProjectDecompiler( DecompilerSettings settings, Guid projectGuid, IAssemblyResolver assemblyResolver, IProjectFileWriter projectWriter, AssemblyReferenceClassifier assemblyReferenceClassifier, IDebugInfoProvider debugInfoProvider) { Settings = settings ?? throw new ArgumentNullException(nameof(settings)); ProjectGuid = projectGuid; AssemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver)); AssemblyReferenceClassifier = assemblyReferenceClassifier ?? new AssemblyReferenceClassifier(); DebugInfoProvider = debugInfoProvider; this.projectWriter = projectWriter ?? (Settings.UseSdkStyleProjectFormat ? ProjectFileWriterSdkStyle.Create() : ProjectFileWriterDefault.Create()); } // per-run members HashSet directories = new HashSet(Platform.FileNameComparer); readonly IProjectFileWriter projectWriter; public void DecompileProject(MetadataFile file, string targetDirectory, CancellationToken cancellationToken = default(CancellationToken)) { string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name, ".csproj")); using (var writer = CreateFile(projectFileName)) { DecompileProject(file, targetDirectory, writer, cancellationToken); } } public ProjectId DecompileProject(MetadataFile file, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(targetDirectory)) { throw new InvalidOperationException("Must set TargetDirectory"); } TargetDirectory = targetDirectory; directories.Clear(); var resources = WriteResourceFilesInProject(file).ToList(); var files = WriteCodeFilesInProject(file, resources.SelectMany(r => r.PartialTypes ?? Enumerable.Empty()).ToList(), cancellationToken).ToList(); files.AddRange(resources); var module = file as PEFile; if (module != null) { files.AddRange(WriteMiscellaneousFilesInProject(module)); } if (StrongNameKeyFile != null) { File.Copy(StrongNameKeyFile, Path.Combine(targetDirectory, Path.GetFileName(StrongNameKeyFile)), overwrite: true); } projectWriter.Write(projectFileWriter, this, files, file); string platformName = module != null ? TargetServices.GetPlatformName(module) : "AnyCPU"; return new ProjectId(platformName, ProjectGuid, ProjectTypeGuids.CSharpWindows); } #region WriteCodeFilesInProject protected virtual bool IncludeTypeWhenDecompilingProject(MetadataFile module, TypeDefinitionHandle type) { var metadata = module.Metadata; var typeDef = metadata.GetTypeDefinition(type); string name = metadata.GetString(typeDef.Name); string ns = metadata.GetString(typeDef.Namespace); if (name == "" || CSharpDecompiler.MemberIsHidden(module, type, Settings)) return false; if (ns == "XamlGeneratedNamespace" && name == "GeneratedInternalTypeHelper") return false; if (!typeDef.IsNested && RemoveEmbeddedAttributes.attributeNames.Contains(ns + "." + name)) return false; return true; } protected virtual TextWriter CreateFile(string path) { return new StreamWriter(path); } protected virtual void CreateDirectory(string path) { try { Directory.CreateDirectory(path); } catch (IOException) { File.Delete(path); Directory.CreateDirectory(path); } } protected virtual CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) { var decompiler = new CSharpDecompiler(ts, Settings); decompiler.DebugInfoProvider = DebugInfoProvider; decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); decompiler.AstTransforms.Add(new RemoveCLSCompliantAttribute()); return decompiler; } IEnumerable WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken) { var decompiler = CreateDecompiler(ts); decompiler.CancellationToken = cancellationToken; decompiler.AstTransforms.Add(new RemoveCompilerGeneratedAssemblyAttributes()); SyntaxTree syntaxTree = decompiler.DecompileModuleAndAssemblyAttributes(); const string prop = "Properties"; if (directories.Add(prop)) CreateDirectory(Path.Combine(TargetDirectory, prop)); string assemblyInfo = Path.Combine(prop, "AssemblyInfo.cs"); using (var w = CreateFile(Path.Combine(TargetDirectory, assemblyInfo))) { syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); } return new[] { new ProjectItemInfo("Compile", assemblyInfo) }; } IEnumerable WriteCodeFilesInProject(MetadataFile module, IList partialTypes, CancellationToken cancellationToken) { var metadata = module.Metadata; var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td)) .GroupBy(GetFileFileNameForHandle, StringComparer.OrdinalIgnoreCase).ToList(); var progressReporter = ProgressIndicator; var progress = new DecompilationProgress { TotalUnits = files.Count, Title = "Exporting project..." }; DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings); var workList = new HashSet(); var processedTypes = new HashSet(); ProcessFiles(files); while (workList.Count > 0) { var additionalFiles = workList .GroupBy(GetFileFileNameForHandle, StringComparer.OrdinalIgnoreCase).ToList(); workList.Clear(); ProcessFiles(additionalFiles); files.AddRange(additionalFiles); progress.TotalUnits = files.Count; } return files.Select(f => new ProjectItemInfo("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); string GetFileFileNameForHandle(TypeDefinitionHandle h) { var type = metadata.GetTypeDefinition(h); string file = CleanUpFileName(metadata.GetString(type.Name), ".cs"); string ns = metadata.GetString(type.Namespace); if (string.IsNullOrEmpty(ns)) { return file; } else { string dir = Settings.UseNestedDirectoriesForNamespaces ? CleanUpPath(ns) : CleanUpDirectoryName(ns); if (directories.Add(dir)) { var path = Path.Combine(TargetDirectory, dir); CreateDirectory(path); } return Path.Combine(dir, file); } } void ProcessFiles(List> files) { processedTypes.AddRange(files.SelectMany(f => f)); Parallel.ForEach( Partitioner.Create(files, loadBalance: true), new ParallelOptions { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, CancellationToken = cancellationToken }, delegate (IGrouping file) { try { using var w = CreateFile(Path.Combine(TargetDirectory, file.Key)); CSharpDecompiler decompiler = CreateDecompiler(ts); foreach (var partialType in partialTypes) { decompiler.AddPartialTypeDefinition(partialType); } decompiler.CancellationToken = cancellationToken; var declaredTypes = file.ToArray(); var syntaxTree = decompiler.DecompileTypes(declaredTypes); foreach (var node in syntaxTree.Descendants) { var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition(); if (td?.ParentModule != ts.MainModule) continue; while (td?.DeclaringTypeDefinition != null) { td = td.DeclaringTypeDefinition; } if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token)) { lock (workList) { workList.Add((TypeDefinitionHandle)token); } } } syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException); } progress.Status = file.Key; Interlocked.Increment(ref progress.UnitsCompleted); progressReporter?.Report(progress); }); } } #endregion #region WriteResourceFilesInProject protected virtual IEnumerable WriteResourceFilesInProject(MetadataFile module) { foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded)) { Stream stream = r.TryOpenStream(); if (stream == null) continue; stream.Position = 0; if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { bool decodedIntoIndividualFiles; var individualResources = new List(); try { var resourcesFile = new ResourcesFile(stream); if (resourcesFile.AllEntriesAreStreams()) { foreach (var (name, value) in resourcesFile) { string fileName = SanitizeFileName(name); string dirName = Path.GetDirectoryName(fileName); if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) { CreateDirectory(Path.Combine(TargetDirectory, dirName)); } Stream entryStream = (Stream)value; entryStream.Position = 0; individualResources.AddRange( WriteResourceToFile(fileName, name, entryStream)); } decodedIntoIndividualFiles = true; } else { decodedIntoIndividualFiles = false; } } catch (BadImageFormatException) { decodedIntoIndividualFiles = false; } catch (EndOfStreamException) { decodedIntoIndividualFiles = false; } if (decodedIntoIndividualFiles) { foreach (var entry in individualResources) { yield return entry; } } else { stream.Position = 0; string fileName = GetFileNameForResource(r.Name); foreach (var entry in WriteResourceToFile(fileName, r.Name, stream)) { yield return entry; } } } else { string fileName = GetFileNameForResource(r.Name); using (FileStream fs = new FileStream(Path.Combine(TargetDirectory, fileName), FileMode.Create, FileAccess.Write)) { stream.Position = 0; stream.CopyTo(fs); } yield return new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", r.Name); } } } protected virtual IEnumerable WriteResourceToFile(string fileName, string resourceName, Stream entryStream) { if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { string resx = Path.ChangeExtension(fileName, ".resx"); try { using (FileStream fs = new FileStream(Path.Combine(TargetDirectory, resx), FileMode.Create, FileAccess.Write)) using (ResXResourceWriter writer = new ResXResourceWriter(fs)) { foreach (var entry in new ResourcesFile(entryStream)) { writer.AddResource(entry.Key, entry.Value); } } return new[] { new ProjectItemInfo("EmbeddedResource", resx).With("LogicalName", resourceName) }; } catch (BadImageFormatException) { // if the .resources can't be decoded, just save them as-is } catch (EndOfStreamException) { // if the .resources can't be decoded, just save them as-is } } using (FileStream fs = new FileStream(Path.Combine(TargetDirectory, fileName), FileMode.Create, FileAccess.Write)) { entryStream.CopyTo(fs); } return new[] { new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", resourceName) }; } string GetFileNameForResource(string fullName) { // Clean up the name first and ensure the length does not exceed the maximum length // supported by the OS. fullName = SanitizeFileName(fullName); // The purpose of the below algorithm is to "maximize" the directory name and "minimize" the file name. // That is, a full name of the form "Namespace1.Namespace2{...}.NamespaceN.ResourceName" is split such that // the directory part Namespace1\Namespace2\... reuses as many existing directories as // possible, and only the remaining name parts are used as prefix for the filename. // This is not affected by the UseNestedDirectoriesForNamespaces setting. string[] splitName = fullName.Split('\\', '/'); string fileName = string.Join(".", splitName); string separator = Path.DirectorySeparatorChar.ToString(); for (int i = splitName.Length - 1; i > 0; i--) { string ns = string.Join(separator, splitName, 0, i); if (directories.Contains(ns)) { string name = string.Join(".", splitName, i, splitName.Length - i); fileName = Path.Combine(ns, name); break; } } return fileName; } #endregion #region WriteMiscellaneousFilesInProject protected virtual IEnumerable WriteMiscellaneousFilesInProject(PEFile module) { var resources = module.Reader.ReadWin32Resources(); if (resources == null) yield break; byte[] appIcon = CreateApplicationIcon(resources); if (appIcon != null) { File.WriteAllBytes(Path.Combine(TargetDirectory, "app.ico"), appIcon); yield return new ProjectItemInfo("ApplicationIcon", "app.ico"); } byte[] appManifest = CreateApplicationManifest(resources); if (appManifest != null && !IsDefaultApplicationManifest(appManifest)) { File.WriteAllBytes(Path.Combine(TargetDirectory, "app.manifest"), appManifest); yield return new ProjectItemInfo("ApplicationManifest", "app.manifest"); } var appConfig = module.FileName + ".config"; if (File.Exists(appConfig)) { File.Copy(appConfig, Path.Combine(TargetDirectory, "app.config"), overwrite: true); yield return new ProjectItemInfo("ApplicationConfig", Path.GetFileName(appConfig)); } } const int RT_ICON = 3; const int RT_GROUP_ICON = 14; unsafe static byte[] CreateApplicationIcon(Win32ResourceDirectory resources) { var iconGroup = resources.Find(new Win32ResourceName(RT_GROUP_ICON))?.FirstDirectory()?.FirstData()?.Data; if (iconGroup == null) return null; var iconDir = resources.Find(new Win32ResourceName(RT_ICON)); if (iconDir == null) return null; using var outStream = new MemoryStream(); using var writer = new BinaryWriter(outStream); fixed (byte* pIconGroupData = iconGroup) { var pIconGroup = (GRPICONDIR*)pIconGroupData; writer.Write(pIconGroup->idReserved); writer.Write(pIconGroup->idType); writer.Write(pIconGroup->idCount); int iconCount = pIconGroup->idCount; uint offset = (2 * 3) + ((uint)iconCount * 0x10); for (int i = 0; i < iconCount; i++) { var pIconEntry = pIconGroup->idEntries + i; writer.Write(pIconEntry->bWidth); writer.Write(pIconEntry->bHeight); writer.Write(pIconEntry->bColorCount); writer.Write(pIconEntry->bReserved); writer.Write(pIconEntry->wPlanes); writer.Write(pIconEntry->wBitCount); writer.Write(pIconEntry->dwBytesInRes); writer.Write(offset); offset += pIconEntry->dwBytesInRes; } for (int i = 0; i < iconCount; i++) { var icon = iconDir.FindDirectory(new Win32ResourceName(pIconGroup->idEntries[i].nID))?.FirstData()?.Data; if (icon == null) return null; writer.Write(icon); } } return outStream.ToArray(); } [StructLayout(LayoutKind.Sequential, Pack = 2)] unsafe struct GRPICONDIR { public ushort idReserved; public ushort idType; public ushort idCount; private fixed byte _idEntries[1]; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] public GRPICONDIRENTRY* idEntries { get { fixed (byte* p = _idEntries) return (GRPICONDIRENTRY*)p; } } }; [StructLayout(LayoutKind.Sequential, Pack = 2)] struct GRPICONDIRENTRY { public byte bWidth; public byte bHeight; public byte bColorCount; public byte bReserved; public ushort wPlanes; public ushort wBitCount; public uint dwBytesInRes; public short nID; }; const int RT_MANIFEST = 24; unsafe static byte[] CreateApplicationManifest(Win32ResourceDirectory resources) { return resources.Find(new Win32ResourceName(RT_MANIFEST))?.FirstDirectory()?.FirstData()?.Data; } static bool IsDefaultApplicationManifest(byte[] appManifest) { const string DEFAULT_APPMANIFEST = ""; string s = CleanUpApplicationManifest(appManifest); return s == DEFAULT_APPMANIFEST; } static string CleanUpApplicationManifest(byte[] appManifest) { bool bom = appManifest.Length >= 3 && appManifest[0] == 0xEF && appManifest[1] == 0xBB && appManifest[2] == 0xBF; string s = Encoding.UTF8.GetString(appManifest, bom ? 3 : 0, appManifest.Length - (bom ? 3 : 0)); var sb = new StringBuilder(s.Length); for (int i = 0; i < s.Length; i++) { char c = s[i]; switch (c) { case '\t': case '\n': case '\r': case ' ': continue; } sb.Append(c); } return sb.ToString(); } #endregion /// /// Cleans up a node name for use as a file name. /// public static string CleanUpFileName(string text, string extension) { Debug.Assert(!string.IsNullOrEmpty(extension)); if (!extension.StartsWith(".")) extension = "." + extension; text = text + extension; return CleanUpName(text, separateAtDots: false, treatAsFileName: !string.IsNullOrEmpty(extension), treatAsPath: false); } /// /// Removes invalid characters from file names and reduces their length, /// but keeps file extensions and path structure intact. /// public static string SanitizeFileName(string fileName) { return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true, treatAsPath: true); } /// /// Cleans up a node name for use as a file system name. If is active, /// dots are seen as segment separators. Each segment is limited to maxSegmentLength characters. /// If is active, we check for file a extension and try to preserve it, /// if it's valid. /// static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName, bool treatAsPath) { string extension = null; int currentSegmentLength = 0; // Extract extension from the end of the name, if valid if (treatAsFileName) { // Check if input is a file name, i.e., has a valid extension // If yes, preserve extension and append it at the end. // But only, if the extension length does not exceed maxSegmentLength, // if that's the case we just give up and treat the extension no different // from the file name. int lastDot = text.LastIndexOf('.'); if (lastDot >= 0 && text.Length - lastDot < maxSegmentLength) { string originalText = text; extension = text.Substring(lastDot); text = text.Remove(lastDot); foreach (var c in extension) { if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_' || c == '.')) { // extension contains an invalid character, therefore cannot be a valid extension. extension = null; text = originalText; break; } } } } // Remove anything that could be confused with a rooted path. int pos = text.IndexOf(':'); if (pos > 0) text = text.Substring(0, pos); text = text.Trim(); // Remove generics pos = text.IndexOf('`'); if (pos > 0) { text = text.Substring(0, pos).Trim(); } // Whitelist allowed characters, replace everything else: StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0)); bool countBytes = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); foreach (var c in text) { if (char.IsLetterOrDigit(c) || c == '-' || c == '_') { unsafe { currentSegmentLength += countBytes ? Encoding.UTF8.GetByteCount(&c, 1) : 1; } // if the current segment exceeds maxSegmentLength characters, // skip until the end of the segment. if (currentSegmentLength <= maxSegmentLength) b.Append(c); } else if (c == '.' && b.Length > 0 && b[^1] != '.') { currentSegmentLength++; // if the current segment exceeds maxSegmentLength characters, // skip until the end of the segment. if (separateAtDots || currentSegmentLength <= maxSegmentLength) b.Append('.'); // allow dot, but never two in a row // Reset length at end of segment. if (separateAtDots) currentSegmentLength = 0; } else if (treatAsPath && (c is '/' or '\\') && currentSegmentLength > 0) { // if we treat this as a file name, we've started a new segment b.Append(Path.DirectorySeparatorChar); currentSegmentLength = 0; } else { if (char.IsHighSurrogate(c)) { // only add one replacement character for surrogate pairs continue; } currentSegmentLength++; // if the current segment exceeds maxSegmentLength characters, // skip until the end of the segment. if (currentSegmentLength <= maxSegmentLength) b.Append('-'); } } if (b.Length == 0) b.Append('-'); string name = b.ToString(); if (extension != null) { // make sure that adding the extension to the filename // does not exceed maxSegmentLength. // trim the name, if necessary. if (name.Length + extension.Length > maxSegmentLength) name = name.Remove(name.Length - extension.Length); name += extension; } if (IsReservedFileSystemName(name)) return name + "_"; else if (name == ".") return "_"; else return name; } /// /// Cleans up a node name for use as a directory name. /// public static string CleanUpDirectoryName(string text) { return CleanUpName(text, separateAtDots: false, treatAsFileName: false, treatAsPath: false); } public static string CleanUpPath(string text) { return CleanUpName(text, separateAtDots: true, treatAsFileName: false, treatAsPath: true) .Replace('.', Path.DirectorySeparatorChar); } static bool IsReservedFileSystemName(string name) { switch (name.ToUpperInvariant()) { case "AUX": case "COM1": case "COM2": case "COM3": case "COM4": case "COM5": case "COM6": case "COM7": case "COM8": case "COM9": case "CON": case "LPT1": case "LPT2": case "LPT3": case "LPT4": case "LPT5": case "LPT6": case "LPT7": case "LPT8": case "LPT9": case "NUL": case "PRN": return true; default: return false; } } public static bool CanUseSdkStyleProjectFormat(MetadataFile module) { return TargetServices.DetectTargetFramework(module).Moniker != null; } } public record struct ProjectItemInfo(string ItemType, string FileName) { public List PartialTypes { get; set; } = null; public Dictionary AdditionalProperties { get; set; } = null; public ProjectItemInfo With(string name, string value) { AdditionalProperties ??= new Dictionary(); AdditionalProperties.Add(name, value); return this; } public ProjectItemInfo With(IEnumerable> pairs) { AdditionalProperties ??= new Dictionary(); AdditionalProperties.AddRange(pairs); return this; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { class RecordDecompiler { readonly IDecompilerTypeSystem typeSystem; readonly ITypeDefinition recordTypeDef; readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; readonly List orderedMembers; readonly bool isInheritedRecord; readonly bool isStruct; readonly bool isSealed; readonly IMethod? primaryCtor; readonly IType? baseClass; readonly Dictionary backingFieldToAutoProperty = new Dictionary(); readonly Dictionary autoPropertyToBackingField = new Dictionary(); readonly Dictionary primaryCtorParameterToAutoProperty = new Dictionary(); readonly Dictionary autoPropertyToPrimaryCtorParameter = new Dictionary(); public RecordDecompiler(IDecompilerTypeSystem dts, ITypeDefinition recordTypeDef, DecompilerSettings settings, CancellationToken cancellationToken) { this.typeSystem = dts; this.recordTypeDef = recordTypeDef; this.settings = settings; this.cancellationToken = cancellationToken; this.baseClass = recordTypeDef.DirectBaseTypes.FirstOrDefault(b => b.Kind == TypeKind.Class); this.isStruct = baseClass?.IsKnownType(KnownTypeCode.ValueType) ?? false; this.isInheritedRecord = !isStruct && !(baseClass?.IsKnownType(KnownTypeCode.Object) ?? false); this.isSealed = recordTypeDef.IsSealed; DetectAutomaticProperties(); this.orderedMembers = DetectMemberOrder(recordTypeDef, backingFieldToAutoProperty); this.primaryCtor = DetectPrimaryConstructor(); } void DetectAutomaticProperties() { var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); foreach (var property in recordTypeDef.Properties) { cancellationToken.ThrowIfCancellationRequested(); var p = (IProperty)property.Specialize(subst); if (IsAutoProperty(p, out var field)) { backingFieldToAutoProperty.Add(field, p); autoPropertyToBackingField.Add(p, field); } } bool IsAutoProperty(IProperty p, [NotNullWhen(true)] out IField? field) { field = null; if (p.IsStatic) return false; if (p.Parameters.Count != 0) return false; if (p.Getter != null) { if (!IsAutoGetter(p.Getter, out field)) return false; } if (p.Setter != null) { if (!IsAutoSetter(p.Setter, out var field2)) return false; if (field != null) { if (!field.Equals(field2)) return false; } else { field = field2; } } if (field == null) return false; if (!IsRecordType(field.DeclaringType)) return false; return field.Name == $"<{p.Name}>k__BackingField"; } bool IsAutoGetter(IMethod method, [NotNullWhen(true)] out IField? field) { field = null; var body = DecompileBody(method); if (body == null) return false; // return this.field; if (!body.Instructions[0].MatchReturn(out var retVal)) return false; if (method.IsStatic) { return retVal.MatchLdsFld(out field); } else { if (!retVal.MatchLdFld(out var target, out field)) return false; return target.MatchLdThis(); } } bool IsAutoSetter(IMethod method, [NotNullWhen(true)] out IField? field) { field = null; Debug.Assert(!method.IsStatic); var body = DecompileBody(method); if (body == null) return false; // this.field = value; ILInstruction? valueInst; if (method.IsStatic) { if (!body.Instructions[0].MatchStsFld(out field, out valueInst)) return false; } else { if (!body.Instructions[0].MatchStFld(out var target, out field, out valueInst)) return false; if (!target.MatchLdThis()) return false; } if (!valueInst.MatchLdLoc(out var value)) return false; if (!(value.Kind == VariableKind.Parameter && value.Index == 0)) return false; return body.Instructions[1].MatchReturn(out var retVal) && retVal.MatchNop(); } } IMethod? DetectPrimaryConstructor() { Debug.Assert(recordTypeDef.IsRecord); if (!settings.UsePrimaryConstructorSyntax) return null; var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); Dictionary nameToMemberMap = new Dictionary(StringComparer.Ordinal); Dictionary ctorChainMap = new Dictionary(); foreach (IMember m in recordTypeDef.GetMembers(m => m.SymbolKind is SymbolKind.Property or SymbolKind.Field, GetMemberOptions.ReturnMemberDefinitions)) { nameToMemberMap[m.Name] = m; } IMethod? guessedPrimaryCtor = null; foreach (var method in recordTypeDef.Methods) { cancellationToken.ThrowIfCancellationRequested(); if (method.IsStatic || !method.IsConstructor) continue; if (IsCopyConstructor(method)) { continue; } var body = DecompileBody(method); if (body == null) { continue; } IMethod? chainedCtor = (IMethod?)FindChainedCtor(body)?.MemberDefinition; ctorChainMap[method] = chainedCtor; if (chainedCtor != null && chainedCtor.DeclaringTypeDefinition!.Equals(recordTypeDef)) { continue; } var m = method.Specialize(subst); if (IsPrimaryConstructor(body, m, method)) { if (guessedPrimaryCtor == null) { guessedPrimaryCtor = method; } else { // Multiple primary constructor candidates found - give up guessedPrimaryCtor = null; break; } } } if (guessedPrimaryCtor != null) { foreach (var (source, target) in ctorChainMap) { if (guessedPrimaryCtor.Equals(source)) continue; // in a record with a primary ctor every other ctor must call the primary ctor // at the end of the ctor chain. // if the target is null or a ctor of another type, we found a ctor that does not // follow this rule. // we don't have to check the full chain, because the C# compiler enforces that // there are no loops in the ctor call graph. if (target == null || !target.DeclaringTypeDefinition!.Equals(recordTypeDef)) { guessedPrimaryCtor = null; break; } } } if (guessedPrimaryCtor == null) { primaryCtorParameterToAutoProperty.Clear(); } foreach (var (parameter, property) in primaryCtorParameterToAutoProperty.ToArray()) { if (!parameter.Owner!.Equals(guessedPrimaryCtor)) { primaryCtorParameterToAutoProperty.Remove(parameter); } else { autoPropertyToPrimaryCtorParameter.Add(property, parameter); } } return guessedPrimaryCtor; bool IsPrimaryConstructor(Block body, IMethod method, IMethod unspecializedMethod) { foreach (IParameter p in unspecializedMethod.Parameters) { // ref and out are not valid modifiers in this context if (p.ReferenceKind is ReferenceKind.Ref or ReferenceKind.Out) return false; // for each positional parameter there must be a field or property of the same name and type if (!nameToMemberMap.TryGetValue(p.Name, out var member)) return false; var paramType = p.ReferenceKind != ReferenceKind.None ? p.Type.UnwrapByRef() : p.Type; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(paramType, member.ReturnType)) return false; } if (!isStruct) { if (body.Instructions.SecondToLastOrDefault() is not CallInstruction baseCtorCall) return false; var baseCtor = baseCtorCall.Method; if (!baseCtor.IsConstructor) return false; if (baseCtor.DeclaringType.Equals(method.DeclaringType)) return false; } var addonInst = isStruct ? 1 : 2; if (body.Instructions.Count < addonInst) return false; int parameterIndex = 0; for (int i = 0; i < body.Instructions.Count - addonInst; i++) { if (!body.Instructions[i].MatchStFld(out var target, out var field, out var valueInst)) return false; if (!target.MatchLdThis()) return false; // allow assignments to fields that are not backing fields of auto-properties if (!backingFieldToAutoProperty.TryGetValue(field, out var property)) continue; if (valueInst.MatchLdLoc(out var v)) { if (!ValidateParameter(v, parameterIndex)) return false; parameterIndex = v.Index!.Value; } else if (valueInst.MatchLdObj(out valueInst, out _) && valueInst.MatchLdLoc(out v)) { if (!ValidateParameter(v, parameterIndex)) return false; parameterIndex = v.Index!.Value; if (method.Parameters[parameterIndex].ReferenceKind is ReferenceKind.None) { return false; } } else { continue; } IParameter parameter = unspecializedMethod.Parameters[parameterIndex]; if (primaryCtorParameterToAutoProperty.ContainsKey(parameter)) { continue; } if (recordTypeDef.Kind != TypeKind.Struct) { if (!(property.CanSet && property.Setter.IsInitOnly)) { continue; } } primaryCtorParameterToAutoProperty.Add(parameter, property); } var returnInst = body.Instructions.LastOrDefault(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); bool ValidateParameter(ILVariable v, int expectedMinimumIndex) { if (v.Kind != VariableKind.Parameter) return false; Debug.Assert(v.Index.HasValue); if (v.Index < 0 || v.Index >= unspecializedMethod.Parameters.Count) return false; var parameter = unspecializedMethod.Parameters[v.Index.Value]; if (primaryCtorParameterToAutoProperty.ContainsKey(parameter)) return true; return v.Index >= expectedMinimumIndex; } } IMethod? FindChainedCtor(Block body) { // look for a call instruction or assignment to a chained constructor foreach (var inst in body.Instructions) { switch (inst) { case Call { Method: { IsConstructor: true } ctor }: return ctor; case StObj { Value: NewObj { Method: var ctor } } stObj when stObj.Target.MatchLdThis(): return ctor; } } return null; } } static List DetectMemberOrder(ITypeDefinition recordTypeDef, Dictionary backingFieldToAutoProperty) { // For records, the order of members is important: // Equals/GetHashCode/PrintMembers must agree on an order of fields+properties. // The IL metadata has the order of fields and the order of properties, but we // need to detect the correct interleaving. // We could try to detect this from the PrintMembers body, but let's initially // restrict ourselves to the common case where the record only uses properties. var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); return recordTypeDef.Properties.Select(p => p.Specialize(subst)).Concat( recordTypeDef.Fields.Select(f => (IField)f.Specialize(subst)).Where(f => !backingFieldToAutoProperty.ContainsKey(f)) ).ToList(); } /// /// Gets the fields and properties of the record type, interleaved as necessary to /// maintain Equals/ToString/etc. semantics. /// public IEnumerable FieldsAndProperties => orderedMembers; /// /// Gets the detected primary constructor. Returns null, if there was no primary constructor detected. /// public IMethod? PrimaryConstructor => primaryCtor; public bool IsInheritedRecord => isInheritedRecord; bool IsRecordType(IType type) { return type.GetDefinition() == recordTypeDef && type.TypeArguments.SequenceEqual(recordTypeDef.TypeParameters); } /// /// Gets whether the member of the record type will be automatically generated by the compiler. /// public bool MethodIsGenerated(IMethod method) { if (IsCopyConstructor(method)) { return IsGeneratedCopyConstructor(method); } switch (method.Name) { // Some members in records are always compiler-generated and lead to a // "duplicate definition" error if we emit the generated code. case "op_Equality": case "op_Inequality": { // Don't emit comparison operators into C# record definition // Note: user can declare additional operator== as long as they have // different parameter types. return method.Parameters.Count == 2 && method.Parameters.All(p => IsRecordType(p.Type)); } case "Equals" when method.Parameters.Count == 1: { IType paramType = method.Parameters[0].Type; if (paramType.IsKnownType(KnownTypeCode.Object) && method.IsOverride) { // override bool Equals(object? obj): always generated return true; } else if (IsRecordType(paramType)) { // virtual bool Equals(R? other): generated unless user-declared return IsGeneratedEquals(method); } else if (isInheritedRecord && baseClass != null && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(paramType, baseClass) && method.IsOverride) { // override bool Equals(BaseClass? obj): always generated return true; } else { return false; } } case "GetHashCode": return IsGeneratedGetHashCode(method); case "$" when method.Parameters.Count == 0: // Always generated; Method name cannot be expressed in C# return true; case "PrintMembers": return IsGeneratedPrintMembers(method); case "ToString" when method.Parameters.Count == 0: return IsGeneratedToString(method); case "Deconstruct" when primaryCtor != null && method.Parameters.Count == primaryCtor.Parameters.Count: return IsGeneratedDeconstruct(method); default: return false; } } internal bool PropertyIsGenerated(IProperty property) { if (!recordTypeDef.IsRecord) return false; switch (property.Name) { case "EqualityContract" when !isStruct: return IsGeneratedEqualityContract(property); default: return IsPropertyDeclaredByPrimaryConstructor(property); } } public bool IsPropertyDeclaredByPrimaryConstructor(IProperty property) { var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); return primaryCtor != null && autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); } internal Dictionary GetParameterToBackingStoreMap() { var result = new Dictionary(); foreach (var (parameter, property) in primaryCtorParameterToAutoProperty) { result.Add(parameter, (property, autoPropertyToBackingField[property])); } return result; } public bool IsCopyConstructor(IMethod method) { if (method == null) return false; Debug.Assert(method.DeclaringTypeDefinition == recordTypeDef); return method.IsConstructor && method.Parameters.Count == 1 && (recordTypeDef.IsSealed ? (method.Accessibility == Accessibility.Private) : (method.Accessibility == Accessibility.Protected)) && IsRecordType(method.Parameters[0].Type); } private bool IsAllowedAttribute(IAttribute attribute) { switch (attribute.AttributeType.ReflectionName) { case "System.Runtime.CompilerServices.CompilerGeneratedAttribute": return true; default: return false; } } private bool IsGeneratedCopyConstructor(IMethod method) { /* call BaseClass..ctor(ldloc this, ldloc original) stfld k__BackingField(ldloc this, ldfld k__BackingField(ldloc original)) leave IL_0000 (nop) */ Debug.Assert(method.IsConstructor && method.Parameters.Count == 1); if (method.GetAttributes().Any(attr => !IsAllowedAttribute(attr)) || method.GetReturnTypeAttributes().Any()) return false; if (method.Accessibility != Accessibility.Protected && (!isSealed || method.Accessibility != Accessibility.Private)) return false; if (orderedMembers == null) return false; var body = DecompileBody(method); if (body == null) return false; var variables = body.Ancestors.OfType().Single().Variables; var other = variables.Single(v => v.Kind == VariableKind.Parameter && v.Index == 0); Debug.Assert(IsRecordType(other.Type)); int pos = 0; // First instruction is the base constructor call if (!(body.Instructions[pos] is Call { Method: { IsConstructor: true } } baseCtorCall)) return false; if (!object.Equals(baseCtorCall.Method.DeclaringType, baseClass)) return false; if (baseCtorCall.Arguments.Count != (isInheritedRecord ? 2 : 1)) return false; if (!baseCtorCall.Arguments[0].MatchLdThis()) return false; if (isInheritedRecord) { if (!baseCtorCall.Arguments[1].MatchLdLoc(other)) return false; } pos++; // Then all the fields are copied over foreach (var member in orderedMembers) { if (member.IsStatic) continue; if (member is not IField field) { if (!autoPropertyToBackingField.TryGetValue((IProperty)member, out field!)) continue; } if (pos >= body.Instructions.Count) return false; if (!body.Instructions[pos].MatchStFld(out var lhsTarget, out var lhsField, out var valueInst)) return false; if (!lhsTarget.MatchLdThis()) return false; if (!lhsField.Equals(field)) return false; if (!valueInst.MatchLdFld(out var rhsTarget, out var rhsField)) return false; if (!rhsTarget.MatchLdLoc(other)) return false; if (!rhsField.Equals(field)) return false; pos++; } return body.Instructions[pos] is Leave; } private bool IsGeneratedEqualityContract(IProperty property) { // Generated member: // protected virtual Type EqualityContract { // [CompilerGenerated] get => typeof(R); // } Debug.Assert(!isStruct && property.Name == "EqualityContract"); if (property.Accessibility != Accessibility.Protected && (!isSealed || property.Accessibility != Accessibility.Private)) return false; if (!(isSealed || property.IsVirtual || property.IsOverride)) return false; if (property.IsSealed) return false; var getter = property.Getter; if (!(getter != null && !property.CanSet)) return false; var attrs = property.GetAttributes().ToList(); switch (attrs.Count) { case 0: // Roslyn 3.x does not emit a CompilerGeneratedAttribute on the property itself. break; case 1: // Roslyn 4.4 started doing so. if (!attrs[0].AttributeType.IsKnownType(KnownAttribute.CompilerGenerated)) return false; break; default: return false; } if (getter.GetReturnTypeAttributes().Any()) return false; attrs = getter.GetAttributes().ToList(); if (attrs.Count != 1) return false; if (!attrs[0].AttributeType.IsKnownType(KnownAttribute.CompilerGenerated)) return false; var body = DecompileBody(getter); if (body == null || body.Instructions.Count != 1) return false; if (!(body.Instructions.Single() is Leave leave)) return false; // leave IL_0000 (call GetTypeFromHandle(ldtypetoken R)) if (!TransformExpressionTrees.MatchGetTypeFromHandle(leave.Value, out IType ty)) return false; return IsRecordType(ty); } private bool IsGeneratedPrintMembers(IMethod method) { Debug.Assert(method.Name == "PrintMembers"); if (method.Parameters.Count != 1) return false; if (!isSealed && !method.IsOverridable) return false; if (method.GetAttributes().Any(attr => !IsAllowedAttribute(attr)) || method.GetReturnTypeAttributes().Any()) return false; if (method.Accessibility != Accessibility.Protected && (!isSealed || method.Accessibility != Accessibility.Private)) return false; if (orderedMembers == null) return false; var body = DecompileBody(method); if (body == null) return false; var variables = body.Ancestors.OfType().Single().Variables; var builder = variables.Single(v => v.Kind == VariableKind.Parameter && v.Index == 0); if (builder.Type.ReflectionName != "System.Text.StringBuilder") return false; int pos = 0; //Roslyn 4.0.0-3.final start to insert an call to RuntimeHelpers.EnsureSufficientExecutionStack() if (!isStruct && !isInheritedRecord && body.Instructions[pos] is Call { Arguments: { Count: 0 }, Method: { Name: "EnsureSufficientExecutionStack", DeclaringType: { Namespace: "System.Runtime.CompilerServices", Name: "RuntimeHelpers" } } }) { pos++; } if (isInheritedRecord) { // Special case: inherited record adding no new members if (body.Instructions[pos].MatchReturn(out var returnValue) && IsBaseCall(returnValue) && !orderedMembers.Any(IsPrintedMember)) { return true; } // if (call PrintMembers(ldloc this, ldloc builder)) Block IL_000f { // callvirt Append(ldloc builder, ldstr ", ") // } if (!body.Instructions[pos].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!IsBaseCall(condition)) return false; // trueInst = callvirt Append(ldloc builder, ldstr ", ") trueInst = Block.Unwrap(trueInst); if (!MatchStringBuilderAppend(trueInst, builder, out var val)) return false; if (!(val.MatchLdStr(out string? text) && text == ", ")) return false; pos++; bool IsBaseCall(ILInstruction inst) { if (!(inst is CallInstruction { Method: { Name: "PrintMembers" } } call)) return false; if (call.Arguments.Count != 2) return false; if (!call.Arguments[0].MatchLdThis()) return false; if (!call.Arguments[1].MatchLdLoc(builder)) return false; return true; } } bool needsComma = false; foreach (var member in orderedMembers) { if (!IsPrintedMember(member)) continue; cancellationToken.ThrowIfCancellationRequested(); /* callvirt Append(ldloc builder, ldstr "A") callvirt Append(ldloc builder, ldstr " = ") callvirt Append(ldloc builder, constrained[System.Int32].callvirt ToString(addressof System.Int32(call get_A(ldloc this)))) callvirt Append(ldloc builder, ldstr ", ") callvirt Append(ldloc builder, ldstr "B") callvirt Append(ldloc builder, ldstr " = ") callvirt Append(ldloc builder, constrained[System.Int32].callvirt ToString(ldflda B(ldloc this))) leave IL_0000 (ldc.i4 1) */ if (!MatchStringBuilderAppendConstant(out string? text)) return false; string expectedText = (needsComma ? ", " : "") + member.Name + " = "; if (text != expectedText) return false; if (!MatchStringBuilderAppend(body.Instructions[pos], builder, out var val)) return false; if (val is CallInstruction { Method: { Name: "ToString", IsStatic: false } } toStringCall) { if (toStringCall.Arguments.Count != 1) return false; val = toStringCall.Arguments[0]; if (val is AddressOf addressOf) { val = addressOf.Value; } } else if (val is Box box) { if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(box.Type, member.ReturnType)) return false; val = box.Argument; } if (val is CallInstruction getterCall && member is IProperty property) { if (!getterCall.Method.Equals(property.Getter)) return false; if (getterCall.Arguments.Count != 1) return false; if (!getterCall.Arguments[0].MatchLdThis()) return false; } else if (val.MatchLdFld(out var target, out var field) || val.MatchLdFlda(out target, out field)) { if (!target.MatchLdThis()) return false; if (!field.Equals(member)) return false; } else { return false; } pos++; needsComma = true; } // leave IL_0000 (ldc.i4 1) return body.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(needsComma ? 1 : 0); bool IsPrintedMember(IMember member) { if (member.IsStatic) { return false; // static fields/properties are not printed } if (member.Accessibility != Accessibility.Public) { return false; // non-public fields/properties are not printed } if (!isStruct && member.Name == "EqualityContract") { return false; // EqualityContract is never printed } if (member.IsExplicitInterfaceImplementation) { return false; // explicit interface impls are not printed } if (member.IsOverride) { return false; // override is not printed (again), the virtual base property was already printed } return true; } bool MatchStringBuilderAppendConstant([NotNullWhen(true)] out string? text) { text = null; while (MatchStringBuilderAppend(body.Instructions[pos], builder, out var val) && val.MatchLdStr(out string? valText)) { text += valText; pos++; } return text != null; } } private bool MatchStringBuilderAppend(ILInstruction inst, ILVariable sb, [NotNullWhen(true)] out ILInstruction? val) { val = null; if (!(inst is CallVirt { Method: { Name: "Append", DeclaringType: { Namespace: "System.Text", Name: "StringBuilder" } } } call)) return false; if (call.Arguments.Count != 2) return false; if (!call.Arguments[0].MatchLdLoc(sb)) return false; val = call.Arguments[1]; return true; } private bool IsGeneratedToString(IMethod method) { Debug.Assert(method.Name == "ToString" && method.Parameters.Count == 0); if (!method.IsOverride) return false; if (method.IsSealed) return false; if (method.GetAttributes().Any(attr => !IsAllowedAttribute(attr)) || method.GetReturnTypeAttributes().Any()) return false; var body = DecompileBody(method); if (body == null) return false; // stloc stringBuilder(newobj StringBuilder..ctor()) if (!body.Instructions[0].MatchStLoc(out var stringBuilder, out var stringBuilderInit)) return false; if (!(stringBuilderInit is NewObj { Arguments: { Count: 0 }, Method: { DeclaringTypeDefinition: { Name: "StringBuilder", Namespace: "System.Text" } } })) return false; // callvirt Append(ldloc stringBuilder, ldstr "R") if (!MatchAppendCallWithValue(body.Instructions[1], recordTypeDef.Name)) return false; // callvirt Append(ldloc stringBuilder, ldstr " { ") if (!MatchAppendCallWithValue(body.Instructions[2], " { ")) return false; // if (callvirt PrintMembers(ldloc this, ldloc stringBuilder)) { trueInst } if (!body.Instructions[3].MatchIfInstruction(out var condition, out var trueInst)) return true; if (!((condition is CallInstruction { Method: { Name: "PrintMembers" } } printMembersCall) && (condition is CallVirt || (isSealed && condition is Call)))) return false; if (printMembersCall.Arguments.Count != 2) return false; if (!printMembersCall.Arguments[0].MatchLdThis()) return false; if (!printMembersCall.Arguments[1].MatchLdLoc(stringBuilder)) return false; // trueInst: callvirt Append(ldloc stringBuilder, ldstr " ") if (!MatchAppendCallWithValue(Block.Unwrap(trueInst), " ")) return false; // callvirt Append(ldloc stringBuilder, ldstr "}") if (!MatchAppendCallWithValue(body.Instructions[4], "}")) return false; // leave IL_0000 (callvirt ToString(ldloc stringBuilder)) if (!(body.Instructions[5] is Leave leave)) return false; if (!(leave.Value is CallVirt { Method: { Name: "ToString" } } toStringCall)) return false; if (toStringCall.Arguments.Count != 1) return false; return toStringCall.Arguments[0].MatchLdLoc(stringBuilder); bool MatchAppendCallWithValue(ILInstruction inst, string val) { if (!(inst is CallVirt { Method: { Name: "Append" } } call)) return false; if (call.Arguments.Count != 2) return false; if (!call.Arguments[0].MatchLdLoc(stringBuilder)) return false; //Roslyn 4.0.0-3.final start to use char for 1 length string if (call.Method.Parameters[0].Type.IsKnownType(KnownTypeCode.Char)) { return val != null && val.Length == 1 && call.Arguments[1].MatchLdcI4(val[0]); } return call.Arguments[1].MatchLdStr(out string? val1) && val1 == val; } } private bool IsGeneratedEquals(IMethod method) { // virtual bool Equals(R? other) { // return other != null && EqualityContract == other.EqualityContract && EqualityComparer.Default.Equals(A, other.A) && ...; // } // Starting with Roslyn 3.10, it's: // virtual bool Equals(R? other) { // return this == other || other != null && EqualityContract == other.EqualityContract && EqualityComparer.Default.Equals(A, other.A) && ...; // } Debug.Assert(method.Name == "Equals" && method.Parameters.Count == 1); if (method.Parameters.Count != 1) return false; if (!isSealed && !method.IsOverridable) return false; if (method.GetAttributes().Any(attr => !IsAllowedAttribute(attr)) || method.GetReturnTypeAttributes().Any()) return false; if (orderedMembers == null) return false; var body = DecompileBody(method); if (body == null) return false; if (!body.Instructions[0].MatchReturn(out var returnValue)) return false; // special case for empty record struct; always returns true; if (returnValue.MatchLdcI4(1)) return true; var variables = body.Ancestors.OfType().Single().Variables; var other = variables.Single(v => v.Kind == VariableKind.Parameter && v.Index == 0); Debug.Assert(IsRecordType(other.Type)); if (returnValue.MatchLogicOr(out var lhs, out var rhs)) { // this == other || ... if (!lhs.MatchCompEquals(out var compLeft, out var compRight)) return false; if (!compLeft.MatchLdThis()) return false; if (!compRight.MatchLdLoc(other)) return false; returnValue = rhs; } var conditions = UnpackLogicAndChain(returnValue); Debug.Assert(conditions.Count >= 1); int pos = 0; if (!isStruct) { if (isInheritedRecord) { // call BaseClass::Equals(ldloc this, ldloc other) if (pos >= conditions.Count) return false; if (!(conditions[pos] is Call { Method: { Name: "Equals" } } call)) return false; if (baseClass != null && !NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType, baseClass)) return false; if (call.Arguments.Count != 2) return false; if (!call.Arguments[0].MatchLdThis()) return false; if (!call.Arguments[1].MatchLdLoc(other)) return false; pos++; } else { // comp.o(ldloc other != ldnull) if (pos >= conditions.Count) return false; if (!conditions[pos].MatchCompNotEqualsNull(out var arg)) return false; if (!arg.MatchLdLoc(other)) return false; pos++; // call op_Equality(callvirt get_EqualityContract(ldloc this), callvirt get_EqualityContract(ldloc other)) // Special-cased because Roslyn isn't using EqualityComparer here. if (pos >= conditions.Count) return false; if (!(conditions[pos] is Call { Method: { IsOperator: true, Name: "op_Equality" } } opEqualityCall)) return false; if (!opEqualityCall.Method.DeclaringType.IsKnownType(KnownTypeCode.Type)) return false; if (opEqualityCall.Arguments.Count != 2) return false; if (!MatchGetEqualityContract(opEqualityCall.Arguments[0], out var target1)) return false; if (!MatchGetEqualityContract(opEqualityCall.Arguments[1], out var target2)) return false; if (!target1.MatchLdThis()) return false; if (!target2.MatchLdLoc(other)) return false; pos++; } } foreach (var member in orderedMembers) { if (!MemberConsideredForEquality(member)) continue; if (!isStruct && member.Name == "EqualityContract") { continue; // already special-cased } // EqualityComparer.Default.Equals(A, other.A) // callvirt Equals(call get_Default(), ldfld k__BackingField(ldloc this), ldfld k__BackingField(ldloc other)) if (pos >= conditions.Count) return false; if (!(conditions[pos] is CallVirt { Method: { Name: "Equals" } } equalsCall)) return false; if (equalsCall.Arguments.Count != 3) return false; if (!IsEqualityComparerGetDefaultCall(equalsCall.Arguments[0], member.ReturnType)) return false; if (!MatchMemberAccess(equalsCall.Arguments[1], out var target1, out var member1)) return false; if (!MatchMemberAccess(equalsCall.Arguments[2], out var target2, out var member2)) return false; if (!target1.MatchLdThis()) return false; if (!member1.Equals(member)) return false; if (!(isStruct ? target2.MatchLdLoca(other) : target2.MatchLdLoc(other))) return false; if (!member2.Equals(member)) return false; pos++; } return pos == conditions.Count; } static List UnpackLogicAndChain(ILInstruction rootOfChain) { var result = new List(); Visit(rootOfChain); return result; void Visit(ILInstruction inst) { if (inst.MatchLogicAnd(out var lhs, out var rhs)) { Visit(lhs); Visit(rhs); } else { result.Add(inst); } } } private bool MatchGetEqualityContract(ILInstruction inst, [NotNullWhen(true)] out ILInstruction? target) { target = null; if (!(inst is CallInstruction { Method: { Name: "get_EqualityContract" } } call)) return false; if (!(inst is CallVirt || (isSealed && inst is Call))) return false; if (call.Arguments.Count != 1) return false; target = call.Arguments[0]; return true; } private static bool IsEqualityComparerGetDefaultCall(ILInstruction inst, IType type) { if (!(inst is Call { Method: { Name: "get_Default", IsStatic: true } } call)) return false; if (!(call.Method.DeclaringType is { Name: "EqualityComparer", Namespace: "System.Collections.Generic" })) return false; if (call.Method.DeclaringType.TypeArguments.Count != 1) return false; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType.TypeArguments[0], type)) return false; return call.Arguments.Count == 0; } bool MemberConsideredForEquality(IMember member) { if (member.IsStatic) return false; if (member is IProperty property) { if (!isStruct && property.Name == "EqualityContract") return !isInheritedRecord; return autoPropertyToBackingField.ContainsKey(property); } else { return member is IField; } } bool IsGeneratedGetHashCode(IMethod method) { /* return ( ( EqualityComparer.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer.Default.GetHashCode(A) ) * -1521134295 + EqualityComparer.Default.GetHashCode(B) ) * -1521134295 + EqualityComparer.Default.GetHashCode(C); */ Debug.Assert(method.Name == "GetHashCode"); if (method.Parameters.Count != 0) return false; if (!method.IsOverride || method.IsSealed) return false; if (method.GetAttributes().Any(attr => !IsAllowedAttribute(attr)) || method.GetReturnTypeAttributes().Any()) return false; if (orderedMembers == null) return false; var body = DecompileBody(method); if (body == null) return false; if (!body.Instructions[0].MatchReturn(out var returnValue)) return false; // special case for empty record struct; always returns false; if (returnValue.MatchLdcI4(0)) return true; var hashedMembers = new List(); bool foundBaseClassHash = false; if (!Visit(returnValue)) return false; if (foundBaseClassHash != isInheritedRecord) return false; return orderedMembers.Where(MemberConsideredForEquality).SequenceEqual(hashedMembers); bool Visit(ILInstruction inst) { if (inst is BinaryNumericInstruction { Operator: BinaryNumericOperator.Add, CheckForOverflow: false, Left: BinaryNumericInstruction { Operator: BinaryNumericOperator.Mul, CheckForOverflow: false, Left: var left, Right: LdcI4 { Value: -1521134295 } }, Right: var right }) { if (!Visit(left)) return false; return ProcessIndividualHashCode(right); } else { return ProcessIndividualHashCode(inst); } } bool ProcessIndividualHashCode(ILInstruction inst) { // base.GetHashCode(): call GetHashCode(ldloc this) if (inst is Call { Method: { Name: "GetHashCode" } } baseHashCodeCall) { if (baseHashCodeCall.Arguments.Count != 1) return false; if (!baseHashCodeCall.Arguments[0].MatchLdThis()) return false; if (foundBaseClassHash || hashedMembers.Count > 0) return false; // must be first foundBaseClassHash = true; return baseHashCodeCall.Method.DeclaringType.Equals(baseClass); } // callvirt GetHashCode(call get_Default(), callvirt get_EqualityContract(ldloc this)) // callvirt GetHashCode(call get_Default(), ldfld k__BackingField(ldloc this))) if (!(inst is CallVirt { Method: { Name: "GetHashCode" } } getHashCodeCall)) return false; if (getHashCodeCall.Arguments.Count != 2) return false; // getHashCodeCall.Arguments[0] checked later if (!MatchMemberAccess(getHashCodeCall.Arguments[1], out var target, out var member)) return false; if (!target.MatchLdThis()) return false; if (!IsEqualityComparerGetDefaultCall(getHashCodeCall.Arguments[0], member.ReturnType)) return false; hashedMembers.Add(member); return true; } } bool IsGeneratedDeconstruct(IMethod method) { Debug.Assert(method.Name == "Deconstruct" && method.Parameters.Count == primaryCtor?.Parameters.Count); if (!method.ReturnType.IsKnownType(KnownTypeCode.Void)) return false; for (int i = 0; i < method.Parameters.Count; i++) { var deconstruct = method.Parameters[i]; var ctor = primaryCtor.Parameters[i]; if (deconstruct.ReferenceKind != ReferenceKind.Out) return false; IType ctorType = ctor.Type; if (ctor.ReferenceKind is ReferenceKind.In or ReferenceKind.RefReadOnly) ctorType = ((ByReferenceType)ctorType).ElementType; if (!ctorType.Equals(((ByReferenceType)deconstruct.Type).ElementType)) return false; if (ctor.Name != deconstruct.Name) return false; } var body = DecompileBody(method); if (body == null || body.Instructions.Count != method.Parameters.Count + 1) return false; for (int i = 0; i < body.Instructions.Count - 1; i++) { // stobj T(ldloc parameter, call getter(ldloc this)) if (!body.Instructions[i].MatchStObj(out var targetInst, out var getter, out _)) return false; if (!targetInst.MatchLdLoc(out var target)) return false; if (!(target.Kind == VariableKind.Parameter && target.Index == i)) return false; if (getter is not Call call || call.Arguments.Count != 1) return false; if (!call.Arguments[0].MatchLdThis()) return false; if (!call.Method.IsAccessor) return false; var autoProperty = (IProperty)call.Method.AccessorOwner; if (!autoPropertyToBackingField.ContainsKey(autoProperty)) { if (autoProperty.DeclaringTypeDefinition == recordTypeDef) return false; } } var returnInst = body.Instructions.LastOrDefault(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); } bool MatchMemberAccess(ILInstruction inst, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member) { target = null; member = null; if (inst is CallInstruction { Method: { AccessorKind: System.Reflection.MethodSemanticsAttributes.Getter, AccessorOwner: IProperty property } } call && (call is CallVirt || (isSealed && call is Call))) { if (call.Arguments.Count != 1) return false; target = call.Arguments[0]; member = property; return true; } else if (inst.MatchLdFld(out target, out IField? field)) { if (backingFieldToAutoProperty.TryGetValue(field, out property!)) member = property; else member = field; return true; } else { return false; } } Block? DecompileBody(IMethod method) { if (method == null || method.MetadataToken.IsNil) return null; var metadata = typeSystem.MainModule.metadata; var methodDefHandle = (MethodDefinitionHandle)method.MetadataToken; var methodDef = metadata.GetMethodDefinition(methodDefHandle); if (!methodDef.HasBody()) return null; var genericContext = new GenericContext( classTypeParameters: recordTypeDef.TypeParameters, methodTypeParameters: null); var body = typeSystem.MainModule.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress); var ilReader = new ILReader(typeSystem.MainModule); var il = ilReader.ReadIL(methodDefHandle, body, genericContext, ILFunctionKind.TopLevelFunction, cancellationToken); var settings = new DecompilerSettings(LanguageVersion.CSharp1); var transforms = CSharpDecompiler.GetILTransforms(); // Remove the last couple transforms -- we don't need variable names etc. here int lastBlockTransform = transforms.FindLastIndex(t => t is BlockILTransform); transforms.RemoveRange(lastBlockTransform + 1, transforms.Count - (lastBlockTransform + 1)); // Use CombineExitsTransform so that "return other != null && ...;" is a single statement even in release builds transforms.Add(new CombineExitsTransform()); il.RunTransforms(transforms, new ILTransformContext(il, typeSystem, debugInfo: null, settings) { CancellationToken = cancellationToken }); if (il.Body is BlockContainer container) { return container.EntryPoint; } else if (il.Body is Block block) { return block; } else { return null; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using static ICSharpCode.Decompiler.Metadata.ILOpCodeExtensions; namespace ICSharpCode.Decompiler.CSharp { class RequiredNamespaceCollector { static readonly Decompiler.TypeSystem.GenericContext genericContext = default; readonly HashSet namespaces; readonly HashSet visitedTypes = new HashSet(); public RequiredNamespaceCollector(HashSet namespaces) { this.namespaces = namespaces; for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) { var ktr = KnownTypeReference.Get((KnownTypeCode)i); if (ktr == null) continue; namespaces.Add(ktr.Namespace); } } public static void CollectNamespaces(MetadataModule module, HashSet namespaces) { var collector = new RequiredNamespaceCollector(namespaces); foreach (var type in module.TypeDefinitions) { collector.CollectNamespaces(type, module, (CodeMappingInfo)null); } collector.HandleAttributes(module.GetAssemblyAttributes()); collector.HandleAttributes(module.GetModuleAttributes()); } public static void CollectAttributeNamespaces(MetadataModule module, HashSet namespaces) { var collector = new RequiredNamespaceCollector(namespaces); collector.HandleAttributes(module.GetAssemblyAttributes()); collector.HandleAttributes(module.GetModuleAttributes()); } public static void CollectNamespaces(IEntity entity, MetadataModule module, HashSet namespaces) { var collector = new RequiredNamespaceCollector(namespaces); collector.CollectNamespaces(entity, module); } void CollectNamespaces(IEntity entity, MetadataModule module, CodeMappingInfo mappingInfo = null) { if (entity == null || entity.MetadataToken.IsNil || module.MetadataFile is not MetadataFile corFile) return; if (mappingInfo == null) mappingInfo = CSharpDecompiler.GetCodeMappingInfo(corFile, entity.MetadataToken); switch (entity) { case ITypeDefinition td: namespaces.Add(td.Namespace); HandleAttributes(td.GetAttributes()); HandleTypeParameters(td.TypeParameters); foreach (var baseType in td.DirectBaseTypes) { CollectNamespacesForTypeReference(baseType); } foreach (var nestedType in td.NestedTypes) { CollectNamespaces(nestedType, module, mappingInfo); } foreach (var field in td.Fields) { CollectNamespaces(field, module, mappingInfo); } foreach (var property in td.Properties) { CollectNamespaces(property, module, mappingInfo); } foreach (var @event in td.Events) { CollectNamespaces(@event, module, mappingInfo); } foreach (var method in td.Methods) { CollectNamespaces(method, module, mappingInfo); } break; case IField field: HandleAttributes(field.GetAttributes()); CollectNamespacesForTypeReference(field.ReturnType); break; case IMethod method: var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList(); foreach (var part in parts) { var partMethod = module.ResolveMethod(part, genericContext); HandleAttributes(partMethod.GetAttributes()); HandleAttributes(partMethod.GetReturnTypeAttributes()); CollectNamespacesForTypeReference(partMethod.ReturnType); foreach (var param in partMethod.Parameters) { HandleAttributes(param.GetAttributes()); CollectNamespacesForTypeReference(param.Type); } HandleTypeParameters(partMethod.TypeParameters); HandleOverrides(part.GetMethodImplementations(module.metadata), module); var methodDef = module.metadata.GetMethodDefinition(part); if (method.HasBody) { MethodBodyBlock body; try { body = module.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress); } catch (BadImageFormatException) { continue; } CollectNamespacesFromMethodBody(body, module); } } break; case IProperty property: HandleAttributes(property.GetAttributes()); CollectNamespacesForTypeReference(property.ReturnType); CollectNamespaces(property.Getter, module, mappingInfo); CollectNamespaces(property.Setter, module, mappingInfo); break; case IEvent @event: HandleAttributes(@event.GetAttributes()); CollectNamespacesForTypeReference(@event.ReturnType); CollectNamespaces(@event.AddAccessor, module, mappingInfo); CollectNamespaces(@event.RemoveAccessor, module, mappingInfo); break; } } void HandleOverrides(ImmutableArray immutableArray, MetadataModule module) { foreach (var h in immutableArray) { var methodImpl = module.metadata.GetMethodImplementation(h); CollectNamespacesForTypeReference(module.ResolveType(methodImpl.Type, genericContext)); CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodBody, genericContext)); CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodDeclaration, genericContext)); } } void CollectNamespacesForTypeReference(IType type) { if (!visitedTypes.Add(type)) return; switch (type) { case ParameterizedType parameterizedType: namespaces.Add(parameterizedType.Namespace); CollectNamespacesForTypeReference(parameterizedType.GenericType); foreach (var arg in parameterizedType.TypeArguments) CollectNamespacesForTypeReference(arg); return; // no need to collect base types again case TypeWithElementType typeWithElementType: CollectNamespacesForTypeReference(typeWithElementType.ElementType); break; case TupleType tupleType: foreach (var elementType in tupleType.ElementTypes) { CollectNamespacesForTypeReference(elementType); } break; case FunctionPointerType fnPtrType: CollectNamespacesForTypeReference(fnPtrType.ReturnType); foreach (var paramType in fnPtrType.ParameterTypes) { CollectNamespacesForTypeReference(paramType); } break; default: namespaces.Add(type.Namespace); break; } foreach (var baseType in type.GetAllBaseTypes()) { namespaces.Add(baseType.Namespace); } } public static void CollectNamespaces(EntityHandle entity, MetadataModule module, HashSet namespaces) { if (entity.IsNil) return; CollectNamespaces(module.ResolveEntity(entity, genericContext), module, namespaces); } void HandleAttributes(IEnumerable attributes) { foreach (var attr in attributes) { namespaces.Add(attr.AttributeType.Namespace); foreach (var arg in attr.FixedArguments) { HandleAttributeValue(arg.Type, arg.Value); } foreach (var arg in attr.NamedArguments) { HandleAttributeValue(arg.Type, arg.Value); } } } void HandleAttributeValue(IType type, object value) { CollectNamespacesForTypeReference(type); if (value is IType typeofType) CollectNamespacesForTypeReference(typeofType); if (value is ImmutableArray> arr) { foreach (var element in arr) { HandleAttributeValue(element.Type, element.Value); } } } void HandleTypeParameters(IEnumerable typeParameters) { foreach (var typeParam in typeParameters) { HandleAttributes(typeParam.GetAttributes()); foreach (var constraint in typeParam.DirectBaseTypes) { CollectNamespacesForTypeReference(constraint); } } } void CollectNamespacesFromMethodBody(MethodBodyBlock method, MetadataModule module) { var metadata = module.metadata; var instructions = method.GetILReader(); if (!method.LocalSignature.IsNil) { ImmutableArray localSignature; try { localSignature = module.DecodeLocalSignature(method.LocalSignature, genericContext); } catch (BadImageFormatException) { // Issue #1211: ignore invalid local signatures localSignature = ImmutableArray.Empty; } foreach (var type in localSignature) CollectNamespacesForTypeReference(type); } foreach (var region in method.ExceptionRegions) { if (region.CatchType.IsNil) continue; IType ty; try { ty = module.ResolveType(region.CatchType, genericContext); } catch (BadImageFormatException) { continue; } CollectNamespacesForTypeReference(ty); } while (instructions.RemainingBytes > 0) { ILOpCode opCode; try { opCode = instructions.DecodeOpCode(); } catch (BadImageFormatException) { return; } switch (opCode.GetOperandType()) { case OperandType.Field: case OperandType.Method: case OperandType.Sig: case OperandType.Tok: case OperandType.Type: EntityHandle handle; try { handle = MetadataTokenHelpers.EntityHandleOrNil(instructions.ReadInt32()); } catch (BadImageFormatException) { return; } if (handle.IsNil) break; switch (handle.Kind) { case HandleKind.TypeDefinition: case HandleKind.TypeReference: case HandleKind.TypeSpecification: IType type; try { type = module.ResolveType(handle, genericContext); } catch (BadImageFormatException) { break; } CollectNamespacesForTypeReference(type); break; case HandleKind.FieldDefinition: case HandleKind.MethodDefinition: case HandleKind.MethodSpecification: case HandleKind.MemberReference: IMember member; try { member = module.ResolveEntity(handle, genericContext) as IMember; } catch (BadImageFormatException) { break; } CollectNamespacesForMemberReference(member); break; case HandleKind.StandaloneSignature: StandaloneSignature sig; try { sig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)handle); } catch (BadImageFormatException) { break; } if (sig.GetKind() == StandaloneSignatureKind.Method) { FunctionPointerType fpt; try { (_, fpt) = module.DecodeMethodSignature((StandaloneSignatureHandle)handle, genericContext); } catch (BadImageFormatException) { break; } CollectNamespacesForTypeReference(fpt); } break; } break; default: try { instructions.SkipOperand(opCode); } catch (BadImageFormatException) { return; } break; } } } void CollectNamespacesForMemberReference(IMember member) { switch (member) { case IField field: CollectNamespacesForTypeReference(field.DeclaringType); CollectNamespacesForTypeReference(field.ReturnType); break; case IMethod method: CollectNamespacesForTypeReference(method.DeclaringType); CollectNamespacesForTypeReference(method.ReturnType); foreach (var param in method.Parameters) CollectNamespacesForTypeReference(param.Type); foreach (var arg in method.TypeArguments) CollectNamespacesForTypeReference(arg); break; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/AliasNamespaceResolveResult.cs ================================================ // // AliasResolveResult.cs // // Author: // Mike Krüger // // Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) // // 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 ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents a namespace resolve result that's resolved using an alias. /// public class AliasNamespaceResolveResult : NamespaceResolveResult { /// /// The alias used. /// public string Alias { get; private set; } public AliasNamespaceResolveResult(string alias, NamespaceResolveResult underlyingResult) : base(underlyingResult.Namespace) { this.Alias = alias; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/AliasTypeResolveResult.cs ================================================ // // AliasTypeResolveResult.cs // // Author: // Mike Krüger // // Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) // // 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 ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents a type resolve result that's resolved using an alias. /// public class AliasTypeResolveResult : TypeResolveResult { /// /// The alias used. /// public string Alias { get; private set; } public AliasTypeResolveResult(string alias, TypeResolveResult underlyingResult) : base(underlyingResult.Type) { this.Alias = alias; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents the result of an await expression. /// public class AwaitResolveResult : ResolveResult { /// /// The method representing the GetAwaiter() call. Can be an or a . /// public readonly ResolveResult GetAwaiterInvocation; /// /// Awaiter type. Will not be null (but can be UnknownType). /// public readonly IType AwaiterType; /// /// Property representing the IsCompleted property on the awaiter type. Can be null if the awaiter type or the property was not found, or when awaiting a dynamic expression. /// public readonly IProperty IsCompletedProperty; /// /// Method representing the OnCompleted method on the awaiter type. Can be null if the awaiter type or the method was not found, or when awaiting a dynamic expression. /// This can also refer to an UnsafeOnCompleted method, if the awaiter type implements System.Runtime.CompilerServices.ICriticalNotifyCompletion. /// public readonly IMethod OnCompletedMethod; /// /// Method representing the GetResult method on the awaiter type. Can be null if the awaiter type or the method was not found, or when awaiting a dynamic expression. /// public readonly IMethod GetResultMethod; public AwaitResolveResult(IType resultType, ResolveResult getAwaiterInvocation, IType awaiterType, IProperty isCompletedProperty, IMethod onCompletedMethod, IMethod getResultMethod) : base(resultType) { if (awaiterType == null) throw new ArgumentNullException(nameof(awaiterType)); if (getAwaiterInvocation == null) throw new ArgumentNullException(nameof(getAwaiterInvocation)); this.GetAwaiterInvocation = getAwaiterInvocation; this.AwaiterType = awaiterType; this.IsCompletedProperty = isCompletedProperty; this.OnCompletedMethod = onCompletedMethod; this.GetResultMethod = getResultMethod; } public override bool IsError { get { return this.GetAwaiterInvocation.IsError || (AwaiterType.Kind != TypeKind.Dynamic && (this.IsCompletedProperty == null || this.OnCompletedMethod == null || this.GetResultMethod == null)); } } public override IEnumerable GetChildResults() { return new[] { GetAwaiterInvocation }; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Contains logic that determines whether an implicit conversion exists between two types. /// /// /// This class is thread-safe. /// public sealed class CSharpConversions { readonly ConcurrentDictionary implicitConversionCache = new ConcurrentDictionary(); readonly ICompilation compilation; public CSharpConversions(ICompilation compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; } /// /// Gets the Conversions instance for the specified . /// This will make use of the context's cache manager to reuse the Conversions instance. /// public static CSharpConversions Get(ICompilation compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); CacheManager cache = compilation.CacheManager; CSharpConversions operators = (CSharpConversions)cache.GetShared(typeof(CSharpConversions)); if (operators == null) { operators = (CSharpConversions)cache.GetOrAddShared(typeof(CSharpConversions), new CSharpConversions(compilation)); } return operators; } #region TypePair (for caching) struct TypePair : IEquatable { public readonly IType FromType; public readonly IType ToType; public TypePair(IType fromType, IType toType) { Debug.Assert(fromType != null && toType != null); this.FromType = fromType; this.ToType = toType; } public override bool Equals(object obj) { return (obj is TypePair) && Equals((TypePair)obj); } public bool Equals(TypePair other) { return object.Equals(this.FromType, other.FromType) && object.Equals(this.ToType, other.ToType); } public override int GetHashCode() { unchecked { return 1000000007 * FromType.GetHashCode() + 1000000009 * ToType.GetHashCode(); } } } #endregion #region ImplicitConversion private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined, bool allowTuple) { Conversion c; if (resolveResult.IsCompileTimeConstant) { c = ImplicitEnumerationConversion(resolveResult, toType); if (c.IsValid) return c; if (ImplicitConstantExpressionConversion(resolveResult, toType)) return Conversion.ImplicitConstantExpressionConversion; } // C# 9.0 spec: §10.2.5 if (resolveResult is InterpolatedStringResolveResult) { if (toType.IsKnownType(KnownTypeCode.IFormattable) || toType.IsKnownType(KnownTypeCode.FormattableString)) return Conversion.ImplicitInterpolatedStringConversion; } if (resolveResult.Type.Kind == TypeKind.Dynamic) return Conversion.ImplicitDynamicConversion; c = AnonymousFunctionConversion(resolveResult, toType); if (c != Conversion.None) return c; c = MethodGroupConversion(resolveResult, toType); if (c != Conversion.None) return c; // C# 9.0 spec: §10.2.16 default literal conversions // TODO if (resolveResult.IsCompileTimeConstant) { c = StandardImplicitConversion(resolveResult.Type, toType, allowTuple); if (c != Conversion.None) return c; if (allowUserDefined) { c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType); if (c != Conversion.None) return c; } } else { if (allowTuple && resolveResult is TupleResolveResult tupleRR) { c = TupleConversion(tupleRR, toType, isExplicit: false); if (c != Conversion.None) return c; } // C# 9.0 spec: §10.2.17 if (resolveResult is ThrowResolveResult) { return Conversion.ThrowExpressionConversion; } if (allowUserDefined && allowTuple) { // if allowUserDefined and allowTuple are true, we might as well use the cache c = ImplicitConversion(resolveResult.Type, toType); } else { c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined, allowTuple); } } return c; } private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined, bool allowTuple) { // C# 4.0 spec: §6.1 var c = StandardImplicitConversion(fromType, toType, allowTuple); if (c == Conversion.None && allowUserDefined) { c = UserDefinedImplicitConversion(null, fromType, toType); } return c; } public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType) { if (resolveResult == null) throw new ArgumentNullException(nameof(resolveResult)); return ImplicitConversion(resolveResult, toType, allowUserDefined: true, allowTuple: true); } public Conversion ImplicitConversion(IType fromType, IType toType) { if (fromType == null) throw new ArgumentNullException(nameof(fromType)); if (toType == null) throw new ArgumentNullException(nameof(toType)); TypePair pair = new TypePair(fromType, toType); if (implicitConversionCache.TryGetValue(pair, out Conversion c)) return c; c = ImplicitConversion(fromType, toType, allowUserDefined: true, allowTuple: true); implicitConversionCache[pair] = c; return c; } public Conversion StandardImplicitConversion(IType fromType, IType toType) { if (fromType == null) throw new ArgumentNullException(nameof(fromType)); if (toType == null) throw new ArgumentNullException(nameof(toType)); return StandardImplicitConversion(fromType, toType, allowTupleConversion: true); } Conversion StandardImplicitConversion(IType fromType, IType toType, bool allowTupleConversion) { // C# 9.0 spec: §10.4.2 if (IdentityConversion(fromType, toType)) return Conversion.IdentityConversion; if (ImplicitNumericConversion(fromType, toType)) return Conversion.ImplicitNumericConversion; Conversion c = ImplicitNullableConversion(fromType, toType); if (c != Conversion.None) return c; if (NullLiteralConversion(fromType, toType)) return Conversion.NullLiteralConversion; if (ImplicitReferenceConversion(fromType, toType, 0)) return Conversion.ImplicitReferenceConversion; if (IsBoxingConversion(fromType, toType)) return Conversion.BoxingConversion; if (ImplicitTypeParameterConversion(fromType, toType)) { // Implicit type parameter conversions that aren't also // reference conversions are considered to be boxing conversions return Conversion.BoxingConversion; } if (ImplicitPointerConversion(fromType, toType)) return Conversion.ImplicitPointerConversion; if (allowTupleConversion) { // TODO are tuple conversions really standard implicit conversions? // the C# 9.0 spec doesn't list them as standard implicit conversions. c = TupleConversion(fromType, toType, isExplicit: false); if (c != Conversion.None) return c; } if ((toType.IsKnownType(KnownTypeCode.SpanOfT) || toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) && fromType.IsInlineArrayType()) { var elementType = fromType.GetInlineArrayElementType(); var spanElementType = toType.TypeArguments[0]; if (IdentityConversion(elementType, spanElementType)) return Conversion.InlineArrayConversion; } if (IsImplicitSpanConversion(fromType, toType)) { return Conversion.ImplicitSpanConversion; } return Conversion.None; } /// /// Gets whether the type 'fromType' is convertible to 'toType' /// using one of the conversions allowed when satisfying constraints (§4.4.4) /// public bool IsConstraintConvertible(IType fromType, IType toType) { if (fromType == null) throw new ArgumentNullException(nameof(fromType)); if (toType == null) throw new ArgumentNullException(nameof(toType)); if (IdentityConversion(fromType, toType)) return true; if (ImplicitReferenceConversion(fromType, toType, 0)) return true; if (NullableType.IsNullable(fromType)) { // An 'object' constraint still allows nullable value types // (object constraints don't exist in C#, but are inserted by DefaultResolvedTypeParameter.DirectBaseTypes) if (toType.IsKnownType(KnownTypeCode.Object)) return true; } else { if (IsBoxingConversion(fromType, toType)) return true; } if (ImplicitTypeParameterConversion(fromType, toType)) return true; return false; } #endregion #region ExplicitConversion public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType) { if (resolveResult == null) throw new ArgumentNullException(nameof(resolveResult)); if (toType == null) throw new ArgumentNullException(nameof(toType)); if (resolveResult.Type.Kind == TypeKind.Dynamic) return Conversion.ExplicitDynamicConversion; Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false, allowTuple: false); if (c != Conversion.None) return c; if (resolveResult is TupleResolveResult tupleRR) { c = TupleConversion(tupleRR, toType, isExplicit: true); if (c != Conversion.None) return c; } c = ExplicitConversionImpl(resolveResult.Type, toType); if (c != Conversion.None) return c; return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType); } public Conversion ExplicitConversion(IType fromType, IType toType) { if (fromType == null) throw new ArgumentNullException(nameof(fromType)); if (toType == null) throw new ArgumentNullException(nameof(toType)); Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false, allowTuple: false); if (c != Conversion.None) return c; c = ExplicitConversionImpl(fromType, toType); if (c != Conversion.None) return c; return UserDefinedExplicitConversion(null, fromType, toType); } Conversion ExplicitConversionNotUserDefined(IType fromType, IType toType) { Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false, allowTuple: false); if (c != Conversion.None) return c; return ExplicitConversionImpl(fromType, toType); } Conversion ExplicitConversionImpl(IType fromType, IType toType) { // This method is called after we already checked for implicit conversions, // so any remaining conversions must be explicit. if (AnyNumericConversion(fromType, toType)) return Conversion.ExplicitNumericConversion; if (ExplicitEnumerationConversion(fromType, toType)) return Conversion.EnumerationConversion(false, false); Conversion c = ExplicitNullableConversion(fromType, toType); if (c != Conversion.None) return c; if (ExplicitReferenceConversion(fromType, toType)) return Conversion.ExplicitReferenceConversion; if (UnboxingConversion(fromType, toType)) return Conversion.UnboxingConversion; c = ExplicitTypeParameterConversion(fromType, toType); if (c != Conversion.None) return c; if (ExplicitPointerConversion(fromType, toType)) return Conversion.ExplicitPointerConversion; return TupleConversion(fromType, toType, isExplicit: true); } #endregion #region Identity Conversion /// /// Gets whether there is an identity conversion from to /// public bool IdentityConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.1 fromType = fromType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); toType = toType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); return fromType.Equals(toType); } #endregion #region Numeric Conversions static readonly bool[,] implicitNumericConversionLookup = { // to: short ushort int uint long ulong // from: /* char */ { false, true , true , true , true , true }, /* sbyte */ { true , false, true , false, true , false }, /* byte */ { true , true , true , true , true , true }, /* short */ { true , false, true , false, true , false }, /* ushort */ { false, true , true , true , true , true }, /* int */ { false, false, true , false, true , false }, /* uint */ { false, false, false, true , true , true }, /* long */ { false, false, false, false, true , false }, /* ulong */ { false, false, false, false, false, true }, }; bool ImplicitNumericConversion(IType fromType, IType toType) { // C# 9.0 spec: §10.2.3 TypeCode from = ReflectionHelper.GetTypeCode(fromType); if (from == TypeCode.Empty) { // When converting from a native-sized integer, treat it as 64-bits switch (fromType.Kind) { case TypeKind.NInt: from = TypeCode.Int64; break; case TypeKind.NUInt: from = TypeCode.UInt64; break; } } TypeCode to = ReflectionHelper.GetTypeCode(toType); if (to == TypeCode.Empty) { // When converting to a native-sized integer, only 32-bits can be stored safely switch (toType.Kind) { case TypeKind.NInt: to = TypeCode.Int32; break; case TypeKind.NUInt: to = TypeCode.UInt32; break; } } if (to >= TypeCode.Single && to <= TypeCode.Decimal) { // Conversions to float/double/decimal exist from all integral types, // and there's a conversion from float to double. return from >= TypeCode.Char && from <= TypeCode.UInt64 || from == TypeCode.Single && to == TypeCode.Double; } else { // Conversions to integral types: look at the table return from >= TypeCode.Char && from <= TypeCode.UInt64 && to >= TypeCode.Int16 && to <= TypeCode.UInt64 && implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16]; } } bool IsNumericType(IType type) { switch (type.Kind) { case TypeKind.NInt: case TypeKind.NUInt: return true; } TypeCode c = ReflectionHelper.GetTypeCode(type); return c >= TypeCode.Char && c <= TypeCode.Decimal; } bool AnyNumericConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.2 + §6.2.1 return IsNumericType(fromType) && IsNumericType(toType); } #endregion #region Enumeration Conversions Conversion ImplicitEnumerationConversion(ResolveResult rr, IType toType) { // C# 9.0 spec: §10.2.4 + enum part of §10.2.6 (Nullable conversions) Debug.Assert(rr.IsCompileTimeConstant); TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type); if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDouble(rr.ConstantValue) == 0) { if (NullableType.GetUnderlyingType(toType).Kind == TypeKind.Enum) { return Conversion.EnumerationConversion(true, NullableType.IsNullable(toType)); } } return Conversion.None; } bool ExplicitEnumerationConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.2.2 if (fromType.Kind == TypeKind.Enum) { return toType.Kind == TypeKind.Enum || IsNumericType(toType); } else if (IsNumericType(fromType)) { return toType.Kind == TypeKind.Enum; } return false; } #endregion #region Nullable Conversions Conversion ImplicitNullableConversion(IType fromType, IType toType) { // C# 9.0 spec: §10.2.6 if (NullableType.IsNullable(toType)) { IType t = NullableType.GetUnderlyingType(toType); IType s = NullableType.GetUnderlyingType(fromType); // might or might not be nullable if (IdentityConversion(s, t)) return Conversion.ImplicitNullableConversion; if (ImplicitNumericConversion(s, t)) return Conversion.ImplicitLiftedNumericConversion; } return Conversion.None; } Conversion ExplicitNullableConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.4 if (NullableType.IsNullable(toType) || NullableType.IsNullable(fromType)) { IType t = NullableType.GetUnderlyingType(toType); IType s = NullableType.GetUnderlyingType(fromType); if (IdentityConversion(s, t)) return Conversion.ExplicitNullableConversion; if (AnyNumericConversion(s, t)) return Conversion.ExplicitLiftedNumericConversion; if (ExplicitEnumerationConversion(s, t)) return Conversion.EnumerationConversion(false, true); } return Conversion.None; } #endregion #region Null Literal Conversion bool NullLiteralConversion(IType fromType, IType toType) { // C# 9.0 spec: §10.2.7 if (fromType.Kind == TypeKind.Null) { return NullableType.IsNullable(toType) || toType.IsReferenceType == true; } else { return false; } } #endregion #region Implicit Reference Conversion public bool IsImplicitReferenceConversion(IType fromType, IType toType) { return ImplicitReferenceConversion(fromType, toType, 0); } bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth) { // C# 9.0 spec: §10.2.8 // reference conversions are possible: // - if both types are known to be reference types // - if both types are type parameters and fromType has a class constraint // (ImplicitTypeParameterConversionWithClassConstraintOnlyOnT) if (!(fromType.IsReferenceType == true && toType.IsReferenceType != false)) return false; ArrayType fromArray = fromType as ArrayType; if (fromArray != null) { ArrayType toArray = toType as ArrayType; if (toArray != null) { // array covariance (the broken kind) return fromArray.Dimensions == toArray.Dimensions && ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType, subtypeCheckNestingDepth); } // conversion from single-dimensional array S[] to IList/IReadOnlyList + base interfaces: IType toTypeArgument = UnpackGenericArrayInterface(toType); if (fromArray.Dimensions == 1 && toTypeArgument != null) { // array covariance plays a part here as well (string[] is IList) return IdentityConversion(fromArray.ElementType, toTypeArgument) || ImplicitReferenceConversion(fromArray.ElementType, toTypeArgument, subtypeCheckNestingDepth); } // conversion from any array to System.Array and the interfaces it implements: IType systemArray = compilation.FindType(KnownTypeCode.Array); return ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth); } // now comes the hard part: traverse the inheritance chain and figure out generics+variance return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth); } /// /// For , , and , returns T. /// Otherwise, returns null. /// IType UnpackGenericArrayInterface(IType interfaceType) { if (interfaceType is ParameterizedType pt) { switch (pt.GetDefinition()?.KnownTypeCode) { case KnownTypeCode.IListOfT: case KnownTypeCode.ICollectionOfT: case KnownTypeCode.IEnumerableOfT: case KnownTypeCode.IReadOnlyListOfT: case KnownTypeCode.IReadOnlyCollectionOfT: return pt.GetTypeArgument(0); } } return null; } // Determines whether s is a subtype of t. // Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth) { // conversion to dynamic + object are always possible if (t.Kind == TypeKind.Dynamic || t.IsKnownType(KnownTypeCode.Object)) return true; if (subtypeCheckNestingDepth > 10) { // Subtyping in C# is undecidable // (see "On Decidability of Nominal Subtyping with Variance" by Andrew J. Kennedy and Benjamin C. Pierce), // so we'll prevent infinite recursions by putting a limit on the nesting depth of variance conversions. // No real C# code should use generics nested more than 10 levels deep, and even if they do, most of // those nestings should not involve variance. return false; } // let GetAllBaseTypes do the work for us foreach (IType baseType in s.GetAllBaseTypes()) { if (IdentityOrVarianceConversion(baseType, t, subtypeCheckNestingDepth + 1)) return true; } return false; } bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth) { ITypeDefinition def = s.GetDefinition(); if (def != null) { if (!def.Equals(t.GetDefinition())) return false; ParameterizedType ps = s as ParameterizedType; ParameterizedType pt = t as ParameterizedType; if (ps != null && pt != null) { // C# 4.0 spec: §13.1.3.2 Variance Conversion for (int i = 0; i < def.TypeParameters.Count; i++) { IType si = ps.GetTypeArgument(i); IType ti = pt.GetTypeArgument(i); if (IdentityConversion(si, ti)) continue; ITypeParameter xi = def.TypeParameters[i]; switch (xi.Variance) { case VarianceModifier.Covariant: if (!ImplicitReferenceConversion(si, ti, subtypeCheckNestingDepth)) return false; break; case VarianceModifier.Contravariant: if (!ImplicitReferenceConversion(ti, si, subtypeCheckNestingDepth)) return false; break; default: return false; } } } else if (ps != null || pt != null) { return false; // only of of them is parameterized, or counts don't match? -> not valid conversion } return true; } else { // not type definitions? we still need to check for equal types (e.g. s and t might be type parameters) return s.Equals(t); } } #endregion #region Explicit Reference Conversion bool ExplicitReferenceConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.2.4 // test that the types are reference types: if (toType.IsReferenceType != true) return false; if (fromType.IsReferenceType != true) { // special case: // converting from F to T is a reference conversion where T : class, F // (because F actually must be a reference type as well, even though C# doesn't treat it as one) if (fromType.Kind == TypeKind.TypeParameter) return IsSubtypeOf(toType, fromType, 0); return false; } if (toType.Kind == TypeKind.Array) { ArrayType toArray = (ArrayType)toType; if (fromType.Kind == TypeKind.Array) { // Array covariance ArrayType fromArray = (ArrayType)fromType; if (fromArray.Dimensions != toArray.Dimensions) return false; return ExplicitReferenceConversion(fromArray.ElementType, toArray.ElementType); } IType fromTypeArgument = UnpackGenericArrayInterface(fromType); if (fromTypeArgument != null && toArray.Dimensions == 1) { return ExplicitReferenceConversion(fromTypeArgument, toArray.ElementType) || IdentityConversion(fromTypeArgument, toArray.ElementType); } // Otherwise treat the array like a sealed class - require implicit conversion in the opposite direction return IsImplicitReferenceConversion(toType, fromType); } else if (fromType.Kind == TypeKind.Array) { ArrayType fromArray = (ArrayType)fromType; IType toTypeArgument = UnpackGenericArrayInterface(toType); if (toTypeArgument != null && fromArray.Dimensions == 1) { return ExplicitReferenceConversion(fromArray.ElementType, toTypeArgument); } // Otherwise treat the array like a sealed class return IsImplicitReferenceConversion(fromType, toType); } else if (fromType.Kind == TypeKind.Delegate && toType.Kind == TypeKind.Delegate) { ITypeDefinition def = fromType.GetDefinition(); if (def == null || !def.Equals(toType.GetDefinition())) return false; ParameterizedType ps = fromType as ParameterizedType; ParameterizedType pt = toType as ParameterizedType; if (ps == null || pt == null) { // non-generic delegate - return true for the identity conversion return ps == null && pt == null; } for (int i = 0; i < def.TypeParameters.Count; i++) { IType si = ps.GetTypeArgument(i); IType ti = pt.GetTypeArgument(i); if (IdentityConversion(si, ti)) continue; ITypeParameter xi = def.TypeParameters[i]; switch (xi.Variance) { case VarianceModifier.Covariant: if (!ExplicitReferenceConversion(si, ti)) return false; break; case VarianceModifier.Contravariant: if (!(si.IsReferenceType == true && ti.IsReferenceType == true)) return false; break; default: return false; } } return true; } else if (IsSealedReferenceType(fromType)) { // If the source type is sealed, explicit conversions can't do anything more than implicit ones return IsImplicitReferenceConversion(fromType, toType); } else if (IsSealedReferenceType(toType)) { // The the target type is sealed, there must be an implicit conversion in the opposite direction return IsImplicitReferenceConversion(toType, fromType); } else { if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface) return true; else return IsImplicitReferenceConversion(toType, fromType) || IsImplicitReferenceConversion(fromType, toType); } } bool IsSealedReferenceType(IType type) { TypeKind kind = type.Kind; return kind == TypeKind.Class && type.GetDefinition().IsSealed || kind == TypeKind.Delegate; } #endregion #region Boxing Conversions bool IsBoxingConversion(IType fromType, IType toType) { // C# 9.0 spec: §10.2.9 fromType = NullableType.GetUnderlyingType(fromType); if (fromType.IsReferenceType == false && !fromType.IsByRefLike && toType.IsReferenceType == true) return IsSubtypeOf(fromType, toType, 0); else return false; } /// /// Gets whether the conversion from fromType to toType is a boxing conversion, /// or an implicit conversion involving a type parameter that might be /// a boxing conversion when instantiated with a value type. /// public bool IsBoxingConversionOrInvolvingTypeParameter(IType fromType, IType toType) { return IsBoxingConversion(fromType, toType) || ImplicitTypeParameterConversion(fromType, toType); } bool UnboxingConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.2.5 toType = NullableType.GetUnderlyingType(toType); if (fromType.IsReferenceType == true && toType.IsReferenceType == false) return IsSubtypeOf(toType, fromType, 0); else return false; } #endregion #region Implicit Constant-Expression Conversion bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) { if (rr == null || !rr.IsCompileTimeConstant) return false; // C# 9.0 spec: §10.2.11 + part of §10.2.6 (Nullable conversions) TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); toType = NullableType.GetUnderlyingType(toType); TypeCode toTypeCode = ReflectionHelper.GetTypeCode(toType); if (toType.Kind == TypeKind.NUInt) { toTypeCode = TypeCode.UInt32; } if (fromTypeCode == TypeCode.Int64) { long val = (long)rr.ConstantValue; return val >= 0 && toTypeCode == TypeCode.UInt64; } else if (fromTypeCode == TypeCode.Int32) { object cv = rr.ConstantValue; if (cv == null) return false; int val = (int)cv; switch (toTypeCode) { case TypeCode.SByte: return val >= SByte.MinValue && val <= SByte.MaxValue; case TypeCode.Byte: return val >= Byte.MinValue && val <= Byte.MaxValue; case TypeCode.Int16: return val >= Int16.MinValue && val <= Int16.MaxValue; case TypeCode.UInt16: return val >= UInt16.MinValue && val <= UInt16.MaxValue; case TypeCode.UInt32: case TypeCode.UInt64: return val >= 0; } } return false; } #endregion #region Conversions involving type parameters /// /// Implicit conversions involving type parameters. /// bool ImplicitTypeParameterConversion(IType fromType, IType toType) { // C# 9.0 spec: §10.2.12 if (fromType.Kind != TypeKind.TypeParameter) return false; // not a type parameter if (fromType.IsReferenceType.HasValue) return false; // already handled by ImplicitReferenceConversion/BoxingConversion return IsSubtypeOf(fromType, toType, 0); } Conversion ExplicitTypeParameterConversion(IType fromType, IType toType) { if (toType.Kind == TypeKind.TypeParameter) { // Explicit type parameter conversions that aren't also // reference conversions are considered to be unboxing conversions if (fromType.Kind == TypeKind.Interface || IsSubtypeOf(toType, fromType, 0)) return Conversion.UnboxingConversion; } else { if (fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface) return Conversion.BoxingConversion; } return Conversion.None; } #endregion #region Pointer Conversions bool ImplicitPointerConversion(IType fromType, IType toType) { // C# 4.0 spec: §18.4 Pointer conversions if (fromType.Kind.IsAnyPointer() && toType is PointerType && toType.ReflectionName == "System.Void*") return true; if (fromType.Kind == TypeKind.Null && toType.Kind.IsAnyPointer()) return true; if (fromType is FunctionPointerType fromFnPtr && toType is FunctionPointerType toFnPtr && fromFnPtr.CallingConvention == toFnPtr.CallingConvention && fromFnPtr.ParameterTypes.Length == toFnPtr.ParameterTypes.Length) { // Variance applies to function pointer types const int nestingDepth = 0; if (!(IdentityConversion(fromFnPtr.ReturnType, toFnPtr.ReturnType) || ImplicitReferenceConversion(fromFnPtr.ReturnType, toFnPtr.ReturnType, nestingDepth))) { return false; } foreach (var (fromPT, toPT) in fromFnPtr.ParameterTypes.Zip(toFnPtr.ParameterTypes)) { if (!(IdentityConversion(toPT, fromPT) || ImplicitReferenceConversion(toPT, fromPT, nestingDepth))) { return false; } } return true; } return false; } bool ExplicitPointerConversion(IType fromType, IType toType) { // C# 4.0 spec: §18.4 Pointer conversions if (fromType.Kind.IsAnyPointer()) { return toType.Kind.IsAnyPointer() || IsIntegerType(toType); } else { return toType.Kind.IsAnyPointer() && IsIntegerType(fromType); } } bool IsIntegerType(IType type) { switch (type.Kind) { case TypeKind.NInt: case TypeKind.NUInt: return true; } TypeCode c = ReflectionHelper.GetTypeCode(type); return c >= TypeCode.SByte && c <= TypeCode.UInt64; } #endregion #region User-Defined Conversions /// /// Gets whether type A is encompassed by type B. /// bool IsEncompassedBy(IType a, IType b) { return StandardImplicitConversion(a, b).IsValid; } bool IsEncompassingOrEncompassedBy(IType a, IType b) { return (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid); } IType FindMostEncompassedType(IEnumerable candidates) { IType best = null; foreach (var current in candidates) { if (best == null || IsEncompassedBy(current, best)) best = current; else if (!IsEncompassedBy(best, current)) return null; // Ambiguous } return best; } IType FindMostEncompassingType(IEnumerable candidates) { IType best = null; foreach (var current in candidates) { if (best == null || IsEncompassedBy(best, current)) best = current; else if (!IsEncompassedBy(current, best)) return null; // Ambiguous } return best; } Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList operators, bool isImplicit, IType source, IType target) { var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList(); if (selected.Count == 0) return Conversion.None; if (selected.Count == 1) { return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversionNotUserDefined(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversionNotUserDefined(mostSpecificTarget, target)); } int nNonLifted = selected.Count(s => !s.IsLifted); if (nNonLifted == 1) { var op = selected.First(s => !s.IsLifted); return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversionNotUserDefined(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversionNotUserDefined(mostSpecificTarget, target)); } return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversionNotUserDefined(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversionNotUserDefined(mostSpecificTarget, target)); } Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType) { // C# 4.0 spec §6.4.4 User-defined implicit conversions // user-defined conversions are not supported with interfaces if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface) { return Conversion.None; } var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false); if (operators.Count > 0) { var mostSpecificSource = operators.Any(op => op.SourceType.Equals(fromType)) ? fromType : FindMostEncompassedType(operators.Select(op => op.SourceType)); if (mostSpecificSource == null) return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType)); if (mostSpecificTarget == null) { if (NullableType.IsNullable(toType)) return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); } var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true, fromType, toType); if (selected != Conversion.None) { if (selected.IsLifted && NullableType.IsNullable(toType)) { // Prefer A -> B -> B? over A -> A? -> B? var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); if (other != Conversion.None) return other; } return selected; } else if (NullableType.IsNullable(toType)) return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else return Conversion.None; } else { return Conversion.None; } } Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType) { // C# 4.0 spec §6.4.5 User-defined explicit conversions // user-defined conversions are not supported with interfaces if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface) { return Conversion.None; } var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true); if (operators.Count > 0) { IType mostSpecificSource; if (operators.Any(op => op.SourceType.Equals(fromType))) { mostSpecificSource = fromType; } else { var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(fromType, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, NullableType.GetUnderlyingType(op.SourceType))).ToList(); if (operatorsWithSourceEncompassingFromType.Any()) mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType)); else mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType)); } if (mostSpecificSource == null) return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); IType mostSpecificTarget; if (operators.Any(op => op.TargetType.Equals(toType))) mostSpecificTarget = toType; else if (operators.Any(op => IsEncompassedBy(op.TargetType, toType))) mostSpecificTarget = FindMostEncompassingType(operators.Where(op => IsEncompassedBy(op.TargetType, toType)).Select(op => op.TargetType)); else mostSpecificTarget = FindMostEncompassedType(operators.Select(op => op.TargetType)); if (mostSpecificTarget == null) { if (NullableType.IsNullable(toType)) return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); } var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false, fromType, toType); if (selected != Conversion.None) { if (selected.IsLifted && NullableType.IsNullable(toType)) { // Prefer A -> B -> B? over A -> A? -> B? var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); if (other != Conversion.None) return other; } return selected; } else if (NullableType.IsNullable(toType)) return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else if (NullableType.IsNullable(fromType)) return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B else return Conversion.None; } else { return Conversion.None; } } class OperatorInfo { public readonly IMethod Method; public readonly IType SourceType; public readonly IType TargetType; public readonly bool IsLifted; public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted) { this.Method = method; this.SourceType = sourceType; this.TargetType = targetType; this.IsLifted = isLifted; } } static IType UnderlyingTypeForConversion(IType type) { if (type.Kind == TypeKind.ByReference) { type = ((ByReferenceType)type).ElementType; } return NullableType.GetUnderlyingType(type); } List GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit) { // Find the candidate operators: Predicate opFilter; if (isExplicit) opFilter = m => m.IsStatic && m.IsOperator && (m.Name == "op_Explicit" || m.Name == "op_Implicit") && m.Parameters.Count == 1; else opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1; var operators = UnderlyingTypeForConversion(fromType).GetMethods(opFilter) .Concat(UnderlyingTypeForConversion(toType).GetMethods(opFilter)).Distinct(); // Determine whether one of them is applicable: List result = new List(); foreach (IMethod op in operators) { IType sourceType = op.Parameters[0].Type; if (sourceType.Kind == TypeKind.ByReference && op.Parameters[0].ReferenceKind == ReferenceKind.In && fromType.Kind != TypeKind.ByReference) { sourceType = ((ByReferenceType)sourceType).ElementType; } IType targetType = op.ReturnType; // Try if the operator is applicable: bool isApplicable; if (isExplicit) { isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) && IsEncompassingOrEncompassedBy(targetType, toType); } else { isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) && IsEncompassedBy(targetType, toType); } // Try if the operator is applicable in lifted form: if (isApplicable) { result.Add(new OperatorInfo(op, sourceType, targetType, false)); } if (NullableType.IsNonNullableValueType(sourceType)) { // An operator can be applicable in both lifted and non-lifted form in case of explicit conversions IType liftedSourceType = NullableType.Create(compilation, sourceType); IType liftedTargetType = NullableType.IsNonNullableValueType(targetType) ? NullableType.Create(compilation, targetType) : targetType; if (isExplicit) { isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType) && IsEncompassingOrEncompassedBy(liftedTargetType, toType); } else { isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType); } if (isApplicable) { result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true)); } } } return result; } #endregion #region Implicit Span Conversion bool IsImplicitSpanConversion(IType fromType, IType toType) { if (!compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes)) { return false; } // An implicit span conversion permits array_types, System.Span, System.ReadOnlySpan, // and string to be converted between each other // see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-14.0/first-class-span-types#span-conversions switch (fromType) { case ArrayType { Dimensions: 1, ElementType: var elementType }: if (toType.IsKnownType(KnownTypeCode.SpanOfT)) { return IdentityConversion(elementType, toType.TypeArguments[0]); } if (toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { return IdentityConversion(elementType, toType.TypeArguments[0]) || IsImplicitReferenceConversion(elementType, toType.TypeArguments[0]); } break; case ParameterizedType pt when pt.IsKnownType(KnownTypeCode.SpanOfT) || pt.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): if (toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { return IdentityConversion(pt.TypeArguments[0], toType.TypeArguments[0]) || IsImplicitReferenceConversion(pt.TypeArguments[0], toType.TypeArguments[0]); } break; case var s when s.IsKnownType(KnownTypeCode.String): return toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && toType.TypeArguments[0].IsKnownType(KnownTypeCode.Char); } return false; } #endregion #region AnonymousFunctionConversion Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType) { // C# 9.0 spec §10.7 Anonymous function conversions LambdaResolveResult f = resolveResult as LambdaResolveResult; if (f == null) return Conversion.None; if (!f.IsAnonymousMethod) { // It's a lambda, so conversions to expression trees exist // (even if the conversion leads to a compile-time error, e.g. for statement lambdas) toType = UnpackExpressionTreeType(toType); } IMethod d = toType.GetDelegateInvokeMethod(); if (d == null) return Conversion.None; IType[] dParamTypes = new IType[d.Parameters.Count]; for (int i = 0; i < dParamTypes.Length; i++) { dParamTypes[i] = d.Parameters[i].Type; } IType dReturnType = d.ReturnType; if (f.HasParameterList) { // If F contains an anonymous-function-signature, then D and F have the same number of parameters. if (d.Parameters.Count != f.Parameters.Count) return Conversion.None; if (f.IsImplicitlyTyped) { // If F has an implicitly typed parameter list, D has no ref or out parameters. // TODO: what about in parameters? foreach (IParameter p in d.Parameters) { if (p.ReferenceKind != ReferenceKind.None) return Conversion.None; } } else { // If F has an explicitly typed parameter list, each parameter in D has the same type // and modifiers as the corresponding parameter in F. for (int i = 0; i < f.Parameters.Count; i++) { IParameter pD = d.Parameters[i]; IParameter pF = f.Parameters[i]; if (pD.ReferenceKind != pF.ReferenceKind) return Conversion.None; if (!IdentityConversion(dParamTypes[i], pF.Type)) return Conversion.None; } } } else { // If F does not contain an anonymous-function-signature, then D may have zero or more parameters of any // type, as long as no parameter of D has the out parameter modifier. foreach (IParameter p in d.Parameters) { if (p.ReferenceKind == ReferenceKind.Out) return Conversion.None; } } return f.IsValid(dParamTypes, dReturnType, this); } static IType UnpackExpressionTreeType(IType type) { ParameterizedType pt = type as ParameterizedType; if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" && pt.Namespace == "System.Linq.Expressions") { return pt.GetTypeArgument(0); } else { return type; } } #endregion #region MethodGroupConversion Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType) { // C# 9.0 spec §10.8 Method group conversions if (resolveResult is not MethodGroupResolveResult rr) return Conversion.None; IMethod invoke = toType.GetDelegateInvokeMethod(); if (invoke == null) return Conversion.None; ResolveResult[] args = new ResolveResult[invoke.Parameters.Count]; for (int i = 0; i < args.Length; i++) { IParameter param = invoke.Parameters[i]; IType parameterType = param.Type; if (param.ReferenceKind != ReferenceKind.None && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else if (param.Type.Kind == TypeKind.Dynamic) { args[i] = new ResolveResult(compilation.FindType(KnownTypeCode.Object)); } else { args[i] = new ResolveResult(parameterType); } } var or = rr.PerformOverloadResolution( compilation, args, allowExpandingParams: false, allowOptionalParameters: false, allowImplicitIn: false, conversions: this ); if (or.FoundApplicableCandidate) { IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments(); bool isVirtual = method.IsOverridable && !(rr.TargetResult is ThisResolveResult { CausesNonVirtualInvocation: true }); bool isValid = !or.IsAmbiguous && IsDelegateCompatible(method, invoke, or.IsExtensionMethodInvocation); bool delegateCapturesFirstArgument = or.IsExtensionMethodInvocation || !method.IsStatic; if (isValid) return Conversion.MethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument); else return Conversion.InvalidMethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument); } else { return Conversion.None; } } /// /// Gets whether a is compatible with a delegate type. /// §15.2 Delegate compatibility /// /// The method to test for compatibility /// The delegate type public bool IsDelegateCompatible(IMethod method, IType delegateType) { if (method == null) throw new ArgumentNullException(nameof(method)); if (delegateType == null) throw new ArgumentNullException(nameof(delegateType)); IMethod invoke = delegateType.GetDelegateInvokeMethod(); if (invoke == null) return false; return IsDelegateCompatible(method, invoke, false); } /// /// Gets whether a method is compatible with a delegate type. /// /// The method to test for compatibility /// The invoke method of the delegate /// Gets whether m is accessed using extension method syntax. /// If this parameter is true, the first parameter of will be ignored. bool IsDelegateCompatible(IMethod m, IMethod d, bool isExtensionMethodInvocation) { // C# 9.0 §20.4 Delegate compatibility if (m == null) throw new ArgumentNullException(nameof(m)); if (d == null) throw new ArgumentNullException(nameof(d)); int firstParameterInM = isExtensionMethodInvocation ? 1 : 0; if (m.Parameters.Count - firstParameterInM != d.Parameters.Count) return false; for (int i = 0; i < d.Parameters.Count; i++) { var pm = m.Parameters[firstParameterInM + i]; var pd = d.Parameters[i]; // ret/out/in must match if (pm.ReferenceKind != pd.ReferenceKind) return false; if (pm.ReferenceKind != ReferenceKind.None) { // ref/out/in parameters must have same types // according to the spec, but Roslyn seems to allow identity conversions if (!IdentityConversion(pd.Type, pm.Type)) return false; } else { // non-ref/out parameters must have an identity or reference conversion from pd to pm if (!IdentityConversion(pd.Type, pm.Type) && !IsImplicitReferenceConversion(pd.Type, pm.Type)) return false; } } if (m.ReturnTypeIsRefReadOnly != d.ReturnTypeIsRefReadOnly) return false; // check return type compatibility return IdentityConversion(m.ReturnType, d.ReturnType) || IsImplicitReferenceConversion(m.ReturnType, d.ReturnType); } #endregion #region Tuple Conversion Conversion TupleConversion(TupleResolveResult fromRR, IType toType, bool isExplicit) { // C# 9.0 spec: §10.2.13 (implicit tuple conversions) + $10.3.6 (explicit tuple conversions) var fromElements = fromRR.Elements; var toElements = TupleType.GetTupleElementTypes(toType); if (toElements.IsDefault || fromElements.Length != toElements.Length) return Conversion.None; Conversion[] elementConversions = new Conversion[fromElements.Length]; for (int i = 0; i < elementConversions.Length; i++) { Conversion c; if (isExplicit) { c = ExplicitConversion(fromElements[i], toElements[i]); } else { c = ImplicitConversion(fromElements[i], toElements[i]); } if (!c.IsValid) return Conversion.None; elementConversions[i] = c; } return Conversion.TupleConversion(elementConversions.ToImmutableArray()); } Conversion TupleConversion(IType fromType, IType toType, bool isExplicit) { // C# 9.0 spec: §10.2.13 (implicit tuple conversions) + $10.3.6 (explicit tuple conversions) var fromElements = TupleType.GetTupleElementTypes(fromType); if (fromElements.IsDefaultOrEmpty) return Conversion.None; var toElements = TupleType.GetTupleElementTypes(toType); if (toElements.IsDefault || fromElements.Length != toElements.Length) return Conversion.None; Conversion[] elementConversions = new Conversion[fromElements.Length]; for (int i = 0; i < elementConversions.Length; i++) { Conversion c; if (isExplicit) { c = ExplicitConversion(fromElements[i], toElements[i]); } else { c = ImplicitConversion(fromElements[i], toElements[i]); } if (!c.IsValid) return Conversion.None; elementConversions[i] = c; } return Conversion.TupleConversion(elementConversions.ToImmutableArray()); } #endregion #region BetterConversion /// /// Gets the better conversion (from expression) (C# 8.0 spec, §12.6.4.5) /// /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2) { bool t1Exact = IsExactlyMatching(resolveResult, t1); bool t2Exact = IsExactlyMatching(resolveResult, t2); if (t1Exact && !t2Exact) return 1; if (t2Exact && !t1Exact) return 2; if (!t1Exact && !t2Exact) { bool c1ImplicitSpanConversion = IsImplicitSpanConversion(resolveResult.Type, t1); bool c2ImplicitSpanConversion = IsImplicitSpanConversion(resolveResult.Type, t2); if (c1ImplicitSpanConversion && !c2ImplicitSpanConversion) return 1; if (c2ImplicitSpanConversion && !c1ImplicitSpanConversion) return 2; } if (t1Exact == t2Exact) { int r = BetterConversionTarget(t1, t2); if (r != 0) return r; } LambdaResolveResult lambda = resolveResult as LambdaResolveResult; if (lambda != null) { if (!lambda.IsAnonymousMethod) { t1 = UnpackExpressionTreeType(t1); t2 = UnpackExpressionTreeType(t2); } IMethod m1 = t1.GetDelegateInvokeMethod(); IMethod m2 = t2.GetDelegateInvokeMethod(); if (m1 == null || m2 == null) return 0; if (m1.Parameters.Count != m2.Parameters.Count) return 0; IType[] parameterTypes = new IType[m1.Parameters.Count]; for (int i = 0; i < parameterTypes.Length; i++) { parameterTypes[i] = m1.Parameters[i].Type; if (!parameterTypes[i].Equals(m2.Parameters[i].Type)) return 0; } if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count) return 0; IType ret1 = m1.ReturnType; IType ret2 = m2.ReturnType; if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void) return 2; if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void) return 1; IType inferredRet = lambda.GetInferredReturnType(parameterTypes); int r = BetterConversion(inferredRet, ret1, ret2); if (r == 0 && lambda.IsAsync) { ret1 = UnpackTask(ret1); ret2 = UnpackTask(ret2); inferredRet = UnpackTask(inferredRet); if (ret1 != null && ret2 != null && inferredRet != null) r = BetterConversion(inferredRet, ret1, ret2); } return r; } else { return BetterConversion(resolveResult.Type, t1, t2); } } /// /// Gets whether an expression E exactly matches a type T (C# 8.0 spec, §12.6.4.6) /// bool IsExactlyMatching(ResolveResult e, IType t) { var s = e.Type; if (IdentityConversion(s, t)) return true; if (e is LambdaResolveResult lambda) { if (!lambda.IsAnonymousMethod) { t = UnpackExpressionTreeType(t); } IMethod m = t.GetDelegateInvokeMethod(); if (m == null) return false; IType[] parameterTypes = new IType[m.Parameters.Count]; for (int i = 0; i < parameterTypes.Length; i++) parameterTypes[i] = m.Parameters[i].Type; var x = lambda.GetInferredReturnType(parameterTypes); var y = m.ReturnType; if (IdentityConversion(x, y)) return true; if (lambda.IsAsync) { x = UnpackTask(x); y = UnpackTask(y); } if (x != null && y != null) return IsExactlyMatching(new ResolveResult(x), y); return false; } else { return false; } } /// /// Unpacks the generic TaskType[T]. Returns null if the input is not TaskType[T]. /// static IType UnpackTask(IType type) { return (TaskType.IsTask(type) || TaskType.IsCustomTask(type, out _)) && type.TypeParameterCount == 1 ? type.TypeArguments[0] : null; } /// /// Gets the better conversion (from type) (C# 4.0 spec, §7.5.3.4) /// /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better public int BetterConversion(IType s, IType t1, IType t2) { bool ident1 = IdentityConversion(s, t1); bool ident2 = IdentityConversion(s, t2); if (ident1 && !ident2) return 1; if (ident2 && !ident1) return 2; return BetterConversionTarget(t1, t2); } /// /// Gets the better conversion target (C# 9.0 spec, §12.6.4.7) /// /// 0 = neither is better; 1 = t1 is better; 2 = t2 is better int BetterConversionTarget(IType t1, IType t2) { if (t1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { if (t2.IsKnownType(KnownTypeCode.SpanOfT)) { if (IdentityConversion(t1.TypeArguments[0], t2.TypeArguments[0])) return 1; } if (t2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { bool t1To2 = ImplicitConversion(t1.TypeArguments[0], t2.TypeArguments[0]).IsValid; bool t2To1 = ImplicitConversion(t2.TypeArguments[0], t1.TypeArguments[0]).IsValid; if (t1To2 && !t2To1) return 1; } } if (t2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { if (t1.IsKnownType(KnownTypeCode.SpanOfT)) { if (IdentityConversion(t2.TypeArguments[0], t1.TypeArguments[0])) return 2; } if (t1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) { bool t1To2 = ImplicitConversion(t1.TypeArguments[0], t2.TypeArguments[0]).IsValid; bool t2To1 = ImplicitConversion(t2.TypeArguments[0], t1.TypeArguments[0]).IsValid; if (t2To1 && !t1To2) return 2; } } { bool t1To2 = ImplicitConversion(t1, t2).IsValid; bool t2To1 = ImplicitConversion(t2, t1).IsValid; if (t1To2 && !t2To1) return 1; if (t2To1 && !t1To2) return 2; } var s1 = UnpackTask(t1); var s2 = UnpackTask(t2); if (s1 != null && s2 != null) return BetterConversionTarget(s1, s2); TypeCode t1Code = ReflectionHelper.GetTypeCode(t1); TypeCode t2Code = ReflectionHelper.GetTypeCode(t2); if (IsBetterIntegralType(t1Code, t2Code)) return 1; if (IsBetterIntegralType(t2Code, t1Code)) return 2; return 0; } bool IsBetterIntegralType(TypeCode t1, TypeCode t2) { // signed types are better than unsigned types switch (t1) { case TypeCode.SByte: return t2 == TypeCode.Byte || t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; case TypeCode.Int16: return t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; case TypeCode.Int32: return t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64; case TypeCode.Int64: return t2 == TypeCode.UInt64; default: return false; } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents the result of a method, constructor or indexer invocation. /// Provides additional C#-specific information for InvocationResolveResult. /// public class CSharpInvocationResolveResult : InvocationResolveResult { public readonly OverloadResolutionErrors OverloadResolutionErrors; /// /// Gets whether this invocation is calling an extension method using extension method syntax. /// public readonly bool IsExtensionMethodInvocation; /// /// Gets whether this invocation is calling a delegate (without explicitly calling ".Invoke()"). /// public readonly bool IsDelegateInvocation; /// /// Gets whether a params-Array is being used in its expanded form. /// public readonly bool IsExpandedForm; readonly IReadOnlyList argumentToParameterMap; public CSharpInvocationResolveResult( ResolveResult targetResult, IParameterizedMember member, IList arguments, OverloadResolutionErrors overloadResolutionErrors = OverloadResolutionErrors.None, bool isExtensionMethodInvocation = false, bool isExpandedForm = false, bool isDelegateInvocation = false, IReadOnlyList argumentToParameterMap = null, IList initializerStatements = null, IType returnTypeOverride = null ) : base(targetResult, member, arguments, initializerStatements, returnTypeOverride) { this.OverloadResolutionErrors = overloadResolutionErrors; this.IsExtensionMethodInvocation = isExtensionMethodInvocation; this.IsExpandedForm = isExpandedForm; this.IsDelegateInvocation = isDelegateInvocation; this.argumentToParameterMap = argumentToParameterMap; } public override bool IsError { get { return this.OverloadResolutionErrors != OverloadResolutionErrors.None; } } /// /// Gets an array that maps argument indices to parameter indices. /// For arguments that could not be mapped to any parameter, the value will be -1. /// /// parameterIndex = ArgumentToParameterMap[argumentIndex] /// public IReadOnlyList GetArgumentToParameterMap() { return argumentToParameterMap; } public override IList GetArgumentsForCall() { ResolveResult[] results = new ResolveResult[Member.Parameters.Count]; List paramsArguments = IsExpandedForm ? new List() : null; // map arguments to parameters: for (int i = 0; i < Arguments.Count; i++) { int mappedTo; if (argumentToParameterMap != null) mappedTo = argumentToParameterMap[i]; else mappedTo = IsExpandedForm ? Math.Min(i, results.Length - 1) : i; if (mappedTo >= 0 && mappedTo < results.Length) { if (IsExpandedForm && mappedTo == results.Length - 1) { paramsArguments.Add(Arguments[i]); } else { var narr = Arguments[i] as NamedArgumentResolveResult; if (narr != null) results[mappedTo] = narr.Argument; else results[mappedTo] = Arguments[i]; } } } if (IsExpandedForm) { IType arrayType = Member.Parameters.Last().Type; IType int32 = Member.Compilation.FindType(KnownTypeCode.Int32); ResolveResult[] sizeArguments = { new ConstantResolveResult(int32, paramsArguments.Count) }; results[results.Length - 1] = new ArrayCreateResolveResult(arrayType, sizeArguments, paramsArguments); } for (int i = 0; i < results.Length; i++) { if (results[i] == null) { if (Member.Parameters[i].IsOptional) { results[i] = new ConstantResolveResult(Member.Parameters[i].Type, Member.Parameters[i].GetConstantValue()); } else { results[i] = ErrorResolveResult.UnknownError; } } } return results; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { sealed class CSharpOperators { readonly ICompilation compilation; private CSharpOperators(ICompilation compilation) { this.compilation = compilation; InitParameterArrays(); } /// /// Gets the CSharpOperators instance for the specified . /// This will make use of the context's cache manager (if available) to reuse the CSharpOperators instance. /// public static CSharpOperators Get(ICompilation compilation) { CacheManager cache = compilation.CacheManager; CSharpOperators? operators = (CSharpOperators?)cache.GetShared(typeof(CSharpOperators)); if (operators == null) { operators = (CSharpOperators)cache.GetOrAddShared(typeof(CSharpOperators), new CSharpOperators(compilation)); } return operators; } #region class OperatorMethod OperatorMethod[] Lift(params OperatorMethod[] methods) { List result = new List(methods); foreach (OperatorMethod method in methods) { OperatorMethod? lifted = method.Lift(this); if (lifted != null) result.Add(lifted); } return result.ToArray(); } IParameter[] normalParameters = new IParameter[(int)(TypeCode.String + 1 - TypeCode.Object)]; IParameter[] nullableParameters = new IParameter[(int)(TypeCode.Decimal + 1 - TypeCode.Boolean)]; void InitParameterArrays() { for (TypeCode i = TypeCode.Object; i <= TypeCode.String; i++) { normalParameters[i - TypeCode.Object] = new DefaultParameter(compilation.FindType(i), string.Empty); } for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) { IType type = NullableType.Create(compilation, compilation.FindType(i)); nullableParameters[i - TypeCode.Boolean] = new DefaultParameter(type, string.Empty); } } IParameter MakeParameter(TypeCode code) { return normalParameters[code - TypeCode.Object]; } IParameter MakeNullableParameter(IParameter normalParameter) { for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) { if (normalParameter == normalParameters[i - TypeCode.Object]) return nullableParameters[i - TypeCode.Boolean]; } throw new ArgumentException(); } internal class OperatorMethod : IParameterizedMember { readonly ICompilation compilation; internal readonly List parameters = new List(); protected OperatorMethod(ICompilation compilation) { this.compilation = compilation; } public IReadOnlyList Parameters { get { return parameters; } } public IType ReturnType { get; internal set; } = null!; // initialized by derived class ctor public ICompilation Compilation { get { return compilation; } } public virtual OperatorMethod? Lift(CSharpOperators operators) { return null; } public System.Reflection.Metadata.EntityHandle MetadataToken => default; ITypeDefinition? IEntity.DeclaringTypeDefinition { get { return null; } } public IType DeclaringType => SpecialType.UnknownType; IMember IMember.MemberDefinition { get { return this; } } IEnumerable IMember.ExplicitlyImplementedInterfaceMembers { get { return EmptyList.Instance; } } bool IMember.IsVirtual { get { return false; } } bool IMember.IsOverride { get { return false; } } bool IMember.IsOverridable { get { return false; } } SymbolKind ISymbol.SymbolKind { get { return SymbolKind.Operator; } } IEnumerable IEntity.GetAttributes() => EmptyList.Instance; bool IEntity.HasAttribute(KnownAttribute attribute) => false; IAttribute? IEntity.GetAttribute(KnownAttribute attribute) => null; Accessibility IEntity.Accessibility { get { return Accessibility.Public; } } bool IEntity.IsStatic { get { return true; } } bool IEntity.IsAbstract { get { return false; } } bool IEntity.IsSealed { get { return false; } } bool IMember.IsExplicitInterfaceImplementation { get { return false; } } IModule IEntity.ParentModule { get { return compilation.MainModule; } } TypeParameterSubstitution IMember.Substitution { get { return TypeParameterSubstitution.Identity; } } IMember IMember.Specialize(TypeParameterSubstitution substitution) { if (TypeParameterSubstitution.Identity.Equals(substitution)) return this; throw new NotSupportedException(); } string INamedElement.FullName { get { return "operator"; } } public string Name { get { return "operator"; } } string INamedElement.Namespace { get { return string.Empty; } } string INamedElement.ReflectionName { get { return "operator"; } } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append(ReturnType + " operator("); for (int i = 0; i < parameters.Count; i++) { if (i > 0) b.Append(", "); b.Append(parameters[i].Type); } b.Append(')'); return b.ToString(); } bool IMember.Equals(IMember? obj, TypeVisitor? typeNormalization) { return this == obj; } } #endregion #region Unary operator class definitions internal class UnaryOperatorMethod : OperatorMethod { public virtual bool CanEvaluateAtCompileTime { get { return false; } } public virtual object? Invoke(CSharpResolver resolver, object? input) { throw new NotSupportedException(); } public UnaryOperatorMethod(ICompilation compilation) : base(compilation) { } } sealed class LambdaUnaryOperatorMethod : UnaryOperatorMethod { readonly Func func; public LambdaUnaryOperatorMethod(CSharpOperators operators, Func func) : base(operators.compilation) { TypeCode typeCode = Type.GetTypeCode(typeof(T)); this.ReturnType = operators.compilation.FindType(typeCode); parameters.Add(operators.MakeParameter(typeCode)); this.func = func; } public override bool CanEvaluateAtCompileTime { get { return true; } } public override object? Invoke(CSharpResolver resolver, object? input) { if (input == null) return null; return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input)); } public override OperatorMethod Lift(CSharpOperators operators) { return new LiftedUnaryOperatorMethod(operators, this); } } sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, ILiftedOperator { UnaryOperatorMethod baseMethod; public LiftedUnaryOperatorMethod(CSharpOperators operators, UnaryOperatorMethod baseMethod) : base(operators.compilation) { this.baseMethod = baseMethod; this.ReturnType = NullableType.Create(baseMethod.Compilation, baseMethod.ReturnType); parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); } public IReadOnlyList NonLiftedParameters => baseMethod.Parameters; public IType NonLiftedReturnType => baseMethod.ReturnType; } #endregion #region Unary operator definitions // C# 4.0 spec: §7.7.1 Unary plus operator OperatorMethod[]? unaryPlusOperators; public OperatorMethod[] UnaryPlusOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref unaryPlusOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref unaryPlusOperators, Lift( new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i), new LambdaUnaryOperatorMethod(this, i => +i) )); } } } // C# 4.0 spec: §7.7.2 Unary minus operator OperatorMethod[]? uncheckedUnaryMinusOperators; public OperatorMethod[] UncheckedUnaryMinusOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref uncheckedUnaryMinusOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref uncheckedUnaryMinusOperators, Lift( new LambdaUnaryOperatorMethod(this, i => unchecked(-i)), new LambdaUnaryOperatorMethod(this, i => unchecked(-i)), new LambdaUnaryOperatorMethod(this, i => unchecked(-i)), new LambdaUnaryOperatorMethod(this, i => unchecked(-i)), new LambdaUnaryOperatorMethod(this, i => unchecked(-i)) )); } } } OperatorMethod[]? checkedUnaryMinusOperators; public OperatorMethod[] CheckedUnaryMinusOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref checkedUnaryMinusOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref checkedUnaryMinusOperators, Lift( new LambdaUnaryOperatorMethod(this, i => checked(-i)), new LambdaUnaryOperatorMethod(this, i => checked(-i)), new LambdaUnaryOperatorMethod(this, i => checked(-i)), new LambdaUnaryOperatorMethod(this, i => checked(-i)), new LambdaUnaryOperatorMethod(this, i => checked(-i)) )); } } } // C# 4.0 spec: §7.7.3 Logical negation operator OperatorMethod[]? logicalNegationOperators; public OperatorMethod[] LogicalNegationOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalNegationOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref logicalNegationOperators, Lift( new LambdaUnaryOperatorMethod(this, b => !b) )); } } } // C# 4.0 spec: §7.7.4 Bitwise complement operator OperatorMethod[]? bitwiseComplementOperators; public OperatorMethod[] BitwiseComplementOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseComplementOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref bitwiseComplementOperators, Lift( new LambdaUnaryOperatorMethod(this, i => ~i), new LambdaUnaryOperatorMethod(this, i => ~i), new LambdaUnaryOperatorMethod(this, i => ~i), new LambdaUnaryOperatorMethod(this, i => ~i) )); } } } #endregion #region Binary operator class definitions internal class BinaryOperatorMethod : OperatorMethod { public virtual bool CanEvaluateAtCompileTime { get { return false; } } public virtual object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) { throw new NotSupportedException(); } public BinaryOperatorMethod(ICompilation compilation) : base(compilation) { } } sealed class LambdaBinaryOperatorMethod : BinaryOperatorMethod { readonly Func checkedFunc; readonly Func uncheckedFunc; public LambdaBinaryOperatorMethod(CSharpOperators operators, Func func) : this(operators, func, func) { } public LambdaBinaryOperatorMethod(CSharpOperators operators, Func checkedFunc, Func uncheckedFunc) : base(operators.compilation) { TypeCode t1 = Type.GetTypeCode(typeof(T1)); this.ReturnType = operators.compilation.FindType(t1); parameters.Add(operators.MakeParameter(t1)); parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); this.checkedFunc = checkedFunc; this.uncheckedFunc = uncheckedFunc; } public override bool CanEvaluateAtCompileTime { get { return true; } } public override object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) { if (lhs == null || rhs == null) return null; Func func = resolver.CheckForOverflow ? checkedFunc : uncheckedFunc; return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), (T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); } public override OperatorMethod Lift(CSharpOperators operators) { return new LiftedBinaryOperatorMethod(operators, this); } } sealed class LiftedBinaryOperatorMethod : BinaryOperatorMethod, ILiftedOperator { readonly BinaryOperatorMethod baseMethod; public LiftedBinaryOperatorMethod(CSharpOperators operators, BinaryOperatorMethod baseMethod) : base(operators.compilation) { this.baseMethod = baseMethod; this.ReturnType = NullableType.Create(operators.compilation, baseMethod.ReturnType); parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[1])); } public IReadOnlyList NonLiftedParameters => baseMethod.Parameters; public IType NonLiftedReturnType => baseMethod.ReturnType; } #endregion #region Arithmetic operators // C# 4.0 spec: §7.8.1 Multiplication operator OperatorMethod[]? multiplicationOperators; public OperatorMethod[] MultiplicationOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref multiplicationOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref multiplicationOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)) )); } } } // C# 4.0 spec: §7.8.2 Division operator OperatorMethod[]? divisionOperators; public OperatorMethod[] DivisionOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref divisionOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref divisionOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)) )); } } } // C# 4.0 spec: §7.8.3 Remainder operator OperatorMethod[]? remainderOperators; public OperatorMethod[] RemainderOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref remainderOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref remainderOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)) )); } } } // C# 4.0 spec: §7.8.3 Addition operator OperatorMethod[]? additionOperators; public OperatorMethod[] AdditionOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref additionOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref additionOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new StringConcatenation(this, TypeCode.String, TypeCode.String), new StringConcatenation(this, TypeCode.String, TypeCode.Object), new StringConcatenation(this, TypeCode.Object, TypeCode.String) )); } } } // not in this list, but handled manually: enum addition, delegate combination sealed class StringConcatenation : BinaryOperatorMethod { bool canEvaluateAtCompileTime; public StringConcatenation(CSharpOperators operators, TypeCode p1, TypeCode p2) : base(operators.compilation) { this.canEvaluateAtCompileTime = p1 == TypeCode.String && p2 == TypeCode.String; this.ReturnType = operators.compilation.FindType(KnownTypeCode.String); parameters.Add(operators.MakeParameter(p1)); parameters.Add(operators.MakeParameter(p2)); } public override bool CanEvaluateAtCompileTime { get { return canEvaluateAtCompileTime; } } public override object? Invoke(CSharpResolver? resolver, object? lhs, object? rhs) { return string.Concat(lhs, rhs); } } // C# 4.0 spec: §7.8.4 Subtraction operator OperatorMethod[]? subtractionOperators; public OperatorMethod[] SubtractionOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref subtractionOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref subtractionOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)) )); } } } // C# 4.0 spec: §7.8.5 Shift operators OperatorMethod[]? shiftLeftOperators; public OperatorMethod[] ShiftLeftOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref shiftLeftOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref shiftLeftOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(this, (a, b) => a << b) )); } } } OperatorMethod[]? shiftRightOperators; public OperatorMethod[] ShiftRightOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref shiftRightOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref shiftRightOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b) )); } } } OperatorMethod[]? unsignedShiftRightOperators; public OperatorMethod[] UnsignedShiftRightOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref unsignedShiftRightOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref unsignedShiftRightOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => (int)((uint)a >> b)), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => (long)((ulong)a >> b)), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b) )); } } } #endregion #region Equality operators sealed class EqualityOperatorMethod : BinaryOperatorMethod { public readonly TypeCode Type; public readonly bool Negate; public EqualityOperatorMethod(CSharpOperators operators, TypeCode type, bool negate) : base(operators.compilation) { this.Negate = negate; this.Type = type; this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); parameters.Add(operators.MakeParameter(type)); parameters.Add(operators.MakeParameter(type)); } public override bool CanEvaluateAtCompileTime { get { return Type != TypeCode.Object; } } public override object Invoke(CSharpResolver resolver, object? lhs, object? rhs) { if (lhs == null && rhs == null) return !Negate; // ==: true; !=: false if (lhs == null || rhs == null) return Negate; // ==: false; !=: true lhs = resolver.CSharpPrimitiveCast(Type, lhs); rhs = resolver.CSharpPrimitiveCast(Type, rhs); bool equal; if (Type == TypeCode.Single) { equal = (float)lhs == (float)rhs; } else if (Type == TypeCode.Double) { equal = (double)lhs == (double)rhs; } else { equal = object.Equals(lhs, rhs); } return equal ^ Negate; } public override OperatorMethod? Lift(CSharpOperators operators) { if (Type == TypeCode.Object || Type == TypeCode.String) return null; else return new LiftedEqualityOperatorMethod(operators, this); } } sealed class LiftedEqualityOperatorMethod : BinaryOperatorMethod, ILiftedOperator { readonly EqualityOperatorMethod baseMethod; public LiftedEqualityOperatorMethod(CSharpOperators operators, EqualityOperatorMethod baseMethod) : base(operators.compilation) { this.baseMethod = baseMethod; this.ReturnType = baseMethod.ReturnType; IParameter p = operators.MakeNullableParameter(baseMethod.Parameters[0]); parameters.Add(p); parameters.Add(p); } public override bool CanEvaluateAtCompileTime { get { return baseMethod.CanEvaluateAtCompileTime; } } public override object Invoke(CSharpResolver resolver, object? lhs, object? rhs) { return baseMethod.Invoke(resolver, lhs, rhs); } public IReadOnlyList NonLiftedParameters => baseMethod.Parameters; public IType NonLiftedReturnType => baseMethod.ReturnType; } // C# 4.0 spec: §7.10 Relational and type-testing operators static readonly TypeCode[] valueEqualityOperatorsFor = { TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Boolean }; OperatorMethod[]? valueEqualityOperators; public OperatorMethod[] ValueEqualityOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref valueEqualityOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref valueEqualityOperators, Lift( valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, false)).ToArray() )); } } } OperatorMethod[]? valueInequalityOperators; public OperatorMethod[] ValueInequalityOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref valueInequalityOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref valueInequalityOperators, Lift( valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, true)).ToArray() )); } } } OperatorMethod[]? referenceEqualityOperators; public OperatorMethod[] ReferenceEqualityOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref referenceEqualityOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref referenceEqualityOperators, Lift( new EqualityOperatorMethod(this, TypeCode.Object, false), new EqualityOperatorMethod(this, TypeCode.String, false) )); } } } OperatorMethod[]? referenceInequalityOperators; public OperatorMethod[] ReferenceInequalityOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref referenceInequalityOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref referenceInequalityOperators, Lift( new EqualityOperatorMethod(this, TypeCode.Object, true), new EqualityOperatorMethod(this, TypeCode.String, true) )); } } } #endregion #region Relational Operators sealed class RelationalOperatorMethod : BinaryOperatorMethod { readonly Func func; public RelationalOperatorMethod(CSharpOperators operators, Func func) : base(operators.compilation) { this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T1)))); parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); this.func = func; } public override bool CanEvaluateAtCompileTime { get { return true; } } public override object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) { if (lhs == null || rhs == null) return null; return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), (T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); } public override OperatorMethod Lift(CSharpOperators operators) { var lifted = new LiftedBinaryOperatorMethod(operators, this); lifted.ReturnType = this.ReturnType; // don't lift the return type for relational operators return lifted; } } OperatorMethod[]? lessThanOperators; public OperatorMethod[] LessThanOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref lessThanOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref lessThanOperators, Lift( new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b), new RelationalOperatorMethod(this, (a, b) => a < b) )); } } } OperatorMethod[]? lessThanOrEqualOperators; public OperatorMethod[] LessThanOrEqualOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref lessThanOrEqualOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref lessThanOrEqualOperators, Lift( new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b), new RelationalOperatorMethod(this, (a, b) => a <= b) )); } } } OperatorMethod[]? greaterThanOperators; public OperatorMethod[] GreaterThanOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref greaterThanOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref greaterThanOperators, Lift( new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b), new RelationalOperatorMethod(this, (a, b) => a > b) )); } } } OperatorMethod[]? greaterThanOrEqualOperators; public OperatorMethod[] GreaterThanOrEqualOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref greaterThanOrEqualOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref greaterThanOrEqualOperators, Lift( new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b), new RelationalOperatorMethod(this, (a, b) => a >= b) )); } } } #endregion #region Bitwise operators OperatorMethod[]? logicalAndOperators; public OperatorMethod[] LogicalAndOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalAndOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref logicalAndOperators, new OperatorMethod[] { new LambdaBinaryOperatorMethod(this, (a, b) => a & b) }); } } } OperatorMethod[]? bitwiseAndOperators; public OperatorMethod[] BitwiseAndOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseAndOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref bitwiseAndOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => a & b), new LambdaBinaryOperatorMethod(this, (a, b) => a & b), new LambdaBinaryOperatorMethod(this, (a, b) => a & b), new LambdaBinaryOperatorMethod(this, (a, b) => a & b), this.LogicalAndOperators[0] )); } } } OperatorMethod[]? logicalOrOperators; public OperatorMethod[] LogicalOrOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalOrOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref logicalOrOperators, new OperatorMethod[] { new LambdaBinaryOperatorMethod(this, (a, b) => a | b) }); } } } OperatorMethod[]? bitwiseOrOperators; public OperatorMethod[] BitwiseOrOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseOrOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref bitwiseOrOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => a | b), new LambdaBinaryOperatorMethod(this, (a, b) => a | b), new LambdaBinaryOperatorMethod(this, (a, b) => a | b), new LambdaBinaryOperatorMethod(this, (a, b) => a | b), this.LogicalOrOperators[0] )); } } } // Note: the logic for the lifted bool? bitwise operators is wrong; // we produce "true | null" = "null" when it should be true. However, this is irrelevant // because bool? cannot be a compile-time type. OperatorMethod[]? bitwiseXorOperators; public OperatorMethod[] BitwiseXorOperators { get { OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseXorOperators); if (ops != null) { return ops; } else { return LazyInit.GetOrSet(ref bitwiseXorOperators, Lift( new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b) )); } } } #endregion #region User-defined operators public static IMethod? LiftUserDefinedOperator(IMethod m) { if (IsComparisonOperator(m)) { if (!m.ReturnType.IsKnownType(KnownTypeCode.Boolean)) return null; // cannot lift this operator } else { if (!NullableType.IsNonNullableValueType(m.ReturnType)) return null; // cannot lift this operator } for (int i = 0; i < m.Parameters.Count; i++) { if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type)) return null; // cannot lift this operator } return new LiftedUserDefinedOperator(m); } internal static bool IsComparisonOperator(IMethod m) { return m.IsOperator && m.Parameters.Count == 2 && (OperatorDeclaration.GetOperatorType(m.Name)?.IsComparisonOperator() ?? false); } sealed class LiftedUserDefinedOperator : SpecializedMethod, ILiftedOperator { internal readonly IParameterizedMember nonLiftedOperator; public LiftedUserDefinedOperator(IMethod nonLiftedMethod) : base((IMethod)nonLiftedMethod.MemberDefinition, nonLiftedMethod.Substitution) { this.nonLiftedOperator = nonLiftedMethod; var compilation = nonLiftedMethod.Compilation; var substitution = nonLiftedMethod.Substitution; this.Parameters = base.CreateParameters( type => NullableType.Create(compilation, type.AcceptVisitor(substitution))); // Comparison operators keep the 'bool' return type even when lifted. if (IsComparisonOperator(nonLiftedMethod)) this.ReturnType = nonLiftedMethod.ReturnType; else this.ReturnType = NullableType.Create(compilation, nonLiftedMethod.ReturnType.AcceptVisitor(substitution)); } public IReadOnlyList NonLiftedParameters => nonLiftedOperator.Parameters; public IType NonLiftedReturnType => nonLiftedOperator.ReturnType; public override bool Equals(object? obj) { LiftedUserDefinedOperator? op = obj as LiftedUserDefinedOperator; return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator); } public override int GetHashCode() { return nonLiftedOperator.GetHashCode() ^ 0x7191254; } } #endregion } /// /// Implement this interface to give overload resolution a hint that the member represents a lifted operator, /// which is used in the tie-breaking rules. /// public interface ILiftedOperator : IParameterizedMember { IType NonLiftedReturnType { get; } IReadOnlyList NonLiftedParameters { get; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Contains the main resolver logic. /// /// /// This class is thread-safe. /// public class CSharpResolver : ICodeContext { static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError; readonly ICompilation compilation; internal readonly CSharpConversions conversions; readonly CSharpTypeResolveContext context; readonly bool checkForOverflow; readonly bool isWithinLambdaExpression; #region Constructor public CSharpResolver(ICompilation compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.conversions = CSharpConversions.Get(compilation); this.context = new CSharpTypeResolveContext(compilation.MainModule); } public CSharpResolver(CSharpTypeResolveContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); this.compilation = context.Compilation; this.conversions = CSharpConversions.Get(compilation); this.context = context; if (context.CurrentTypeDefinition != null) currentTypeDefinitionCache = new TypeDefinitionCache(context.CurrentTypeDefinition); } private CSharpResolver(ICompilation compilation, CSharpConversions conversions, CSharpTypeResolveContext context, bool checkForOverflow, bool isWithinLambdaExpression, TypeDefinitionCache currentTypeDefinitionCache, ImmutableStack> localVariableStack, ObjectInitializerContext objectInitializerStack) { this.compilation = compilation; this.conversions = conversions; this.context = context; this.checkForOverflow = checkForOverflow; this.isWithinLambdaExpression = isWithinLambdaExpression; this.currentTypeDefinitionCache = currentTypeDefinitionCache; this.localVariableStack = localVariableStack; this.objectInitializerStack = objectInitializerStack; } #endregion #region Properties /// /// Gets the compilation used by the resolver. /// public ICompilation Compilation { get { return compilation; } } /// /// Gets the current type resolve context. /// public CSharpTypeResolveContext CurrentTypeResolveContext { get { return context; } } IModule ITypeResolveContext.CurrentModule { get { return context.CurrentModule; } } CSharpResolver WithContext(CSharpTypeResolveContext newContext) { return new CSharpResolver(compilation, conversions, newContext, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); } /// /// Gets whether the current context is checked. /// public bool CheckForOverflow { get { return checkForOverflow; } } /// /// Sets whether the current context is checked. /// public CSharpResolver WithCheckForOverflow(bool checkForOverflow) { if (checkForOverflow == this.checkForOverflow) return this; return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); } /// /// Gets whether the resolver is currently within a lambda expression or anonymous method. /// public bool IsWithinLambdaExpression { get { return isWithinLambdaExpression; } } /// /// Sets whether the resolver is currently within a lambda expression. /// public CSharpResolver WithIsWithinLambdaExpression(bool isWithinLambdaExpression) { return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack); } /// /// Gets the current member definition that is used to look up identifiers as parameters /// or type parameters. /// public IMember CurrentMember { get { return context.CurrentMember; } } /// /// Sets the current member definition. /// /// Don't forget to also set CurrentTypeDefinition when setting CurrentMember; /// setting one of the properties does not automatically set the other. public CSharpResolver WithCurrentMember(IMember member) { return WithContext(context.WithCurrentMember(member)); } ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member) { return WithCurrentMember(member); } /// /// Gets the current using scope that is used to look up identifiers as class names. /// public UsingScope CurrentUsingScope { get { return context.CurrentUsingScope; } } /// /// Sets the current using scope that is used to look up identifiers as class names. /// public CSharpResolver WithCurrentUsingScope(UsingScope usingScope) { return WithContext(context.WithUsingScope(usingScope)); } #endregion #region Per-CurrentTypeDefinition Cache readonly TypeDefinitionCache currentTypeDefinitionCache; /// /// Gets the current type definition. /// public ITypeDefinition CurrentTypeDefinition { get { return context.CurrentTypeDefinition; } } /// /// Sets the current type definition. /// public CSharpResolver WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { if (this.CurrentTypeDefinition == typeDefinition) return this; TypeDefinitionCache newTypeDefinitionCache; if (typeDefinition != null) newTypeDefinitionCache = new TypeDefinitionCache(typeDefinition); else newTypeDefinitionCache = null; return new CSharpResolver(compilation, conversions, context.WithCurrentTypeDefinition(typeDefinition), checkForOverflow, isWithinLambdaExpression, newTypeDefinitionCache, localVariableStack, objectInitializerStack); } ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { return WithCurrentTypeDefinition(typeDefinition); } sealed class TypeDefinitionCache { public readonly ITypeDefinition TypeDefinition; public readonly Dictionary SimpleNameLookupCacheExpression = new Dictionary(); public readonly Dictionary SimpleNameLookupCacheInvocationTarget = new Dictionary(); public readonly Dictionary SimpleTypeLookupCache = new Dictionary(); public TypeDefinitionCache(ITypeDefinition typeDefinition) { this.TypeDefinition = typeDefinition; } } #endregion #region Local Variable Management // We store the local variables in an immutable stack. // The beginning of a block is marked by a null entry. // This data structure is used to allow efficient cloning of the resolver with its local variable context. readonly ImmutableStack> localVariableStack = ImmutableStack>.Empty; CSharpResolver WithLocalVariableStack(ImmutableStack> stack) { return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, stack, objectInitializerStack); } /// /// Adds new variableŝ or lambda parameters to the current block. /// public CSharpResolver AddVariables(Dictionary variables) { if (variables == null) throw new ArgumentNullException(nameof(variables)); return WithLocalVariableStack(localVariableStack.Push(variables)); } /// /// Gets all currently visible local variables and lambda parameters. /// Does not include method parameters. /// public IEnumerable LocalVariables { get { return localVariableStack.SelectMany(s => s.Values); } } #endregion #region Object Initializer Context sealed class ObjectInitializerContext { internal readonly ResolveResult initializedObject; internal readonly ObjectInitializerContext prev; public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev) { this.initializedObject = initializedObject; this.prev = prev; } } readonly ObjectInitializerContext objectInitializerStack; CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack) { return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, stack); } /// /// Pushes the type of the object that is currently being initialized. /// public CSharpResolver PushObjectInitializer(ResolveResult initializedObject) { if (initializedObject == null) throw new ArgumentNullException(nameof(initializedObject)); return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack)); } public CSharpResolver PopObjectInitializer() { if (objectInitializerStack == null) throw new InvalidOperationException(); return WithObjectInitializerStack(objectInitializerStack.prev); } /// /// Gets whether this context is within an object initializer. /// public bool IsInObjectInitializer { get { return objectInitializerStack != null; } } /// /// Gets the current object initializer. This usually is an /// or (for nested initializers) a semantic tree based on an . /// Returns ErrorResolveResult if there is no object initializer. /// public ResolveResult CurrentObjectInitializer { get { return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult; } } /// /// Gets the type of the object currently being initialized. /// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer /// has unknown type). /// public IType CurrentObjectInitializerType { get { return CurrentObjectInitializer.Type; } } #endregion #region ResolveUnaryOperator #region ResolveUnaryOperator method public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression) { if (expression.Type.Kind == TypeKind.Dynamic) { if (op == UnaryOperatorType.Await) { return new AwaitResolveResult(SpecialType.Dynamic, new DynamicInvocationResolveResult(new DynamicMemberResolveResult(expression, "GetAwaiter"), DynamicInvocationType.Invocation, EmptyList.Instance), SpecialType.Dynamic, null, null, null); } else { return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression); } } // C# 4.0 spec: §7.3.3 Unary operator overload resolution string overloadableOperatorName = GetOverloadableOperatorName(op); if (overloadableOperatorName == null) { switch (op) { case UnaryOperatorType.Dereference: PointerType p = expression.Type as PointerType; if (p != null) return UnaryOperatorResolveResult(p.ElementType, op, expression); else return ErrorResult; case UnaryOperatorType.AddressOf: return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression); case UnaryOperatorType.Await: { ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList.Instance, NameLookupMode.InvocationTarget); ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, Empty.Array, argumentNames: null, allowOptionalParameters: false); var lookup = CreateMemberLookup(); IMethod getResultMethod; IType awaitResultType; var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList.Instance, true) as MethodGroupResolveResult; if (getResultMethodGroup != null) { var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, Empty.Array, allowExtensionMethods: false, conversions: conversions); getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null; awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType; } else { getResultMethod = null; awaitResultType = SpecialType.UnknownType; } var isCompletedRR = lookup.Lookup(getAwaiterInvocation, "IsCompleted", EmptyList.Instance, false); var isCompletedProperty = (isCompletedRR is MemberResolveResult ? ((MemberResolveResult)isCompletedRR).Member as IProperty : null); if (isCompletedProperty != null && (!isCompletedProperty.ReturnType.IsKnownType(KnownTypeCode.Boolean) || !isCompletedProperty.CanGet)) isCompletedProperty = null; /* var interfaceOnCompleted = compilation.FindType(KnownTypeCode.INotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "OnCompleted"); var interfaceUnsafeOnCompleted = compilation.FindType(KnownTypeCode.ICriticalNotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "UnsafeOnCompleted"); IMethod onCompletedMethod = null; var candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceUnsafeOnCompleted)).ToList(); if (candidates.Count == 0) { candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceOnCompleted)).ToList(); if (candidates.Count == 1) onCompletedMethod = candidates[0]; } else if (candidates.Count == 1) { onCompletedMethod = candidates[0]; } return new AwaitResolveResult(awaitResultType, getAwaiterInvocation, getAwaiterInvocation.Type, isCompletedProperty, onCompletedMethod, getResultMethod); */ // Not adjusted to TS changes for interface impls // But I believe this is dead code for ILSpy anyways... throw new NotImplementedException(); } default: return ErrorResolveResult.UnknownError; } } // If the type is nullable, get the underlying type: IType type = NullableType.GetUnderlyingType(expression.Type); bool isNullable = NullableType.IsNullable(expression.Type); // the operator is overloadable: OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression }); foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) { userDefinedOperatorOR.AddCandidate(candidate); } if (userDefinedOperatorOR.FoundApplicableCandidate) { return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); } expression = UnaryNumericPromotion(op, ref type, isNullable, expression); CSharpOperators.OperatorMethod[] methodGroup; CSharpOperators operators = CSharpOperators.Get(compilation); switch (op) { case UnaryOperatorType.Increment: case UnaryOperatorType.Decrement: case UnaryOperatorType.PostIncrement: case UnaryOperatorType.PostDecrement: // C# 4.0 spec: §7.6.9 Postfix increment and decrement operators // C# 4.0 spec: §7.7.5 Prefix increment and decrement operators TypeCode code = ReflectionHelper.GetTypeCode(type); if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer || type.IsCSharpNativeIntegerType()) return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); else return new ErrorResolveResult(expression.Type); case UnaryOperatorType.Plus: if (type.IsCSharpNativeIntegerType()) { return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); } methodGroup = operators.UnaryPlusOperators; break; case UnaryOperatorType.Minus: if (type.IsCSharpNativeIntegerType()) { return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); } methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators; break; case UnaryOperatorType.Not: methodGroup = operators.LogicalNegationOperators; break; case UnaryOperatorType.BitNot: if (type.Kind == TypeKind.Enum) { if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) { // evaluate as (E)(~(U)x); var U = compilation.FindType(expression.ConstantValue.GetType()); var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue); var rr = ResolveUnaryOperator(op, unpackedEnum); rr = WithCheckForOverflow(false).ResolveCast(type, rr); if (rr.IsCompileTimeConstant) return rr; } return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); } else if (type.IsCSharpNativeIntegerType()) { return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable); } else { methodGroup = operators.BitwiseComplementOperators; break; } default: throw new InvalidOperationException(); } OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression }); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate; IType resultType = m.ReturnType; if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) { if (userDefinedOperatorOR.BestCandidate != null) { // If there are any user-defined operators, prefer those over the built-in operators. // It'll be a more informative error. return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); } else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) { // If the best candidate is ambiguous, just use the input type instead // of picking one of the ambiguous overloads. return new ErrorResolveResult(expression.Type); } else { return new ErrorResolveResult(resultType); } } else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) { object val; try { val = m.Invoke(this, expression.ConstantValue); } catch (ArithmeticException) { return new ErrorResolveResult(resultType); } return new ConstantResolveResult(resultType, val); } else { expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]); return UnaryOperatorResolveResult(resultType, op, expression, builtinOperatorOR.BestCandidate is ILiftedOperator); } } OperatorResolveResult UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult expression, bool isLifted = false) { return new OperatorResolveResult( resultType, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow), null, isLifted, new[] { expression }); } #endregion #region UnaryNumericPromotion ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression) { // C# 4.0 spec: §7.3.6.1 TypeCode code = ReflectionHelper.GetTypeCode(type); if (isNullable && type.Kind == TypeKind.Null) code = TypeCode.SByte; // cause promotion of null to int32 switch (op) { case UnaryOperatorType.Minus: if (code == TypeCode.UInt32) { type = compilation.FindType(KnownTypeCode.Int64); return Convert(expression, MakeNullable(type, isNullable), isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } goto case UnaryOperatorType.Plus; case UnaryOperatorType.Plus: case UnaryOperatorType.BitNot: if (code >= TypeCode.Char && code <= TypeCode.UInt16) { type = compilation.FindType(KnownTypeCode.Int32); return Convert(expression, MakeNullable(type, isNullable), isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } break; } return expression; } #endregion #region GetOverloadableOperatorName static string GetOverloadableOperatorName(UnaryOperatorType op) { switch (op) { case UnaryOperatorType.Not: return "op_LogicalNot"; case UnaryOperatorType.BitNot: return "op_OnesComplement"; case UnaryOperatorType.Minus: return "op_UnaryNegation"; case UnaryOperatorType.Plus: return "op_UnaryPlus"; case UnaryOperatorType.Increment: case UnaryOperatorType.PostIncrement: return "op_Increment"; case UnaryOperatorType.Decrement: case UnaryOperatorType.PostDecrement: return "op_Decrement"; default: return null; } } #endregion #endregion #region ResolveBinaryOperator #region ResolveBinaryOperator method public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) { if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) { lhs = Convert(lhs, SpecialType.Dynamic); rhs = Convert(rhs, SpecialType.Dynamic); return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs); } // C# 4.0 spec: §7.3.4 Binary operator overload resolution string overloadableOperatorName = GetOverloadableOperatorName(op); if (overloadableOperatorName == null) { // Handle logical and/or exactly as bitwise and/or: // - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator. // - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit. // - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit if (op == BinaryOperatorType.ConditionalAnd) { overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd); } else if (op == BinaryOperatorType.ConditionalOr) { overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr); } else if (op == BinaryOperatorType.NullCoalescing) { // null coalescing operator is not overloadable and needs to be handled separately return ResolveNullCoalescingOperator(lhs, rhs); } else { return ErrorResolveResult.UnknownError; } } // If the type is nullable, get the underlying type: bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type); IType lhsType = NullableType.GetUnderlyingType(lhs.Type); IType rhsType = NullableType.GetUnderlyingType(rhs.Type); // the operator is overloadable: OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); HashSet userOperatorCandidates = new HashSet(); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName)); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName)); foreach (var candidate in userOperatorCandidates) { userDefinedOperatorOR.AddCandidate(candidate); } if (userDefinedOperatorOR.FoundApplicableCandidate) { return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); } if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false || lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null) { isNullable = true; } if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight || op == BinaryOperatorType.UnsignedShiftRight) { // special case: the shift operators allow "var x = null << null", producing int?. if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) isNullable = true; // for shift operators, do unary promotion independently on both arguments lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs); rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs); } else { bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality; if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants)) return new ErrorResolveResult(lhs.Type); } // re-read underlying types after numeric promotion lhsType = NullableType.GetUnderlyingType(lhs.Type); rhsType = NullableType.GetUnderlyingType(rhs.Type); IEnumerable methodGroup; CSharpOperators operators = CSharpOperators.Get(compilation); switch (op) { case BinaryOperatorType.Multiply: methodGroup = operators.MultiplicationOperators; break; case BinaryOperatorType.Divide: methodGroup = operators.DivisionOperators; break; case BinaryOperatorType.Modulus: methodGroup = operators.RemainderOperators; break; case BinaryOperatorType.Add: methodGroup = operators.AdditionOperators; { if (lhsType.Kind == TypeKind.Enum) { // E operator +(E x, U y); IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable); if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) { return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); } } if (rhsType.Kind == TypeKind.Enum) { // E operator +(U x, E y); IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable); if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) { return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); } } if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { return BinaryOperatorResolveResult(lhsType, lhs, op, rhs); } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { return BinaryOperatorResolveResult(rhsType, lhs, op, rhs); } if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) return new ErrorResolveResult(SpecialType.NullType); } break; case BinaryOperatorType.Subtract: methodGroup = operators.SubtractionOperators; { if (lhsType.Kind == TypeKind.Enum) { // U operator –(E x, E y); if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs, allowConversionFromConstantZero: false)) { return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs); } // E operator –(E x, U y); IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable); if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) { return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); } } if (rhsType.Kind == TypeKind.Enum) { // U operator –(E x, E y); if (TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs)) { return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs); } // E operator -(U x, E y); IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable); if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) { return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); } } if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { return BinaryOperatorResolveResult(lhsType, lhs, op, rhs); } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { return BinaryOperatorResolveResult(rhsType, lhs, op, rhs); } if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) return new ErrorResolveResult(SpecialType.NullType); } break; case BinaryOperatorType.ShiftLeft: methodGroup = operators.ShiftLeftOperators; break; case BinaryOperatorType.ShiftRight: methodGroup = operators.ShiftRightOperators; break; case BinaryOperatorType.UnsignedShiftRight: methodGroup = operators.UnsignedShiftRightOperators; break; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: case BinaryOperatorType.LessThan: case BinaryOperatorType.GreaterThan: case BinaryOperatorType.LessThanOrEqual: case BinaryOperatorType.GreaterThanOrEqual: { if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) { // bool operator op(E x, E y); return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs); } else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) { // bool operator op(E x, E y); return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs); } else if (lhsType is PointerType && rhsType is PointerType) { return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs); } else if (lhsType.IsCSharpNativeIntegerType() || rhsType.IsCSharpNativeIntegerType()) { if (lhsType.Equals(rhsType)) return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs, isLifted: isNullable); else return new ErrorResolveResult(compilation.FindType(KnownTypeCode.Boolean)); } if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) { if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true && lhsType.Kind != TypeKind.Null && rhsType.Kind != TypeKind.Null && (conversions.IdentityConversion(lhsType, rhsType) || conversions.ExplicitConversion(lhsType, rhsType).IsReferenceConversion || conversions.ExplicitConversion(rhsType, lhsType).IsReferenceConversion)) { // If it's a reference comparison if (op == BinaryOperatorType.Equality) methodGroup = operators.ReferenceEqualityOperators; else methodGroup = operators.ReferenceInequalityOperators; break; } else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type) || IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) { // compare type parameter or nullable type with the null literal return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs); } } switch (op) { case BinaryOperatorType.Equality: methodGroup = operators.ValueEqualityOperators; break; case BinaryOperatorType.InEquality: methodGroup = operators.ValueInequalityOperators; break; case BinaryOperatorType.LessThan: methodGroup = operators.LessThanOperators; break; case BinaryOperatorType.GreaterThan: methodGroup = operators.GreaterThanOperators; break; case BinaryOperatorType.LessThanOrEqual: methodGroup = operators.LessThanOrEqualOperators; break; case BinaryOperatorType.GreaterThanOrEqual: methodGroup = operators.GreaterThanOrEqualOperators; break; default: throw new InvalidOperationException(); } } break; case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: { if (lhsType.Kind == TypeKind.Enum) { // bool operator op(E x, E y); if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs)) { return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); } } if (rhsType.Kind == TypeKind.Enum) { // bool operator op(E x, E y); if (TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs)) { return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); } } switch (op) { case BinaryOperatorType.BitwiseAnd: methodGroup = operators.BitwiseAndOperators; break; case BinaryOperatorType.BitwiseOr: methodGroup = operators.BitwiseOrOperators; break; case BinaryOperatorType.ExclusiveOr: methodGroup = operators.BitwiseXorOperators; break; default: throw new InvalidOperationException(); } } break; case BinaryOperatorType.ConditionalAnd: methodGroup = operators.LogicalAndOperators; break; case BinaryOperatorType.ConditionalOr: methodGroup = operators.LogicalOrOperators; break; default: throw new InvalidOperationException(); } if (lhsType.IsCSharpNativeIntegerType() || rhsType.IsCSharpNativeIntegerType()) { if (lhsType.Equals(rhsType)) { return BinaryOperatorResolveResult( isNullable ? NullableType.Create(compilation, lhsType) : lhsType, lhs, op, rhs, isLifted: isNullable); } // mixing nint/nuint is not allowed return new ErrorResolveResult(lhsType); } OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate; IType resultType = m.ReturnType; if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) { // If there are any user-defined operators, prefer those over the built-in operators. // It'll be a more informative error. if (userDefinedOperatorOR.BestCandidate != null) return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow)); else return new ErrorResolveResult(resultType); } else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) { object val; try { val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue); } catch (ArithmeticException) { return new ErrorResolveResult(resultType); } return new ConstantResolveResult(resultType, val); } else { lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]); rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]); return BinaryOperatorResolveResult(resultType, lhs, op, rhs, builtinOperatorOR.BestCandidate is ILiftedOperator); } } bool IsNullableTypeOrNonValueType(IType type) { return NullableType.IsNullable(type) || type.IsReferenceType != false; } ResolveResult BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, bool isLifted = false) { return new OperatorResolveResult( resultType, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow), null, isLifted, new[] { lhs, rhs }); } #endregion #region Pointer arithmetic CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, KnownTypeCode inputType2) { return PointerArithmeticOperator(resultType, inputType1, compilation.FindType(inputType2)); } CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, KnownTypeCode inputType1, IType inputType2) { return PointerArithmeticOperator(resultType, compilation.FindType(inputType1), inputType2); } CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, IType inputType2) { return new CSharpOperators.BinaryOperatorMethod(compilation) { ReturnType = resultType, parameters = { new DefaultParameter(inputType1, string.Empty), new DefaultParameter(inputType2, string.Empty) } }; } #endregion #region Enum helper methods IType GetEnumUnderlyingType(IType enumType) { ITypeDefinition def = enumType.GetDefinition(); return def != null ? def.EnumUnderlyingType : SpecialType.UnknownType; } /// /// Handle the case where an enum value is compared with another enum value /// bool operator op(E x, E y); /// ResolveResult HandleEnumComparison(BinaryOperatorType op, IType enumType, bool isNullable, ResolveResult lhs, ResolveResult rhs) { // evaluate as ((U)x op (U)y) IType elementType = GetEnumUnderlyingType(enumType); if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) { var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); if (rr.IsCompileTimeConstant) return rr; } IType resultType = compilation.FindType(KnownTypeCode.Boolean); return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable); } /// /// Handle the case where an enum value is subtracted from another enum value /// U operator –(E x, E y); /// ResolveResult HandleEnumSubtraction(bool isNullable, IType enumType, ResolveResult lhs, ResolveResult rhs) { // evaluate as (U)((U)x – (U)y) IType elementType = GetEnumUnderlyingType(enumType); if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) { var rr = ResolveBinaryOperator(BinaryOperatorType.Subtract, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); rr = WithCheckForOverflow(false).ResolveCast(elementType, rr); if (rr.IsCompileTimeConstant) return rr; } IType resultType = MakeNullable(elementType, isNullable); return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable); } /// /// Handle the following enum operators: /// E operator +(E x, U y); /// E operator +(U x, E y); /// E operator –(E x, U y); /// E operator &(E x, E y); /// E operator |(E x, E y); /// E operator ^(E x, E y); /// ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) { // evaluate as (E)((U)x op (U)y) if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) { IType elementType = GetEnumUnderlyingType(enumType); if (elementType.Kind != TypeKind.Enum) { var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs)); rr = WithCheckForOverflow(false).ResolveCast(enumType, rr); if (rr.IsCompileTimeConstant) // only report result if it's a constant; use the regular OperatorResolveResult codepath otherwise return rr; } } IType resultType = MakeNullable(enumType, isNullable); return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable); } IType MakeNullable(IType type, bool isNullable) { if (isNullable) return NullableType.Create(compilation, type); else return type; } #endregion #region BinaryNumericPromotion bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants) { // C# 4.0 spec: §7.3.6.2 var lhsUType = NullableType.GetUnderlyingType(lhs.Type); var rhsUType = NullableType.GetUnderlyingType(rhs.Type); TypeCode lhsCode = ReflectionHelper.GetTypeCode(lhsUType); TypeCode rhsCode = ReflectionHelper.GetTypeCode(rhsUType); // Treat C# 9 native integers as falling between int and long. // However they don't have a TypeCode, so we hack around that here: if (lhsUType.Kind == TypeKind.NInt) { lhsCode = TypeCode.Int32; } else if (lhsUType.Kind == TypeKind.NUInt) { lhsCode = TypeCode.UInt32; } if (rhsUType.Kind == TypeKind.NInt) { rhsCode = TypeCode.Int32; } else if (rhsUType.Kind == TypeKind.NUInt) { rhsCode = TypeCode.UInt32; } // if one of the inputs is the null literal, promote that to the type of the other operand if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) { lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants); lhsCode = rhsCode; } else if (isNullable && rhs.Type.Kind == TypeKind.Null && lhsCode >= TypeCode.Boolean && lhsCode <= TypeCode.Decimal) { rhs = CastTo(lhsCode, isNullable, rhs, allowNullableConstants); rhsCode = lhsCode; } bool bindingError = false; if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal && rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal) { TypeCode targetType; if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) { targetType = TypeCode.Decimal; bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double || rhsCode == TypeCode.Single || rhsCode == TypeCode.Double); } else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) { targetType = TypeCode.Double; } else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) { targetType = TypeCode.Single; } else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) { targetType = TypeCode.UInt64; bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs); } else if (lhsUType.Kind == TypeKind.NUInt || rhsUType.Kind == TypeKind.NUInt) { bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs); lhs = CastTo(SpecialType.NUInt, isNullable, lhs, allowNullableConstants); rhs = CastTo(SpecialType.NUInt, isNullable, rhs, allowNullableConstants); return !bindingError; } else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) { targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32; } else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) { targetType = TypeCode.Int64; } else if (lhsUType.Kind == TypeKind.NInt || rhsUType.Kind == TypeKind.NInt) { lhs = CastTo(SpecialType.NInt, isNullable, lhs, allowNullableConstants); rhs = CastTo(SpecialType.NInt, isNullable, rhs, allowNullableConstants); return !bindingError; } else { targetType = TypeCode.Int32; } lhs = CastTo(targetType, isNullable, lhs, allowNullableConstants); rhs = CastTo(targetType, isNullable, rhs, allowNullableConstants); } return !bindingError; } bool IsSigned(TypeCode code, ResolveResult rr) { // Determine whether the rr with code==ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rr.Type)) // is a signed primitive type. switch (code) { case TypeCode.SByte: case TypeCode.Int16: return true; case TypeCode.Int32: // for int, consider implicit constant expression conversion if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (int)rr.ConstantValue >= 0) return false; else return true; case TypeCode.Int64: // for long, consider implicit constant expression conversion if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (long)rr.ConstantValue >= 0) return false; else return true; default: return false; } } ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants) { return CastTo(compilation.FindType(targetType), isNullable, expression, allowNullableConstants); } ResolveResult CastTo(IType targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants) { IType nullableType = MakeNullable(targetType, isNullable); if (nullableType.Equals(expression.Type)) return expression; if (allowNullableConstants && expression.IsCompileTimeConstant) { if (expression.ConstantValue == null) return new ConstantResolveResult(nullableType, null); ResolveResult rr = ResolveCast(targetType, expression); if (rr.IsError) return rr; if (rr.IsCompileTimeConstant) return new ConstantResolveResult(nullableType, rr.ConstantValue); } return Convert(expression, nullableType, isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } #endregion #region GetOverloadableOperatorName static string GetOverloadableOperatorName(BinaryOperatorType op) { switch (op) { case BinaryOperatorType.Add: return "op_Addition"; case BinaryOperatorType.Subtract: return "op_Subtraction"; case BinaryOperatorType.Multiply: return "op_Multiply"; case BinaryOperatorType.Divide: return "op_Division"; case BinaryOperatorType.Modulus: return "op_Modulus"; case BinaryOperatorType.BitwiseAnd: return "op_BitwiseAnd"; case BinaryOperatorType.BitwiseOr: return "op_BitwiseOr"; case BinaryOperatorType.ExclusiveOr: return "op_ExclusiveOr"; case BinaryOperatorType.ShiftLeft: return "op_LeftShift"; case BinaryOperatorType.ShiftRight: return "op_RightShift"; case BinaryOperatorType.UnsignedShiftRight: return "op_UnsignedRightShift"; case BinaryOperatorType.Equality: return "op_Equality"; case BinaryOperatorType.InEquality: return "op_Inequality"; case BinaryOperatorType.GreaterThan: return "op_GreaterThan"; case BinaryOperatorType.LessThan: return "op_LessThan"; case BinaryOperatorType.GreaterThanOrEqual: return "op_GreaterThanOrEqual"; case BinaryOperatorType.LessThanOrEqual: return "op_LessThanOrEqual"; default: return null; } } #endregion #region Null coalescing operator ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs) { if (NullableType.IsNullable(lhs.Type)) { IType a0 = NullableType.GetUnderlyingType(lhs.Type); if (TryConvert(ref rhs, a0)) { return BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs); } } if (TryConvert(ref rhs, lhs.Type)) { return BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); } if (TryConvert(ref lhs, rhs.Type)) { return BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); } else { return new ErrorResolveResult(lhs.Type); } } #endregion #endregion #region Get user-defined operator candidates public IEnumerable GetUserDefinedOperatorCandidates(IType type, string operatorName) { if (operatorName == null) return EmptyList.Instance; TypeCode c = ReflectionHelper.GetTypeCode(type); if (TypeCode.Boolean <= c && c <= TypeCode.Decimal) { // The .NET framework contains some of C#'s built-in operators as user-defined operators. // However, we must not use those as user-defined operators (we would skip numeric promotion). return EmptyList.Instance; } // C# 4.0 spec: §7.3.5 Candidate user-defined operators var operators = type.GetMethods(m => m.IsOperator && m.Name == operatorName).ToList(); LiftUserDefinedOperators(operators); return operators; } void LiftUserDefinedOperators(List operators) { int nonLiftedMethodCount = operators.Count; // Construct lifted operators for (int i = 0; i < nonLiftedMethodCount; i++) { var liftedMethod = CSharpOperators.LiftUserDefinedOperator(operators[i]); if (liftedMethod != null) operators.Add(liftedMethod); } } ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType) { if (r.BestCandidateErrors != OverloadResolutionErrors.None) return r.CreateResolveResult(null); IMethod method = (IMethod)r.BestCandidate; return new OperatorResolveResult(method.ReturnType, operatorType, method, isLiftedOperator: method is ILiftedOperator, operands: r.GetArgumentsWithConversions()); } #endregion #region ResolveCast bool TryConvert(ref ResolveResult rr, IType targetType) { Conversion c = conversions.ImplicitConversion(rr, targetType); if (c.IsValid) { rr = Convert(rr, targetType, c); return true; } else { return false; } } /// /// /// /// The input resolve result that should be converted. /// If a conversion exists, it is applied to the resolve result /// The target type that we should convert to /// Whether we are dealing with a lifted operator /// The resolve result that is enum-typed. /// If necessary, a nullable conversion is applied. /// /// Whether the conversion from the constant zero is allowed. /// /// True if the conversion is successful; false otherwise. /// If the conversion is not successful, the ref parameters will not be modified. bool TryConvertEnum(ref ResolveResult rr, IType targetType, ref bool isNullable, ref ResolveResult enumRR, bool allowConversionFromConstantZero = true) { Conversion c; if (!isNullable) { // Try non-nullable c = conversions.ImplicitConversion(rr, targetType); if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) { rr = Convert(rr, targetType, c); return true; } } // make targetType nullable if it isn't already: if (!targetType.IsKnownType(KnownTypeCode.NullableOfT)) targetType = NullableType.Create(compilation, targetType); c = conversions.ImplicitConversion(rr, targetType); if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) { rr = Convert(rr, targetType, c); isNullable = true; // Also convert the enum-typed RR to nullable, if it isn't already if (!enumRR.Type.IsKnownType(KnownTypeCode.NullableOfT)) { var nullableType = NullableType.Create(compilation, enumRR.Type); enumRR = new ConversionResolveResult(nullableType, enumRR, Conversion.ImplicitNullableConversion); } return true; } return false; } ResolveResult Convert(ResolveResult rr, IType targetType) { return Convert(rr, targetType, conversions.ImplicitConversion(rr, targetType)); } ResolveResult Convert(ResolveResult rr, IType targetType, Conversion c) { if (c == Conversion.IdentityConversion) return rr; else if (rr.IsCompileTimeConstant && c != Conversion.None && !c.IsUserDefined) return ResolveCast(targetType, rr); else return new ConversionResolveResult(targetType, rr, c, checkForOverflow); } public ResolveResult ResolveCast(IType targetType, ResolveResult expression) { // C# 4.0 spec: §7.7.6 Cast expressions Conversion c = conversions.ExplicitConversion(expression, targetType); if (expression.IsCompileTimeConstant && !c.IsUserDefined) { IType underlyingType = targetType.GetEnumUnderlyingType(); TypeCode code = ReflectionHelper.GetTypeCode(underlyingType); if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) { if (expression.ConstantValue is string) { return new ErrorResolveResult(targetType); } try { return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue)); } catch (OverflowException) { return new ErrorResolveResult(targetType); } catch (InvalidCastException) { return new ErrorResolveResult(targetType); } } else if (code == TypeCode.String) { if (expression.ConstantValue == null || expression.ConstantValue is string) return new ConstantResolveResult(targetType, expression.ConstantValue); else return new ErrorResolveResult(targetType); } else if ((underlyingType.Kind == TypeKind.NInt || underlyingType.Kind == TypeKind.NUInt) && expression.ConstantValue != null) { if (expression.ConstantValue is string) { return new ErrorResolveResult(targetType); } code = (underlyingType.Kind == TypeKind.NInt ? TypeCode.Int32 : TypeCode.UInt32); try { return new ConstantResolveResult(targetType, Util.CSharpPrimitiveCast.Cast(code, expression.ConstantValue, checkForOverflow: true)); } catch (OverflowException) { // If constant value doesn't fit into 32-bits, the conversion is not a compile-time constant return new ConversionResolveResult(targetType, expression, c, checkForOverflow); } catch (InvalidCastException) { return new ErrorResolveResult(targetType); } } } return new ConversionResolveResult(targetType, expression, c, checkForOverflow); } internal object CSharpPrimitiveCast(TypeCode targetType, object input) { return Util.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow); } #endregion #region ResolveSimpleName public ResolveResult ResolveSimpleName(string identifier, IReadOnlyList typeArguments, bool isInvocationTarget = false) { // C# 4.0 spec: §7.6.2 Simple Names return LookupSimpleNameOrTypeName( identifier, typeArguments, isInvocationTarget ? NameLookupMode.InvocationTarget : NameLookupMode.Expression); } public ResolveResult LookupSimpleNameOrTypeName(string identifier, IReadOnlyList typeArguments, NameLookupMode lookupMode) { // C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names if (identifier == null) throw new ArgumentNullException(nameof(identifier)); if (typeArguments == null) throw new ArgumentNullException(nameof(typeArguments)); int k = typeArguments.Count; if (k == 0) { if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) { // Look in local variables foreach (Dictionary variables in localVariableStack) { if (variables.TryGetValue(identifier, out var v)) { return new LocalResolveResult(v); } } // Look in parameters of current method IParameterizedMember parameterizedMember = this.CurrentMember as IParameterizedMember; if (parameterizedMember != null) { foreach (IParameter p in parameterizedMember.Parameters) { if (p.Name == identifier) { return new LocalResolveResult(p); } } } } // look in type parameters of current method IMethod m = this.CurrentMember as IMethod; if (m != null) { foreach (ITypeParameter tp in m.TypeParameters) { if (tp.Name == identifier) return new TypeResolveResult(tp); } } } bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument)); ResolveResult r = null; if (currentTypeDefinitionCache != null) { Dictionary cache = null; bool foundInCache = false; if (k == 0) { switch (lookupMode) { case NameLookupMode.Expression: cache = currentTypeDefinitionCache.SimpleNameLookupCacheExpression; break; case NameLookupMode.InvocationTarget: cache = currentTypeDefinitionCache.SimpleNameLookupCacheInvocationTarget; break; case NameLookupMode.Type: cache = currentTypeDefinitionCache.SimpleTypeLookupCache; break; } if (cache != null) { lock (cache) foundInCache = cache.TryGetValue(identifier, out r); } } if (foundInCache) { r = (r != null ? r.ShallowClone() : null); } else { r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType); if (cache != null) { // also cache missing members (r==null) lock (cache) cache[identifier] = r; } } if (r != null) return r; } if (context.CurrentUsingScope == null) { // If no using scope was specified, we still need to look in the global namespace: r = LookInUsingScopeNamespace(null, compilation.RootNamespace, identifier, typeArguments, parameterizeResultType); } else { if (k == 0 && lookupMode != NameLookupMode.TypeInUsingDeclaration) { if (context.CurrentUsingScope.ResolveCache.TryGetValue(identifier, out r)) { r = (r != null ? r.ShallowClone() : null); } else { r = LookInCurrentUsingScope(identifier, typeArguments, false, false); context.CurrentUsingScope.ResolveCache.TryAdd(identifier, r); } } else { r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == NameLookupMode.TypeInUsingDeclaration, parameterizeResultType); } } if (r != null) return r; if (typeArguments.Count == 0 && identifier == "dynamic") { return new TypeResolveResult(SpecialType.Dynamic); } else { return new UnknownIdentifierResolveResult(identifier, typeArguments.Count); } } public bool IsVariableReferenceWithSameType(ResolveResult rr, string identifier, out TypeResolveResult trr) { if (!(rr is MemberResolveResult || rr is LocalResolveResult)) { trr = null; return false; } trr = LookupSimpleNameOrTypeName(identifier, EmptyList.Instance, NameLookupMode.Type) as TypeResolveResult; return trr != null && trr.Type.Equals(rr.Type); } ResolveResult LookInCurrentType(string identifier, IReadOnlyList typeArguments, NameLookupMode lookupMode, bool parameterizeResultType) { int k = typeArguments.Count; MemberLookup lookup = CreateMemberLookup(lookupMode); // look in current type definitions for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { if (k == 0) { // Look for type parameter with that name var typeParameters = t.TypeParameters; // Look at all type parameters, including those copied from outer classes, // so that we can fetch the version with the correct owner. for (int i = 0; i < typeParameters.Count; i++) { if (typeParameters[i].Name == identifier) return new TypeResolveResult(typeParameters[i]); } } if (lookupMode == NameLookupMode.BaseTypeReference && t == this.CurrentTypeDefinition) { // don't look in current type when resolving a base type reference continue; } ResolveResult r; if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) { var targetResolveResult = (t == this.CurrentTypeDefinition ? ResolveThisReference() : new TypeResolveResult(t)); r = lookup.Lookup(targetResolveResult, identifier, typeArguments, lookupMode == NameLookupMode.InvocationTarget); } else { r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType); } if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult return r; } return null; } ResolveResult LookInCurrentUsingScope(string identifier, IReadOnlyList typeArguments, bool isInUsingDeclaration, bool parameterizeResultType) { // look in current namespace definitions UsingScope currentUsingScope = this.CurrentUsingScope; for (UsingScope u = currentUsingScope; u != null; u = u.Parent) { var resultInNamespace = LookInUsingScopeNamespace(u, u.Namespace, identifier, typeArguments, parameterizeResultType); if (resultInNamespace != null) return resultInNamespace; // then look for aliases: if (typeArguments.Count == 0) { if (u.ExternAliases.Contains(identifier)) { return ResolveExternAlias(identifier); } if (!(isInUsingDeclaration && u == currentUsingScope)) { foreach (var pair in u.UsingAliases) { if (pair.Key == identifier) { return pair.Value.ShallowClone(); } } } } // finally, look in the imported namespaces: if (!(isInUsingDeclaration && u == currentUsingScope)) { IType firstResult = null; foreach (var importedNamespace in u.Usings) { ITypeDefinition def = importedNamespace.GetTypeDefinition(identifier, typeArguments.Count); if (def != null) { IType resultType; if (parameterizeResultType && typeArguments.Count > 0) resultType = new ParameterizedType(def, typeArguments); else resultType = def; if (firstResult == null || !TopLevelTypeDefinitionIsAccessible(firstResult.GetDefinition())) { if (TopLevelTypeDefinitionIsAccessible(resultType.GetDefinition())) firstResult = resultType; } else if (TopLevelTypeDefinitionIsAccessible(def)) { return new AmbiguousTypeResolveResult(firstResult); } } } if (firstResult != null) return new TypeResolveResult(firstResult); } // if we didn't find anything: repeat lookup with parent namespace } return null; } ResolveResult LookInUsingScopeNamespace(UsingScope usingScope, INamespace n, string identifier, IReadOnlyList typeArguments, bool parameterizeResultType) { if (n == null) return null; // first look for a namespace int k = typeArguments.Count; if (k == 0) { INamespace childNamespace = n.GetChildNamespace(identifier); if (childNamespace != null) { if (usingScope != null && usingScope.HasAlias(identifier)) return new AmbiguousTypeResolveResult(new UnknownType(null, identifier)); return new NamespaceResolveResult(childNamespace); } } // then look for a type ITypeDefinition def = n.GetTypeDefinition(identifier, k); if (def != null && TopLevelTypeDefinitionIsAccessible(def)) { IType result = def; if (parameterizeResultType && k > 0) { result = new ParameterizedType(def, typeArguments); } if (usingScope != null && usingScope.HasAlias(identifier)) return new AmbiguousTypeResolveResult(result); else return new TypeResolveResult(result); } return null; } bool TopLevelTypeDefinitionIsAccessible(ITypeDefinition typeDef) { if (typeDef.Accessibility == Accessibility.Internal) { return typeDef.ParentModule.InternalsVisibleTo(compilation.MainModule); } return true; } /// /// Looks up an alias (identifier in front of :: operator) /// public ResolveResult ResolveAlias(string identifier) { if (identifier == "global") return new NamespaceResolveResult(compilation.RootNamespace); for (UsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) { if (n.ExternAliases.Contains(identifier)) { return ResolveExternAlias(identifier); } foreach (var pair in n.UsingAliases) { if (pair.Key == identifier) { return (pair.Value as NamespaceResolveResult) ?? ErrorResult; } } } return ErrorResult; } ResolveResult ResolveExternAlias(string alias) { INamespace ns = compilation.GetNamespaceForExternAlias(alias); if (ns != null) return new NamespaceResolveResult(ns); else return ErrorResult; } #endregion #region ResolveMemberAccess public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier, IReadOnlyList typeArguments, NameLookupMode lookupMode = NameLookupMode.Expression) { // C# 4.0 spec: §7.6.4 bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument)); NamespaceResolveResult nrr = target as NamespaceResolveResult; if (nrr != null) { return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments, parameterizeResultType); } if (target.Type.Kind == TypeKind.Dynamic) return new DynamicMemberResolveResult(target, identifier); MemberLookup lookup = CreateMemberLookup(lookupMode); ResolveResult result; switch (lookupMode) { case NameLookupMode.Expression: result = lookup.Lookup(target, identifier, typeArguments, isInvocation: false); break; case NameLookupMode.InvocationTarget: result = lookup.Lookup(target, identifier, typeArguments, isInvocation: true); break; case NameLookupMode.Type: case NameLookupMode.TypeInUsingDeclaration: case NameLookupMode.BaseTypeReference: // Don't do the UnknownMemberResolveResult/MethodGroupResolveResult processing, // it's only relevant for expressions. return lookup.LookupType(target.Type, identifier, typeArguments, parameterizeResultType); default: throw new NotSupportedException("Invalid value for NameLookupMode"); } if (result is UnknownMemberResolveResult) { // We intentionally use all extension methods here, not just the eligible ones. // Proper eligibility checking is only possible for the full invocation // (after we know the remaining arguments). // The eligibility check in GetExtensionMethods is only intended for code completion. var extensionMethods = GetExtensionMethods(identifier, typeArguments); if (extensionMethods.Count > 0) { return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { extensionMethods = extensionMethods }; } } else { MethodGroupResolveResult mgrr = result as MethodGroupResolveResult; if (mgrr != null) { Debug.Assert(mgrr.extensionMethods == null); // set the values that are necessary to make MethodGroupResolveResult.GetExtensionMethods() work mgrr.resolver = this; } } return result; } ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IReadOnlyList typeArguments, bool parameterizeResultType) { if (typeArguments.Count == 0) { INamespace childNamespace = nrr.Namespace.GetChildNamespace(identifier); if (childNamespace != null) return new NamespaceResolveResult(childNamespace); } ITypeDefinition def = nrr.Namespace.GetTypeDefinition(identifier, typeArguments.Count); if (def != null) { if (parameterizeResultType && typeArguments.Count > 0) return new TypeResolveResult(new ParameterizedType(def, typeArguments)); else return new TypeResolveResult(def); } return ErrorResult; } /// /// Creates a MemberLookup instance using this resolver's settings. /// public MemberLookup CreateMemberLookup() { ITypeDefinition currentTypeDefinition = this.CurrentTypeDefinition; bool isInEnumMemberInitializer = this.CurrentMember != null && this.CurrentMember.SymbolKind == SymbolKind.Field && currentTypeDefinition != null && currentTypeDefinition.Kind == TypeKind.Enum; return new MemberLookup(currentTypeDefinition, this.Compilation.MainModule, isInEnumMemberInitializer); } /// /// Creates a MemberLookup instance using this resolver's settings. /// public MemberLookup CreateMemberLookup(NameLookupMode lookupMode) { if (lookupMode == NameLookupMode.BaseTypeReference && this.CurrentTypeDefinition != null) { // When looking up a base type reference, treat us as being outside the current type definition // for accessibility purposes. // This avoids a stack overflow when referencing a protected class nested inside the base class // of a parent class. (NameLookupTests.InnerClassInheritingFromProtectedBaseInnerClassShouldNotCauseStackOverflow) return new MemberLookup(this.CurrentTypeDefinition.DeclaringTypeDefinition, this.Compilation.MainModule, false); } else { return CreateMemberLookup(); } } #endregion #region ResolveIdentifierInObjectInitializer public ResolveResult ResolveIdentifierInObjectInitializer(string identifier) { MemberLookup memberLookup = CreateMemberLookup(); return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList.Instance, false); } #endregion #region ResolveForeach public ForEachResolveResult ResolveForeach(ResolveResult expression) { var memberLookup = CreateMemberLookup(); IType collectionType, enumeratorType, elementType; ResolveResult getEnumeratorInvocation; ResolveResult currentRR = null; // C# 4.0 spec: §8.8.4 The foreach statement if (expression.Type.Kind == TypeKind.Array || expression.Type.Kind == TypeKind.Dynamic) { collectionType = compilation.FindType(KnownTypeCode.IEnumerable); enumeratorType = compilation.FindType(KnownTypeCode.IEnumerator); if (expression.Type.Kind == TypeKind.Array) { elementType = ((ArrayType)expression.Type).ElementType; } else { elementType = SpecialType.Dynamic; } getEnumeratorInvocation = ResolveCast(collectionType, expression); getEnumeratorInvocation = ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget); getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, Empty.Array); } else { var getEnumeratorMethodGroup = memberLookup.Lookup(expression, "GetEnumerator", EmptyList.Instance, true) as MethodGroupResolveResult; if (getEnumeratorMethodGroup != null) { var or = getEnumeratorMethodGroup.PerformOverloadResolution( compilation, Empty.Array, allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false); if (or.FoundApplicableCandidate && !or.IsAmbiguous && !or.BestCandidate.IsStatic && or.BestCandidate.Accessibility == Accessibility.Public) { collectionType = expression.Type; getEnumeratorInvocation = or.CreateResolveResult(expression); enumeratorType = getEnumeratorInvocation.Type; currentRR = memberLookup.Lookup(new ResolveResult(enumeratorType), "Current", EmptyList.Instance, false); elementType = currentRR.Type; } else { CheckForEnumerableInterface(expression, out collectionType, out enumeratorType, out elementType, out getEnumeratorInvocation); } } else { CheckForEnumerableInterface(expression, out collectionType, out enumeratorType, out elementType, out getEnumeratorInvocation); } } IMethod moveNextMethod = null; var moveNextMethodGroup = memberLookup.Lookup(new ResolveResult(enumeratorType), "MoveNext", EmptyList.Instance, false) as MethodGroupResolveResult; if (moveNextMethodGroup != null) { var or = moveNextMethodGroup.PerformOverloadResolution( compilation, Empty.Array, allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false); moveNextMethod = or.GetBestCandidateWithSubstitutedTypeArguments() as IMethod; } if (currentRR == null) currentRR = memberLookup.Lookup(new ResolveResult(enumeratorType), "Current", EmptyList.Instance, false); IProperty currentProperty = null; if (currentRR is MemberResolveResult) currentProperty = ((MemberResolveResult)currentRR).Member as IProperty; var voidType = compilation.FindType(KnownTypeCode.Void); return new ForEachResolveResult(getEnumeratorInvocation, collectionType, enumeratorType, elementType, currentProperty, moveNextMethod, voidType); } void CheckForEnumerableInterface(ResolveResult expression, out IType collectionType, out IType enumeratorType, out IType elementType, out ResolveResult getEnumeratorInvocation) { elementType = expression.Type.GetElementTypeFromIEnumerable(compilation, false, out bool? isGeneric); if (isGeneric == true) { ITypeDefinition enumerableOfT = compilation.FindType(KnownTypeCode.IEnumerableOfT).GetDefinition(); if (enumerableOfT != null) collectionType = new ParameterizedType(enumerableOfT, new[] { elementType }); else collectionType = SpecialType.UnknownType; ITypeDefinition enumeratorOfT = compilation.FindType(KnownTypeCode.IEnumeratorOfT).GetDefinition(); if (enumeratorOfT != null) enumeratorType = new ParameterizedType(enumeratorOfT, new[] { elementType }); else enumeratorType = SpecialType.UnknownType; } else if (isGeneric == false) { collectionType = compilation.FindType(KnownTypeCode.IEnumerable); enumeratorType = compilation.FindType(KnownTypeCode.IEnumerator); } else { collectionType = SpecialType.UnknownType; enumeratorType = SpecialType.UnknownType; } getEnumeratorInvocation = ResolveCast(collectionType, expression); getEnumeratorInvocation = ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget); getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, Empty.Array); } #endregion #region GetExtensionMethods /// /// Gets all extension methods that are available in the current context. /// /// Name of the extension method. Pass null to retrieve all extension methods. /// Explicitly provided type arguments. /// An empty list will return all matching extension method definitions; /// a non-empty list will return s for all extension methods /// with the matching number of type parameters. /// /// The results are stored in nested lists because they are grouped by using scope. /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", /// the return value will be /// new List { /// new List { all extensions from MoreExtensions }, /// new List { all extensions from SomeExtensions } /// } /// public List> GetExtensionMethods(string name = null, IReadOnlyList typeArguments = null) { return GetExtensionMethods(null, name, typeArguments); } /// /// Gets the extension methods that are called 'name' /// and are applicable with a first argument type of 'targetType'. /// /// Type of the 'this' argument /// Name of the extension method. Pass null to retrieve all extension methods. /// Explicitly provided type arguments. /// An empty list will return all matching extension method definitions; /// a non-empty list will return s for all extension methods /// with the matching number of type parameters. /// /// Specifies whether to produce a /// when type arguments could be inferred from . This parameter /// is only used for inferred types and has no effect if is non-empty. /// /// /// The results are stored in nested lists because they are grouped by using scope. /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", /// the return value will be /// new List { /// new List { all extensions from MoreExtensions }, /// new List { all extensions from SomeExtensions } /// } /// public List> GetExtensionMethods(IType targetType, string name = null, IReadOnlyList typeArguments = null, bool substituteInferredTypes = false) { var lookup = CreateMemberLookup(); List> extensionMethodGroups = new List>(); foreach (var inputGroup in GetAllExtensionMethods(lookup)) { List outputGroup = new List(); foreach (var method in inputGroup) { if (name != null && method.Name != name) continue; if (!lookup.IsAccessible(method, false)) continue; IType[] inferredTypes; if (typeArguments != null && typeArguments.Count > 0) { if (method.TypeParameters.Count != typeArguments.Count) continue; var sm = method.Specialize(new TypeParameterSubstitution(null, typeArguments)); if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { if (substituteInferredTypes && inferredTypes != null) { outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes))); } else { outputGroup.Add(method); } } } } if (outputGroup.Count > 0) extensionMethodGroups.Add(outputGroup); } return extensionMethodGroups; } /// /// Checks whether the specified extension method is eligible on the target type. /// /// Target type that is passed as first argument to the extension method. /// The extension method. /// Whether to perform type inference for the method. /// Use false if is already parameterized (e.g. when type arguments were given explicitly). /// Otherwise, use true. /// /// If the method is generic and is true, /// and at least some of the type arguments could be inferred, this parameter receives the inferred type arguments. /// Since only the type for the first parameter is considered, not all type arguments may be inferred. /// If an array is returned, any slot with an uninferred type argument will be set to the method's /// corresponding type parameter. /// public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes) { if (targetType == null) throw new ArgumentNullException(nameof(targetType)); if (method == null) throw new ArgumentNullException(nameof(method)); var compilation = method.Compilation; return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes); } static bool IsEligibleExtensionMethod(ICompilation compilation, CSharpConversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes) { outInferredTypes = null; if (targetType == null) return true; if (method.Parameters.Count == 0) return false; IType thisParameterType = method.Parameters[0].Type; if (thisParameterType.Kind == TypeKind.ByReference) { // extension method with `this in` or `this ref` thisParameterType = ((ByReferenceType)thisParameterType).ElementType; } if (useTypeInference && method.TypeParameters.Count > 0) { // We need to infer type arguments from targetType: TypeInference ti = new TypeInference(compilation, conversions); ResolveResult[] arguments = { new ResolveResult(targetType) }; IType[] parameterTypes = { thisParameterType }; var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out _); var substitution = new TypeParameterSubstitution(null, inferredTypes); // Validate that the types that could be inferred (aren't unknown) satisfy the constraints: bool hasInferredTypes = false; for (int i = 0; i < inferredTypes.Length; i++) { if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) { hasInferredTypes = true; if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions)) return false; } else { inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred } } if (hasInferredTypes) outInferredTypes = inferredTypes; thisParameterType = thisParameterType.AcceptVisitor(substitution); } Conversion c = conversions.ImplicitConversion(targetType, thisParameterType); return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion || c.IsImplicitSpanConversion); } /// /// Gets all extension methods available in the current using scope. /// This list includes inaccessible methods. /// IList> GetAllExtensionMethods(MemberLookup lookup) { var currentUsingScope = context.CurrentUsingScope; if (currentUsingScope == null) return EmptyList>.Instance; List> extensionMethodGroups = LazyInit.VolatileRead(ref currentUsingScope.AllExtensionMethods); if (extensionMethodGroups != null) { return extensionMethodGroups; } extensionMethodGroups = new List>(); List m; for (UsingScope scope = currentUsingScope; scope != null; scope = scope.Parent) { INamespace ns = scope.Namespace; if (ns != null) { m = GetExtensionMethods(lookup, ns).ToList(); if (m.Count > 0) extensionMethodGroups.Add(m); } m = scope.Usings .Distinct() .SelectMany(importedNamespace => GetExtensionMethods(lookup, importedNamespace)) .ToList(); if (m.Count > 0) extensionMethodGroups.Add(m); } return LazyInit.GetOrSet(ref currentUsingScope.AllExtensionMethods, extensionMethodGroups); } IEnumerable GetExtensionMethods(MemberLookup lookup, INamespace ns) { // TODO: maybe make this a property on INamespace? return from c in ns.Types where c.IsStatic && c.HasExtensions && c.TypeParameters.Count == 0 && lookup.IsAccessible(c, false) from m in c.Methods where m.IsExtensionMethod select m; } #endregion #region ResolveInvocation IList AddArgumentNamesIfNecessary(ResolveResult[] arguments, string[] argumentNames) { if (argumentNames == null) { return arguments; } else { var result = new ResolveResult[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { result[i] = (argumentNames[i] != null ? new NamedArgumentResolveResult(argumentNames[i], arguments[i]) : arguments[i]); } return result; } } private ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames, bool allowOptionalParameters) { // C# 4.0 spec: §7.6.5 if (target.Type.Kind == TypeKind.Dynamic) { return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); } bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic); MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { if (isDynamic) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray()); var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); if (applicableMethods.Count > 1) { ResolveResult actualTarget; if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult)) actualTarget = new TypeResolveResult(mgrr.TargetType); else actualTarget = mgrr.TargetResult; var l = new List(); foreach (var m in applicableMethods) { if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType) l.Add(new MethodListWithDeclaringType(m.DeclaringType)); l[l.Count - 1].Add(m.Method); } return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); } } OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); else return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); } else { // No candidate found at all (not even an inapplicable one). // This can happen with empty method groups (as sometimes used with extension methods) return new UnknownMethodResolveResult( mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames)); } } UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult; if (umrr != null) { return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames)); } UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult; if (uirr != null && CurrentTypeDefinition != null) { return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList.Instance, CreateParameters(arguments, argumentNames)); } IMethod invokeMethod = target.Type.GetDelegateInvokeMethod(); if (invokeMethod != null) { OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddCandidate(invokeMethod); return new CSharpInvocationResolveResult( target, invokeMethod, //invokeMethod.ReturnType.Resolve(context), or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors, isExpandedForm: or.BestCandidateIsExpandedForm, isDelegateInvocation: true, argumentToParameterMap: or.GetArgumentToParameterMap(), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); } return ErrorResult; } /// /// Resolves an invocation. /// /// The target of the invocation. Usually a MethodGroupResolveResult. /// /// Arguments passed to the method. /// The resolver may mutate this array to wrap elements in s! /// /// /// The argument names. Pass the null string for positional arguments. /// /// InvocationResolveResult or UnknownMethodResolveResult public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null) { return ResolveInvocation(target, arguments, argumentNames, allowOptionalParameters: true); } List CreateParameters(ResolveResult[] arguments, string[] argumentNames) { List list = new List(); if (argumentNames == null) { argumentNames = new string[arguments.Length]; } else { if (argumentNames.Length != arguments.Length) throw new ArgumentException(); argumentNames = (string[])argumentNames.Clone(); } for (int i = 0; i < arguments.Length; i++) { // invent argument names where necessary: if (argumentNames[i] == null) { string newArgumentName = GuessParameterName(arguments[i]); if (argumentNames.Contains(newArgumentName)) { // disambiguate argument name (e.g. add a number) int num = 1; string newName; do { newName = newArgumentName + num.ToString(); num++; } while (argumentNames.Contains(newName)); newArgumentName = newName; } argumentNames[i] = newArgumentName; } // create the parameter: ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], referenceKind: brrr.ReferenceKind)); } else { // argument might be a lambda or delegate type, so we have to try to guess the delegate type IType type = arguments[i].Type; if (type.Kind == TypeKind.Null || type.Kind == TypeKind.None) { list.Add(new DefaultParameter(compilation.FindType(KnownTypeCode.Object), argumentNames[i])); } else { list.Add(new DefaultParameter(type, argumentNames[i])); } } } return list; } static string GuessParameterName(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; if (mrr != null) return mrr.Member.Name; UnknownMemberResolveResult umrr = rr as UnknownMemberResolveResult; if (umrr != null) return umrr.MemberName; MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; if (mgrr != null) return mgrr.MethodName; LocalResolveResult vrr = rr as LocalResolveResult; if (vrr != null) return MakeParameterName(vrr.Variable.Name); if (rr.Type.Kind != TypeKind.Unknown && !string.IsNullOrEmpty(rr.Type.Name)) { return MakeParameterName(rr.Type.Name); } else { return "parameter"; } } static string MakeParameterName(string variableName) { if (string.IsNullOrEmpty(variableName)) return "parameter"; if (variableName.Length > 1 && variableName[0] == '_') variableName = variableName.Substring(1); return char.ToLower(variableName[0]) + variableName.Substring(1); } OverloadResolution CreateOverloadResolution(ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null) { var or = new OverloadResolution(compilation, arguments, argumentNames, typeArguments, conversions); or.CheckForOverflow = checkForOverflow; return or; } #endregion #region ResolveIndexer /// /// Resolves an indexer access. /// /// Target expression. /// /// Arguments passed to the indexer. /// The resolver may mutate this array to wrap elements in s! /// /// /// The argument names. Pass the null string for positional arguments. /// /// ArrayAccessResolveResult, InvocationResolveResult, or ErrorResolveResult public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null) { switch (target.Type.Kind) { case TypeKind.Dynamic: return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames)); case TypeKind.Array: case TypeKind.Pointer: // §7.6.6.1 Array access / §18.5.3 Pointer element access AdjustArrayAccessArguments(arguments); return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments); } // §7.6.6.2 Indexer access MemberLookup lookup = CreateMemberLookup(); var indexers = lookup.LookupIndexers(target); if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer. var or2 = CreateOverloadResolution(arguments, argumentNames, null); var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList(); if (applicableIndexers.Count > 1) { return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames)); } } OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddMethodLists(indexers); if (or.BestCandidate != null) { return or.CreateResolveResult(target); } else { return ErrorResult; } } /// /// Converts all arguments to int,uint,long or ulong. /// void AdjustArrayAccessArguments(ResolveResult[] arguments) { for (int i = 0; i < arguments.Length; i++) { if (!(TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int32)) || TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt32)) || TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int64)) || TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt64)))) { // conversion failed arguments[i] = Convert(arguments[i], compilation.FindType(KnownTypeCode.Int32), Conversion.None); } } } #endregion #region ResolveObjectCreation /// /// Resolves an object creation. /// /// Type of the object to create. /// /// Arguments passed to the constructor. /// The resolver may mutate this array to wrap elements in s! /// /// /// The argument names. Pass the null string for positional arguments. /// /// /// Whether to allow calling protected constructors. /// This should be false except when resolving constructor initializers. /// /// /// Statements for Objects/Collections initializer. /// /// /// InvocationResolveResult or ErrorResolveResult public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList initializerStatements = null) { if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { ResolveResult input = arguments[0]; IMethod invoke = input.Type.GetDelegateInvokeMethod(); if (invoke != null) { input = new MethodGroupResolveResult( input, invoke.Name, methods: new[] { new MethodListWithDeclaringType(input.Type) { invoke } }, typeArguments: EmptyList.Instance ); } return Convert(input, type); } OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List() : null); foreach (IMethod ctor in type.GetConstructors()) { if (lookup.IsAccessible(ctor, allowProtectedAccess)) { var orErrors = or.AddCandidate(ctor); if (allApplicable != null && OverloadResolution.IsApplicable(orErrors)) allApplicable.Add(ctor); } else or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible); } if (allApplicable != null && allApplicable.Count > 1) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor. return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, AddArgumentNamesIfNecessary(arguments, argumentNames), initializerStatements); } if (or.BestCandidate != null) { return or.CreateResolveResult(null, initializerStatements); } else { return new ErrorResolveResult(type); } } #endregion #region ResolveSizeOf /// /// Resolves 'sizeof(type)'. /// public ResolveResult ResolveSizeOf(IType type) { IType int32 = compilation.FindType(KnownTypeCode.Int32); int? size = null; var typeForConstant = (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type; switch (ReflectionHelper.GetTypeCode(typeForConstant)) { case TypeCode.Boolean: case TypeCode.SByte: case TypeCode.Byte: size = 1; break; case TypeCode.Char: case TypeCode.Int16: case TypeCode.UInt16: size = 2; break; case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Single: size = 4; break; case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Double: size = 8; break; } return new SizeOfResolveResult(int32, type, size); } #endregion #region Resolve This/Base Reference /// /// Resolves 'this'. /// public ResolveResult ResolveThisReference() { ITypeDefinition t = CurrentTypeDefinition; if (t != null) { if (t.TypeParameterCount != 0) { // Self-parameterize the type return new ThisResolveResult(new ParameterizedType(t, t.TypeParameters)); } else { return new ThisResolveResult(t); } } return ErrorResult; } /// /// Resolves 'base'. /// public ResolveResult ResolveBaseReference() { ITypeDefinition t = CurrentTypeDefinition; if (t != null) { foreach (IType baseType in t.DirectBaseTypes) { if (baseType.Kind != TypeKind.Unknown && baseType.Kind != TypeKind.Interface) { return new ThisResolveResult(baseType, causesNonVirtualInvocation: true); } } } return ErrorResult; } #endregion #region ResolveConditional /// /// Converts the input to bool using the rules for boolean expressions. /// That is, operator true is used if a regular conversion to bool is not possible. /// public ResolveResult ResolveCondition(ResolveResult input) { if (input == null) throw new ArgumentNullException(nameof(input)); IType boolean = compilation.FindType(KnownTypeCode.Boolean); Conversion c = conversions.ImplicitConversion(input, boolean); if (!c.IsValid) { var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault(); if (opTrue != null) { c = Conversion.UserDefinedConversion(opTrue, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); } } return Convert(input, boolean, c); } /// /// Converts the negated input to bool using the rules for boolean expressions. /// Computes !(bool)input if the implicit cast to bool is valid; otherwise /// computes input.operator false(). /// public ResolveResult ResolveConditionFalse(ResolveResult input) { if (input == null) throw new ArgumentNullException(nameof(input)); IType boolean = compilation.FindType(KnownTypeCode.Boolean); Conversion c = conversions.ImplicitConversion(input, boolean); if (!c.IsValid) { var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault(); if (opFalse != null) { c = Conversion.UserDefinedConversion(opFalse, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None); return Convert(input, boolean, c); } } return ResolveUnaryOperator(UnaryOperatorType.Not, Convert(input, boolean, c)); } public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression) { // C# 4.0 spec §7.14: Conditional operator bool isValid; IType resultType; if (trueExpression.Type.Kind == TypeKind.Dynamic || falseExpression.Type.Kind == TypeKind.Dynamic) { resultType = SpecialType.Dynamic; isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType); } else if (HasType(trueExpression) && HasType(falseExpression)) { Conversion t2f = conversions.ImplicitConversion(trueExpression, falseExpression.Type); Conversion f2t = conversions.ImplicitConversion(falseExpression, trueExpression.Type); // The operator is valid: // a) if there's a conversion in one direction but not the other // b) if there are conversions in both directions, and the types are equivalent if (IsBetterConditionalConversion(t2f, f2t)) { resultType = falseExpression.Type; isValid = true; trueExpression = Convert(trueExpression, resultType, t2f); } else if (IsBetterConditionalConversion(f2t, t2f)) { resultType = trueExpression.Type; isValid = true; falseExpression = Convert(falseExpression, resultType, f2t); } else { resultType = trueExpression.Type; isValid = trueExpression.Type.Equals(falseExpression.Type); } } else if (HasType(trueExpression)) { resultType = trueExpression.Type; isValid = TryConvert(ref falseExpression, resultType); } else if (HasType(falseExpression)) { resultType = falseExpression.Type; isValid = TryConvert(ref trueExpression, resultType); } else { return ErrorResult; } condition = ResolveCondition(condition); if (isValid) { if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) { bool? val = condition.ConstantValue as bool?; if (val == true) return trueExpression; else if (val == false) return falseExpression; } return new OperatorResolveResult(resultType, System.Linq.Expressions.ExpressionType.Conditional, condition, trueExpression, falseExpression); } else { return new ErrorResolveResult(resultType); } } bool IsBetterConditionalConversion(Conversion c1, Conversion c2) { // Valid is better than ImplicitConstantExpressionConversion is better than invalid if (!c1.IsValid) return false; if (c1 != Conversion.ImplicitConstantExpressionConversion && c2 == Conversion.ImplicitConstantExpressionConversion) return true; return !c2.IsValid; } bool HasType(ResolveResult r) { return r.Type.Kind != TypeKind.None && r.Type.Kind != TypeKind.Null; } #endregion #region ResolvePrimitive public ResolveResult ResolvePrimitive(object value) { if (value == null) { return new ResolveResult(SpecialType.NullType); } else { TypeCode typeCode = Type.GetTypeCode(value.GetType()); IType type = compilation.FindType(typeCode); return new ConstantResolveResult(type, value); } } #endregion #region ResolveDefaultValue public ResolveResult ResolveDefaultValue(IType type) { return new ConstantResolveResult(type, GetDefaultValue(type)); } public static object GetDefaultValue(IType type) { ITypeDefinition typeDef = type.GetDefinition(); if (typeDef == null) return null; if (typeDef.Kind == TypeKind.Enum) { typeDef = typeDef.EnumUnderlyingType.GetDefinition(); if (typeDef == null) return null; } switch (typeDef.KnownTypeCode) { case KnownTypeCode.Boolean: return false; case KnownTypeCode.Char: return '\0'; case KnownTypeCode.SByte: return (sbyte)0; case KnownTypeCode.Byte: return (byte)0; case KnownTypeCode.Int16: return (short)0; case KnownTypeCode.UInt16: return (ushort)0; case KnownTypeCode.Int32: return 0; case KnownTypeCode.UInt32: return 0U; case KnownTypeCode.Int64: return 0L; case KnownTypeCode.UInt64: return 0UL; case KnownTypeCode.Single: return 0f; case KnownTypeCode.Double: return 0.0; case KnownTypeCode.Decimal: return 0m; default: return null; } } #endregion #region ResolveArrayCreation /// /// Resolves an array creation. /// /// /// The array element type. /// Pass null to resolve an implicitly-typed array creation. /// /// /// The size arguments. /// The length of this array will be used as the number of dimensions of the array type. /// Negative values will be treated as errors. /// /// /// The initializer elements. May be null if no array initializer was specified. /// The resolver may mutate this array to wrap elements in s! /// public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, int[] sizeArguments, ResolveResult[] initializerElements = null) { ResolveResult[] sizeArgResults = new ResolveResult[sizeArguments.Length]; for (int i = 0; i < sizeArguments.Length; i++) { if (sizeArguments[i] < 0) sizeArgResults[i] = ErrorResolveResult.UnknownError; else sizeArgResults[i] = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), sizeArguments[i]); } return ResolveArrayCreation(elementType, sizeArgResults, initializerElements); } /// /// Resolves an array creation. /// /// /// The array element type. /// Pass null to resolve an implicitly-typed array creation. /// /// /// The size arguments. /// The length of this array will be used as the number of dimensions of the array type. /// The resolver may mutate this array to wrap elements in s! /// /// /// The initializer elements. May be null if no array initializer was specified. /// The resolver may mutate this array to wrap elements in s! /// public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, ResolveResult[] sizeArguments, ResolveResult[] initializerElements = null) { int dimensions = sizeArguments.Length; if (dimensions == 0) throw new ArgumentException("sizeArguments.Length must not be 0"); if (elementType == null) { TypeInference typeInference = new TypeInference(compilation, conversions); elementType = typeInference.GetBestCommonType(initializerElements, out _); } IType arrayType = new ArrayType(compilation, elementType, dimensions); AdjustArrayAccessArguments(sizeArguments); if (initializerElements != null) { for (int i = 0; i < initializerElements.Length; i++) { initializerElements[i] = Convert(initializerElements[i], elementType); } } return new ArrayCreateResolveResult(arrayType, sizeArguments, initializerElements); } #endregion public ResolveResult ResolveTypeOf(IType referencedType) { return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType); } #region ResolveAssignment public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs) { var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow); var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op); if (bop == null) { return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type)); } ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs); OperatorResolveResult opResult = bopResult as OperatorResolveResult; if (opResult == null || opResult.Operands.Count != 2) return bopResult; return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator, new[] { lhs, opResult.Operands[1] }); } #endregion #region CanTransformToExtensionMethodCall public bool CanTransformToExtensionMethodCall(IMethod method, IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) { if (target is LambdaResolveResult) return false; var rr = ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; if (rr == null) return false; var or = rr.PerformOverloadResolution(CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true); if (or == null || or.IsAmbiguous) return false; return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) && CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _); } public bool CanTransformToExtensionMethodCall(IMethod method, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true) { if (method.Parameters.Count == 0) return false; var targetType = method.Parameters.Select(p => new ResolveResult(p.Type)).First(); var paramTypes = method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray(); var paramNames = ignoreArgumentNames ? null : method.Parameters.SelectReadOnlyArray(p => p.Name); var typeArgs = ignoreTypeArguments ? Empty.Array : method.TypeArguments.ToArray(); return CanTransformToExtensionMethodCall(method, typeArgs, targetType, paramTypes, argumentNames: paramNames); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/DynamicInvocationResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Globalization; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { public enum DynamicInvocationType { /// /// The invocation is a normal invocation ( 'a(b)' ). /// Invocation, /// /// The invocation is an indexing ( 'a[b]' ). /// Indexing, /// /// The invocation is an object creation ( 'new a(b)' ). /// ObjectCreation, } /// /// Represents the result of an invocation of a member of a dynamic object. /// public class DynamicInvocationResolveResult : ResolveResult { /// /// Target of the invocation. Can be a dynamic expression or a . /// public readonly ResolveResult Target; /// /// Type of the invocation. /// public readonly DynamicInvocationType InvocationType; /// /// Arguments for the call. Named arguments will be instances of . /// public readonly IList Arguments; /// /// Gets the list of initializer statements that are appplied to the result of this invocation. /// This is used to represent object and collection initializers. /// With the initializer statements, the is used /// to refer to the result of this invocation. /// Initializer statements can only exist if the is . /// public readonly IList InitializerStatements; public DynamicInvocationResolveResult(ResolveResult target, DynamicInvocationType invocationType, IList arguments, IList initializerStatements = null) : base(SpecialType.Dynamic) { this.Target = target; this.InvocationType = invocationType; this.Arguments = arguments ?? EmptyList.Instance; this.InitializerStatements = initializerStatements ?? EmptyList.Instance; } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[Dynamic invocation ]"); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/DynamicMemberResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Globalization; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents the result of an access to a member of a dynamic object. /// public class DynamicMemberResolveResult : ResolveResult { /// /// Target of the member access (a dynamic object). /// public readonly ResolveResult Target; /// /// Name of the accessed member. /// public readonly string Member; public DynamicMemberResolveResult(ResolveResult target, string member) : base(SpecialType.Dynamic) { this.Target = target; this.Member = member; } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[Dynamic member '{0}']", Member); } public override IEnumerable GetChildResults() { return new[] { Target }; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Represents an anonymous method or lambda expression. /// Note: the lambda has no type. /// To retrieve the delegate type, look at the anonymous function conversion. /// public abstract class LambdaResolveResult : ResolveResult { protected LambdaResolveResult() : base(SpecialType.NoType) { } /// /// Gets whether there is a parameter list. /// This property always returns true for C# 3.0-lambdas, but may return false /// for C# 2.0 anonymous methods. /// public abstract bool HasParameterList { get; } /// /// Gets whether this lambda is using the C# 2.0 anonymous method syntax. /// public abstract bool IsAnonymousMethod { get; } /// /// Gets whether the lambda parameters are implicitly typed. /// /// This property returns false for anonymous methods without parameter list. public abstract bool IsImplicitlyTyped { get; } /// /// Gets whether the lambda is async. /// public abstract bool IsAsync { get; } /// /// Gets the return type inferred when the parameter types are inferred to be /// /// /// This method determines the return type inferred from the lambda body, which is used as part of C# type inference. /// Use the property to retrieve the actual return type as determined by the target delegate type. /// public abstract IType GetInferredReturnType(IType[] parameterTypes); /// /// Gets the list of parameters. /// public abstract IReadOnlyList Parameters { get; } /// /// Gets the return type of the lambda. /// /// If the lambda is async, the return type includes Task<T> /// public abstract IType ReturnType { get; } /// /// Gets whether the lambda body is valid for the given parameter types and return type. /// /// /// Produces a conversion with =true if the lambda is valid; /// otherwise returns . /// public abstract Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions); /// /// Gets the resolve result for the lambda body. /// Returns a resolve result for 'void' for statement lambdas. /// public abstract ResolveResult Body { get; } public override IEnumerable GetChildResults() { return new[] { this.Body }; } } sealed class DecompiledLambdaResolveResult : LambdaResolveResult { readonly IL.ILFunction function; public readonly IType DelegateType; /// /// The inferred return type. /// Can differ from ReturnType if a return statement /// performs an implicit conversion. /// public IType InferredReturnType; public DecompiledLambdaResolveResult(IL.ILFunction function, IType delegateType, IType inferredReturnType, bool hasParameterList, bool isAnonymousMethod, bool isImplicitlyTyped) { this.function = function ?? throw new ArgumentNullException(nameof(function)); this.DelegateType = delegateType ?? throw new ArgumentNullException(nameof(delegateType)); this.InferredReturnType = inferredReturnType ?? throw new ArgumentNullException(nameof(inferredReturnType)); this.HasParameterList = hasParameterList; this.IsAnonymousMethod = isAnonymousMethod; this.IsImplicitlyTyped = isImplicitlyTyped; this.Body = new ResolveResult(SpecialType.UnknownType); } public override bool HasParameterList { get; } public override bool IsAnonymousMethod { get; } public override bool IsImplicitlyTyped { get; } public override bool IsAsync => function.IsAsync; public override IReadOnlyList Parameters => function.Parameters; public override IType ReturnType => function.ReturnType; public override ResolveResult Body { get; } public override IType GetInferredReturnType(IType[] parameterTypes) { // We don't know how to compute which type would be inferred if // given other parameter types. // Let's hope this is good enough: return InferredReturnType; } public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions) { // Anonymous method expressions without parameter lists are applicable to any parameter list. if (HasParameterList) { if (this.Parameters.Count != parameterTypes.Length) return Conversion.None; for (int i = 0; i < parameterTypes.Length; ++i) { if (!conversions.IdentityConversion(parameterTypes[i], this.Parameters[i].Type)) { if (IsImplicitlyTyped) { // it's possible that different parameter types also lead to a valid conversion return LambdaConversion.Instance; } else { return Conversion.None; } } } } if (conversions.IdentityConversion(this.ReturnType, returnType) || conversions.ImplicitConversion(this.InferredReturnType, returnType).IsValid) { return LambdaConversion.Instance; } else { return Conversion.None; } } } class LambdaConversion : Conversion { public static readonly LambdaConversion Instance = new LambdaConversion(); public override bool IsAnonymousFunctionConversion => true; public override bool IsImplicit => true; } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/Log.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Diagnostics; using System.Linq; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Resolver logging helper. /// Wraps System.Diagnostics.Debug so that resolver-specific logging can be enabled/disabled on demand. /// (it's a huge amount of debug spew and slows down the resolver quite a bit) /// static class Log { const bool logEnabled = false; #if __MonoCS__ [Conditional("MCS_DEBUG")] #else [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] #endif internal static void WriteLine(string text) { Debug.WriteLine(text); } #if __MonoCS__ [Conditional("MCS_DEBUG")] #else [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] #endif internal static void WriteLine(string format, params object[] args) { Debug.WriteLine(format, args); } #if __MonoCS__ [Conditional("MCS_DEBUG")] #else [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] #endif internal static void WriteCollection(string text, IEnumerable lines) { #if DEBUG T[] arr = lines.ToArray(); if (arr.Length == 0) { Debug.WriteLine(text + ""); } else { Debug.WriteLine(text + (arr[0] != null ? arr[0].ToString() : "")); for (int i = 1; i < arr.Length; i++) { Debug.WriteLine(new string(' ', text.Length) + (arr[i] != null ? arr[i].ToString() : "")); } } #endif } #if __MonoCS__ [Conditional("MCS_DEBUG")] #else [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] #endif public static void Indent() { Debug.Indent(); } #if __MonoCS__ [Conditional("MCS_DEBUG")] #else [Conditional(logEnabled ? "DEBUG" : "LOG_DISABLED")] #endif public static void Unindent() { Debug.Unindent(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// Implementation of member lookup (C# 4.0 spec, §7.4). /// public class MemberLookup { #region Static helper methods /// /// Gets whether the member is considered to be invocable. /// public static bool IsInvocable(IMember member) { if (member == null) throw new ArgumentNullException(nameof(member)); // C# 4.0 spec, §7.4 member lookup if (member is IEvent || member is IMethod) return true; IType returnType = member.ReturnType; return returnType.Kind == TypeKind.Dynamic || returnType.Kind == TypeKind.Delegate || returnType.Kind == TypeKind.FunctionPointer; } #endregion readonly ITypeDefinition currentTypeDefinition; readonly IModule currentModule; readonly bool isInEnumMemberInitializer; public MemberLookup(ITypeDefinition currentTypeDefinition, IModule currentModule, bool isInEnumMemberInitializer = false) { this.currentTypeDefinition = currentTypeDefinition; this.currentModule = currentModule; this.isInEnumMemberInitializer = isInEnumMemberInitializer; } #region IsAccessible /// /// Gets whether access to protected instance members of the target expression is possible. /// public bool IsProtectedAccessAllowed(ResolveResult targetResolveResult) { return targetResolveResult is ThisResolveResult || IsProtectedAccessAllowed(targetResolveResult.Type); } /// /// Gets whether access to protected instance members of the target type is possible. /// /// /// This method does not consider the special case of the 'base' reference. If possible, use the /// IsProtectedAccessAllowed(ResolveResult) overload instead. /// public bool IsProtectedAccessAllowed(IType targetType) { if (targetType.Kind == TypeKind.TypeParameter) targetType = ((ITypeParameter)targetType).EffectiveBaseClass; ITypeDefinition typeDef = targetType.GetDefinition(); if (typeDef == null) return false; for (ITypeDefinition c = currentTypeDefinition; c != null; c = c.DeclaringTypeDefinition) { if (typeDef.IsDerivedFrom(c)) return true; } return false; } /// /// Gets whether is accessible in the current class. /// /// The entity to test /// /// Whether protected access to instance members is allowed. /// True if the type of the reference is derived from the current class. /// Protected static members may be accessible even if false is passed for this parameter. /// public bool IsAccessible(IEntity entity, bool allowProtectedAccess) { if (entity == null) throw new ArgumentNullException(nameof(entity)); // C# 4.0 spec, §3.5.2 Accessiblity domains switch (entity.Accessibility) { case Accessibility.None: return false; case Accessibility.Private: // check for members of outer classes (private members of outer classes can be accessed) for (var t = currentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { if (t.Equals(entity.DeclaringTypeDefinition)) return true; } return false; case Accessibility.Public: return true; case Accessibility.Protected: return IsProtectedAccessible(allowProtectedAccess, entity); case Accessibility.Internal: return IsInternalAccessible(entity.ParentModule); case Accessibility.ProtectedOrInternal: return IsInternalAccessible(entity.ParentModule) || IsProtectedAccessible(allowProtectedAccess, entity); case Accessibility.ProtectedAndInternal: return IsInternalAccessible(entity.ParentModule) && IsProtectedAccessible(allowProtectedAccess, entity); default: throw new Exception("Invalid value for Accessibility"); } } bool IsInternalAccessible(IModule module) { return module != null && currentModule != null && module.InternalsVisibleTo(currentModule); } bool IsProtectedAccessible(bool allowProtectedAccess, IEntity entity) { // For static members and type definitions, we do not require the qualifying reference // to be derived from the current class (allowProtectedAccess). if (entity.IsStatic || entity.SymbolKind == SymbolKind.TypeDefinition) allowProtectedAccess = true; for (var t = currentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { if (t.Equals(entity.DeclaringTypeDefinition)) return true; // PERF: this might hurt performance as this method is called several times (once for each member) // make sure resolving base types is cheap (caches?) or cache within the MemberLookup instance if (allowProtectedAccess && t.IsDerivedFrom(entity.DeclaringTypeDefinition)) return true; } return false; } #endregion #region GetAccessibleMembers /// /// Retrieves all members that are accessible and not hidden (by being overridden or shadowed). /// Returns both members and nested type definitions. Does not include extension methods. /// public IEnumerable GetAccessibleMembers(ResolveResult targetResolveResult) { if (targetResolveResult == null) throw new ArgumentNullException(nameof(targetResolveResult)); bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); // maps the member name to the list of lookup groups var lookupGroupDict = new Dictionary>(); // This loop will handle base types before derived types. // The loop performs three jobs: // 1) It marks entries in lookup groups from base classes as removed when those members // are hidden by a derived class. // 2) It adds a new lookup group with the members from a declaring type. // 3) It replaces virtual members with the overridden version, placing the override in the // lookup group belonging to the base class. foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { List entities = new List(); entities.AddRange(type.GetMembers(options: GetMemberOptions.IgnoreInheritedMembers)); if (!targetIsTypeParameter) { var nestedTypes = type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); // GetDefinition() might return null if some IType has a strange implementation of GetNestedTypes. entities.AddRange(nestedTypes.Select(t => t.GetDefinition()).Where(td => td != null)); } foreach (var entityGroup in entities.GroupBy(e => e.Name)) { List lookupGroups = new List(); if (!lookupGroupDict.TryGetValue(entityGroup.Key, out lookupGroups)) lookupGroupDict.Add(entityGroup.Key, lookupGroups = new List()); List newNestedTypes = null; List newMethods = null; IMember newNonMethod = null; IEnumerable typeBaseTypes = null; if (!targetIsTypeParameter) { AddNestedTypes(type, entityGroup.OfType(), 0, lookupGroups, ref typeBaseTypes, ref newNestedTypes); } AddMembers(type, entityGroup.OfType(), allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); if (newNestedTypes != null || newMethods != null || newNonMethod != null) lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); } } foreach (List lookupGroups in lookupGroupDict.Values) { // Remove interface members hidden by class members. if (targetIsTypeParameter) { // This can happen only with type parameters. RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); } // Now report the results: foreach (LookupGroup lookupGroup in lookupGroups) { if (!lookupGroup.MethodsAreHidden) { foreach (IMethod method in lookupGroup.Methods) { yield return method; } } if (!lookupGroup.NonMethodIsHidden) { yield return lookupGroup.NonMethod; } if (lookupGroup.NestedTypes != null) { foreach (IType type in lookupGroup.NestedTypes) { ITypeDefinition typeDef = type.GetDefinition(); if (typeDef != null) yield return typeDef; } } } } } #endregion #region class LookupGroup sealed class LookupGroup { public readonly IType DeclaringType; // When a nested type is hidden, it is simply removed from the list. public List NestedTypes; // When members are hidden, they are merely marked as hidden. // We still need to store the hidden methods so that the 'override' processing can // find them, so that it won't introduce the override as a new method. public readonly List Methods; public bool MethodsAreHidden; public IMember NonMethod; public bool NonMethodIsHidden; public LookupGroup(IType declaringType, List nestedTypes, List methods, IMember nonMethod) { this.DeclaringType = declaringType; this.NestedTypes = nestedTypes; this.Methods = methods; this.NonMethod = nonMethod; this.MethodsAreHidden = (methods == null || methods.Count == 0); this.NonMethodIsHidden = (nonMethod == null); } public bool AllHidden { get { if (NestedTypes != null && NestedTypes.Count > 0) return false; return NonMethodIsHidden && MethodsAreHidden; } } } #endregion #region LookupType public ResolveResult LookupType(IType declaringType, string name, IReadOnlyList typeArguments, bool parameterizeResultType = true) { if (declaringType == null) throw new ArgumentNullException(nameof(declaringType)); if (name == null) throw new ArgumentNullException(nameof(name)); if (typeArguments == null) throw new ArgumentNullException(nameof(typeArguments)); int typeArgumentCount = typeArguments.Count; Predicate filter = delegate (ITypeDefinition d) { return InnerTypeParameterCount(d) == typeArgumentCount && d.Name == name && IsAccessible(d, true); }; List lookupGroups = new List(); if (declaringType.Kind != TypeKind.TypeParameter) { foreach (IType type in declaringType.GetNonInterfaceBaseTypes()) { List newNestedTypes = null; IEnumerable typeBaseTypes = null; IEnumerable nestedTypes; if (parameterizeResultType) { nestedTypes = type.GetNestedTypes(typeArguments, filter, GetMemberOptions.IgnoreInheritedMembers); } else { nestedTypes = type.GetNestedTypes(filter, GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); } AddNestedTypes(type, nestedTypes, typeArgumentCount, lookupGroups, ref typeBaseTypes, ref newNestedTypes); if (newNestedTypes != null) lookupGroups.Add(new LookupGroup(type, newNestedTypes, null, null)); } } lookupGroups.RemoveAll(g => g.AllHidden); Debug.Assert(lookupGroups.All(g => g.NestedTypes != null && g.NestedTypes.Count > 0)); if (lookupGroups.Count == 0) { return new UnknownMemberResolveResult(declaringType, name, typeArguments); } LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1]; if (resultGroup.NestedTypes.Count > 1 || lookupGroups.Count > 1) return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]); else return new TypeResolveResult(resultGroup.NestedTypes[0]); } static int InnerTypeParameterCount(IType type) { // inner types contain the type parameters of outer types. therefore this count has to been adjusted. return type.TypeParameterCount - (type.DeclaringType != null ? type.DeclaringType.TypeParameterCount : 0); } #endregion #region Lookup /// /// Performs a member lookup. /// public ResolveResult Lookup(ResolveResult targetResolveResult, string name, IReadOnlyList typeArguments, bool isInvocation) { if (targetResolveResult == null) throw new ArgumentNullException(nameof(targetResolveResult)); if (name == null) throw new ArgumentNullException(nameof(name)); if (typeArguments == null) throw new ArgumentNullException(nameof(typeArguments)); bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); Predicate nestedTypeFilter = delegate (ITypeDefinition entity) { return entity.Name == name && IsAccessible(entity, allowProtectedAccess); }; Predicate memberFilter = delegate (IMember entity) { // NOTE: Atm destructors can be looked up with 'Finalize' return entity.SymbolKind != SymbolKind.Indexer && entity.SymbolKind != SymbolKind.Operator && entity.Name == name; }; List lookupGroups = new List(); // This loop will handle base types before derived types. // The loop performs three jobs: // 1) It marks entries in lookup groups from base classes as removed when those members // are hidden by a derived class. // 2) It adds a new lookup group with the members from a declaring type. // 3) It replaces virtual members with the overridden version, placing the override in the // lookup group belonging to the base class. foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { List newNestedTypes = null; List newMethods = null; IMember newNonMethod = null; IEnumerable typeBaseTypes = null; if (!isInvocation && !targetIsTypeParameter) { // Consider nested types only if it's not an invocation. // type.GetNestedTypes() is checking the type parameter count for an exact match, // so we don't need to do that in our filter. var nestedTypes = type.GetNestedTypes(typeArguments, nestedTypeFilter, GetMemberOptions.IgnoreInheritedMembers); AddNestedTypes(type, nestedTypes, typeArguments.Count, lookupGroups, ref typeBaseTypes, ref newNestedTypes); } IEnumerable members; if (typeArguments.Count == 0) { // Note: IsInvocable-checking cannot be done as part of the filter; // because it must be done after type substitution. members = type.GetMembers(memberFilter, GetMemberOptions.IgnoreInheritedMembers); if (isInvocation) members = members.Where(m => IsInvocable(m)); } else { // No need to check for isInvocation/isInvocable here: // we only fetch methods members = type.GetMethods(typeArguments, memberFilter, GetMemberOptions.IgnoreInheritedMembers); } AddMembers(type, members, allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); if (newNestedTypes != null || newMethods != null || newNonMethod != null) lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); } // Remove interface members hidden by class members. if (targetIsTypeParameter) { // This can happen only with type parameters. RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); } return CreateResult(targetResolveResult, lookupGroups, name, typeArguments); } #endregion #region Lookup Indexer /// /// Looks up the indexers on the target type. /// public IReadOnlyList LookupIndexers(ResolveResult targetResolveResult) { if (targetResolveResult == null) throw new ArgumentNullException(nameof(targetResolveResult)); IType targetType = targetResolveResult.Type; bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); Predicate filter = p => p.IsIndexer && !p.IsExplicitInterfaceImplementation; List lookupGroups = new List(); foreach (IType type in targetType.GetNonInterfaceBaseTypes()) { List newMethods = null; IMember newNonMethod = null; IEnumerable typeBaseTypes = null; var members = type.GetProperties(filter, GetMemberOptions.IgnoreInheritedMembers); AddMembers(type, members, allowProtectedAccess, lookupGroups, true, ref typeBaseTypes, ref newMethods, ref newNonMethod); if (newMethods != null || newNonMethod != null) lookupGroups.Add(new LookupGroup(type, null, newMethods, newNonMethod)); } // Remove interface members hidden by class members. if (targetType.Kind == TypeKind.TypeParameter) { // This can happen only with type parameters. RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); } // Remove all hidden groups lookupGroups.RemoveAll(g => g.MethodsAreHidden || g.Methods.Count == 0); MethodListWithDeclaringType[] methodLists = new MethodListWithDeclaringType[lookupGroups.Count]; for (int i = 0; i < methodLists.Length; i++) { methodLists[i] = new MethodListWithDeclaringType(lookupGroups[i].DeclaringType, lookupGroups[i].Methods); } return methodLists; } #endregion #region AddNestedTypes /// /// Adds the nested types to 'newNestedTypes' and removes any hidden members from the existing lookup groups. /// /// Declaring type of the nested types /// List of nested types to add. /// The number of type arguments - used for hiding types from the base class /// List of existing lookup groups /// The base types of 'type' (initialized on demand) /// The target list (created on demand). void AddNestedTypes(IType type, IEnumerable nestedTypes, int typeArgumentCount, List lookupGroups, ref IEnumerable typeBaseTypes, ref List newNestedTypes) { foreach (IType nestedType in nestedTypes) { // Remove all non-types declared in a base type of 'type', // and all types with same number of type parameters declared in a base type of 'type'. foreach (var lookupGroup in lookupGroups) { if (lookupGroup.AllHidden) continue; // everything is already hidden if (typeBaseTypes == null) typeBaseTypes = type.GetNonInterfaceBaseTypes(); if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { lookupGroup.MethodsAreHidden = true; lookupGroup.NonMethodIsHidden = true; if (lookupGroup.NestedTypes != null) lookupGroup.NestedTypes.RemoveAll(t => InnerTypeParameterCount(t) == typeArgumentCount); } } // Add the new nested type. if (newNestedTypes == null) newNestedTypes = new List(); newNestedTypes.Add(nestedType); } } #endregion #region AddMembers /// /// Adds members to 'newMethods'/'newNonMethod'. /// Removes any members in the existing lookup groups that were hidden by added members. /// Substitutes 'virtual' members in the existing lookup groups for added 'override' members. /// /// Declaring type of the members /// List of members to add. /// Whether protected members are accessible /// List of existing lookup groups /// Whether to treat properties as methods /// The base types of 'type' (initialized on demand) /// The target list for methods (created on demand). /// The target variable for non-method members. void AddMembers(IType type, IEnumerable members, bool allowProtectedAccess, List lookupGroups, bool treatAllParameterizedMembersAsMethods, ref IEnumerable typeBaseTypes, ref List newMethods, ref IMember newNonMethod) { foreach (IMember member in members) { if (!IsAccessible(member, allowProtectedAccess)) continue; IParameterizedMember method; if (treatAllParameterizedMembersAsMethods) method = member as IParameterizedMember; else method = member as IMethod; bool replacedVirtualMemberWithOverride = false; if (member.IsOverride) { // Replacing virtual member with override: // Go backwards so that we find the corresponding virtual member // in the most-derived type for (int i = lookupGroups.Count - 1; i >= 0 && !replacedVirtualMemberWithOverride; i--) { if (typeBaseTypes == null) typeBaseTypes = type.GetNonInterfaceBaseTypes(); var lookupGroup = lookupGroups[i]; if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { if (method != null && !lookupGroup.MethodsAreHidden) { // Find the matching method, and replace it with the override for (int j = 0; j < lookupGroup.Methods.Count; j++) { if (SignatureComparer.Ordinal.Equals(method, lookupGroup.Methods[j])) { lookupGroup.Methods[j] = method; replacedVirtualMemberWithOverride = true; break; } } } else { // If the member type matches, replace it with the override if (lookupGroup.NonMethod != null && lookupGroup.NonMethod.SymbolKind == member.SymbolKind) { lookupGroup.NonMethod = member; replacedVirtualMemberWithOverride = true; break; } } } } } // If the member wasn't an override, or if we didn't find any matching virtual method, // proceed to add the member. if (!replacedVirtualMemberWithOverride) { // Make the member hide other members: foreach (var lookupGroup in lookupGroups) { if (lookupGroup.AllHidden) continue; // everything is already hidden if (typeBaseTypes == null) typeBaseTypes = type.GetNonInterfaceBaseTypes(); if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) { // Methods hide all non-methods; Non-methods hide everything lookupGroup.NestedTypes = null; lookupGroup.NonMethodIsHidden = true; if (method == null) { // !(member is IMethod) lookupGroup.MethodsAreHidden = true; } } } // Add the new member if (method != null) { if (newMethods == null) newMethods = new List(); newMethods.Add(method); } else { newNonMethod = member; } } } } #endregion #region RemoveInterfaceMembersHiddenByClassMembers void RemoveInterfaceMembersHiddenByClassMembers(List lookupGroups) { foreach (var classLookupGroup in lookupGroups) { if (IsInterfaceOrSystemObject(classLookupGroup.DeclaringType)) continue; // The current lookup groups contains class members that might hide interface members bool hasNestedTypes = classLookupGroup.NestedTypes != null && classLookupGroup.NestedTypes.Count > 0; if (hasNestedTypes || !classLookupGroup.NonMethodIsHidden) { // Hide all members from interface types foreach (var interfaceLookupGroup in lookupGroups) { if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) { interfaceLookupGroup.NestedTypes = null; interfaceLookupGroup.NonMethodIsHidden = true; interfaceLookupGroup.MethodsAreHidden = true; } } } else if (!classLookupGroup.MethodsAreHidden) { foreach (var classMethod in classLookupGroup.Methods) { // Hide all non-methods from interface types, and all methods with the same signature // as a method in this class type. foreach (var interfaceLookupGroup in lookupGroups) { if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) { interfaceLookupGroup.NestedTypes = null; interfaceLookupGroup.NonMethodIsHidden = true; if (interfaceLookupGroup.Methods != null && !interfaceLookupGroup.MethodsAreHidden) { // The mapping of virtual to overridden methods is already done, // so we can simply remove the methods from the collection interfaceLookupGroup.Methods.RemoveAll( m => SignatureComparer.Ordinal.Equals(classMethod, m)); } } } } } } } static bool IsInterfaceOrSystemObject(IType type) { // return true if type is an interface or System.Object if (type.Kind == TypeKind.Interface) return true; ITypeDefinition d = type.GetDefinition(); return d != null && d.KnownTypeCode == KnownTypeCode.Object; } #endregion #region CreateResult ResolveResult CreateResult(ResolveResult targetResolveResult, List lookupGroups, string name, IReadOnlyList typeArguments) { // Remove all hidden groups lookupGroups.RemoveAll(g => g.AllHidden); if (lookupGroups.Count == 0) { // No members found return new UnknownMemberResolveResult(targetResolveResult.Type, name, typeArguments); } if (lookupGroups.Any(g => !g.MethodsAreHidden && g.Methods.Count > 0)) { // If there are methods, make a MethodGroupResolveResult. // Note that a conflict between a member and a method (possible with multiple interface inheritance) // is only a warning, not an error, and the C# compiler will prefer the method group. List methodLists = new List(); foreach (var lookupGroup in lookupGroups) { if (!lookupGroup.MethodsAreHidden && lookupGroup.Methods.Count > 0) { var methodListWithDeclType = new MethodListWithDeclaringType(lookupGroup.DeclaringType); foreach (var method in lookupGroup.Methods) { methodListWithDeclType.Add((IMethod)method); } methodLists.Add(methodListWithDeclType); } } return new MethodGroupResolveResult(targetResolveResult, name, methodLists, typeArguments); } // If there are ambiguities, report the most-derived result (last group) LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1]; if (resultGroup.NestedTypes != null && resultGroup.NestedTypes.Count > 0) { if (resultGroup.NestedTypes.Count > 1 || !resultGroup.NonMethodIsHidden || lookupGroups.Count > 1) return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]); else return new TypeResolveResult(resultGroup.NestedTypes[0]); } if (resultGroup.NonMethod.IsStatic && targetResolveResult is ThisResolveResult) { targetResolveResult = new TypeResolveResult(targetResolveResult.Type); } if (lookupGroups.Count > 1) { return new AmbiguousMemberResolveResult(targetResolveResult, resultGroup.NonMethod); } else { if (isInEnumMemberInitializer) { IField field = resultGroup.NonMethod as IField; if (field != null && field.DeclaringTypeDefinition != null && field.DeclaringTypeDefinition.Kind == TypeKind.Enum) { return new MemberResolveResult( targetResolveResult, field, field.DeclaringTypeDefinition.EnumUnderlyingType, field.IsConst, field.GetConstantValue()); } } return new MemberResolveResult(targetResolveResult, resultGroup.NonMethod); } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Net.NetworkInformation; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// A method list that belongs to a declaring type. /// public class MethodListWithDeclaringType : List { readonly IType declaringType; /// /// The declaring type. /// /// /// Not all methods in this list necessarily have this as their declaring type. /// For example, this program: /// /// class Base { /// public virtual void M() {} /// } /// class Derived : Base { /// public override void M() {} /// public void M(int i) {} /// } /// /// results in two lists: /// new MethodListWithDeclaringType(Base) { Derived.M() }, /// new MethodListWithDeclaringType(Derived) { Derived.M(int) } /// public IType DeclaringType { get { return declaringType; } } public MethodListWithDeclaringType(IType declaringType) { this.declaringType = declaringType; } public MethodListWithDeclaringType(IType declaringType, IEnumerable methods) : base(methods) { this.declaringType = declaringType; } } /// /// Represents a group of methods. /// A method reference used to create a delegate is resolved to a MethodGroupResolveResult. /// The MethodGroupResolveResult has no type. /// To retrieve the delegate type or the chosen overload, look at the method group conversion. /// public class MethodGroupResolveResult : ResolveResult { readonly IReadOnlyList methodLists; readonly IReadOnlyList typeArguments; readonly ResolveResult targetResult; readonly string methodName; IMethod chosenMethod; public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IReadOnlyList methods, IReadOnlyList typeArguments) : base(SpecialType.NoType) { if (methods == null) throw new ArgumentNullException(nameof(methods)); this.targetResult = targetResult; this.methodName = methodName; this.methodLists = methods; this.typeArguments = typeArguments ?? EmptyList.Instance; } /// /// Gets the resolve result for the target object. /// public ResolveResult TargetResult { get { return targetResult; } } /// /// Gets the type of the reference to the target object. /// public IType TargetType { get { return targetResult != null ? targetResult.Type : SpecialType.UnknownType; } } /// /// Gets the name of the methods in this group. /// public string MethodName { get { return methodName; } } /// /// Gets the methods that were found. /// This list does not include extension methods. /// public IEnumerable Methods { get { return methodLists.SelectMany(m => m.Cast()); } } /// /// Gets the methods that were found, grouped by their declaring type. /// This list does not include extension methods. /// Base types come first in the list. /// public IEnumerable MethodsGroupedByDeclaringType { get { return methodLists; } } /// /// Gets the type arguments that were explicitly provided. /// public IReadOnlyList TypeArguments { get { return typeArguments; } } /// /// List of extension methods, used to avoid re-calculating it in ResolveInvocation() when it was already /// calculated by ResolveMemberAccess(). /// internal List> extensionMethods; // the resolver is used to fetch extension methods on demand internal CSharpResolver resolver; /// /// Gets the method that was chosen for this group. /// /// Only set for MethodGroupResolveResults found in ILSpy AST annotations. /// public IMethod ChosenMethod => chosenMethod; public MethodGroupResolveResult WithChosenMethod(IMethod method) { var result = (MethodGroupResolveResult)ShallowClone(); result.chosenMethod = method; return result; } /// /// Gets all candidate extension methods. /// Note: this includes candidates that are not eligible due to an inapplicable /// this argument. /// The candidates will only be specialized if the type arguments were provided explicitly. /// /// /// The results are stored in nested lists because they are grouped by using scope. /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", /// the return value will be /// new List { /// new List { all extensions from MoreExtensions }, /// new List { all extensions from SomeExtensions } /// } /// public IEnumerable> GetExtensionMethods() { if (resolver != null) { Debug.Assert(extensionMethods == null); try { extensionMethods = resolver.GetExtensionMethods(methodName, typeArguments); } finally { resolver = null; } } return extensionMethods ?? Enumerable.Empty>(); } /// /// Gets the eligible extension methods. /// /// /// Specifies whether to produce a SpecializedMethod /// when type arguments could be inferred from . /// This setting is only used for inferred types and has no effect if the type parameters are /// specified explicitly. /// /// /// The results are stored in nested lists because they are grouped by using scope. /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", /// the return value will be /// new List { /// new List { all extensions from MoreExtensions }, /// new List { all extensions from SomeExtensions } /// } /// public IEnumerable> GetEligibleExtensionMethods(bool substituteInferredTypes) { var result = new List>(); foreach (var methodGroup in GetExtensionMethods()) { var outputGroup = new List(); foreach (var method in methodGroup) { if (CSharpResolver.IsEligibleExtensionMethod(this.TargetType, method, true, out IType[] inferredTypes)) { if (substituteInferredTypes && inferredTypes != null) { outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes))); } else { outputGroup.Add(method); } } } if (outputGroup.Count > 0) result.Add(outputGroup); } return result; } public override string ToString() { return string.Format("[{0} with {1} method(s)]", GetType().Name, this.Methods.Count()); } public OverloadResolution PerformOverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true, bool allowOptionalParameters = true, bool allowImplicitIn = true, bool checkForOverflow = false, CSharpConversions conversions = null) { Log.WriteLine("Performing overload resolution for " + this); Log.WriteCollection(" Arguments: ", arguments); var typeArgumentArray = this.TypeArguments.ToArray(); OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, typeArgumentArray, conversions); or.AllowExpandingParams = allowExpandingParams; or.AllowOptionalParameters = allowOptionalParameters; or.CheckForOverflow = checkForOverflow; or.AllowImplicitIn = allowImplicitIn; or.AddMethodLists(methodLists); if (allowExtensionMethods && !or.FoundApplicableCandidate) { // No applicable match found, so let's try extension methods. var extensionMethods = this.GetExtensionMethods(); if (extensionMethods.Any()) { Log.WriteLine("No candidate is applicable, trying {0} extension methods groups...", extensionMethods.Count()); ResolveResult[] extArguments = new ResolveResult[arguments.Length + 1]; extArguments[0] = new ResolveResult(this.TargetType); arguments.CopyTo(extArguments, 1); string[] extArgumentNames = null; if (argumentNames != null) { extArgumentNames = new string[argumentNames.Length + 1]; argumentNames.CopyTo(extArgumentNames, 1); } var extOr = new OverloadResolution(compilation, extArguments, extArgumentNames, typeArgumentArray, conversions); extOr.AllowExpandingParams = allowExpandingParams; extOr.AllowOptionalParameters = allowOptionalParameters; extOr.IsExtensionMethodInvocation = true; extOr.CheckForOverflow = checkForOverflow; extOr.AllowImplicitIn = allowImplicitIn; foreach (var g in extensionMethods) { foreach (var method in g) { Log.Indent(); OverloadResolutionErrors errors = extOr.AddCandidate(method); Log.Unindent(); or.LogCandidateAddingResult(" Extension", method, errors); } if (extOr.FoundApplicableCandidate) break; } // For the lack of a better comparison function (the one within OverloadResolution // cannot be used as it depends on the argument set): if (extOr.FoundApplicableCandidate || or.BestCandidate == null) { // Consider an extension method result better than the normal result only // if it's applicable; or if there is no normal result. or = extOr; } } } Log.WriteLine("Overload resolution finished, best candidate is {0}.", or.GetBestCandidateWithSubstitutedTypeArguments()); return or; } public override IEnumerable GetChildResults() { if (targetResult != null) return new[] { targetResult }; else return Enumerable.Empty(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/NameLookupMode.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Resolver { public enum NameLookupMode { /// /// Normal name lookup in expressions /// Expression, /// /// Name lookup in expression, where the expression is the target of an invocation. /// Such a lookup will only return methods and delegate-typed fields. /// InvocationTarget, /// /// Normal name lookup in type references. /// Type, /// /// Name lookup in the type reference inside a using declaration. /// TypeInUsingDeclaration, /// /// Name lookup for base type references. /// BaseTypeReference } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { /// /// C# overload resolution (C# 4.0 spec: §7.5). /// public class OverloadResolution { sealed class Candidate { public readonly IParameterizedMember Member; /// /// Returns the normal form candidate, if this is an expanded candidate. /// public readonly bool IsExpandedForm; /// /// Gets the parameter types. In the first step, these are the types without any substitution. /// After type inference, substitutions will be performed. /// public readonly IType[] ParameterTypes; /// /// argument index -> parameter index; -1 for arguments that could not be mapped /// public int[] ArgumentToParameterMap; public OverloadResolutionErrors Errors; public int ErrorCount; public bool HasUnmappedOptionalParameters; public IType[] InferredTypes; /// /// Gets the original member parameters (before any substitution!) /// public readonly IReadOnlyList Parameters; /// /// Gets the original method type parameters (before any substitution!) /// public readonly IReadOnlyList TypeParameters; /// /// Conversions applied to the arguments. /// This field is set by the CheckApplicability step. /// public Conversion[] ArgumentConversions; /// /// Gets the type of the collection that is used for the 'params' parameter (before any substitution!). /// Otherwise returns SpecialType.UnknownType. /// public IType ParamsCollectionType { get { if (IsExpandedForm && Parameters.Count > 0) { IParameter lastParameter = Parameters[Parameters.Count - 1]; if (lastParameter.IsParams) { return lastParameter.Type; } } return SpecialType.UnknownType; } } public bool IsGenericMethod { get { IMethod method = Member as IMethod; return method != null && method.TypeParameters.Count > 0; } } public int ArgumentsPassedToParams { get { int count = 0; if (IsExpandedForm) { int paramsParameterIndex = this.Parameters.Count - 1; foreach (int parameterIndex in ArgumentToParameterMap) { if (parameterIndex == paramsParameterIndex) count++; } } return count; } } public Candidate(IParameterizedMember member, bool isExpanded) { this.Member = member; this.IsExpandedForm = isExpanded; IParameterizedMember memberDefinition = (IParameterizedMember)member.MemberDefinition; // For specialized methods, go back to the original parameters: // (without any type parameter substitution, not even class type parameters) // We'll re-substitute them as part of RunTypeInference(). this.Parameters = memberDefinition.Parameters; IMethod methodDefinition = memberDefinition as IMethod; if (methodDefinition != null && methodDefinition.TypeParameters.Count > 0) { this.TypeParameters = methodDefinition.TypeParameters; } this.ParameterTypes = new IType[this.Parameters.Count]; } public void AddError(OverloadResolutionErrors newError) { this.Errors |= newError; if (!IsApplicable(newError)) this.ErrorCount++; } } readonly ICompilation compilation; readonly ResolveResult[] arguments; readonly string[] argumentNames; readonly CSharpConversions conversions; //List candidates = new List(); Candidate bestCandidate; Candidate bestCandidateAmbiguousWith; IType[] explicitlyGivenTypeArguments; bool bestCandidateWasValidated; OverloadResolutionErrors bestCandidateValidationResult; #region Constructor public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (arguments == null) throw new ArgumentNullException(nameof(arguments)); if (argumentNames == null) argumentNames = new string[arguments.Length]; else if (argumentNames.Length != arguments.Length) throw new ArgumentException("argumentsNames.Length must be equal to arguments.Length"); this.compilation = compilation; this.arguments = arguments; this.argumentNames = argumentNames; // keep explicitlyGivenTypeArguments==null when no type arguments were specified if (typeArguments != null && typeArguments.Length > 0) this.explicitlyGivenTypeArguments = typeArguments; this.conversions = conversions ?? CSharpConversions.Get(compilation); this.AllowExpandingParams = true; this.AllowOptionalParameters = true; } #endregion #region Input Properties /// /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax. /// /// /// Setting this property to true restricts the possible conversions on the first argument to /// implicit identity, reference, or boxing conversions. /// public bool IsExtensionMethodInvocation { get; set; } /// /// Gets/Sets whether expanding 'params' into individual elements is allowed. /// The default value is true. /// public bool AllowExpandingParams { get; set; } /// /// Gets/Sets whether optional parameters may be left at their default value. /// The default value is true. /// If this property is set to false, optional parameters will be treated like regular parameters. /// public bool AllowOptionalParameters { get; set; } /// /// Gets/Sets whether a value argument can be passed to an `in` reference parameter. /// public bool AllowImplicitIn { get; set; } = true; /// /// Gets/Sets whether ConversionResolveResults created by this OverloadResolution /// instance apply overflow checking. /// The default value is false. /// public bool CheckForOverflow { get; set; } /// /// Gets the arguments for which this OverloadResolution instance was created. /// public IList Arguments { get { return arguments; } } #endregion #region AddCandidate /// /// Adds a candidate to overload resolution. /// /// The candidate member to add. /// The errors that prevent the member from being applicable, if any. /// Note: this method does not return errors that do not affect applicability. public OverloadResolutionErrors AddCandidate(IParameterizedMember member) { return AddCandidate(member, OverloadResolutionErrors.None); } /// /// Adds a candidate to overload resolution. /// /// The candidate member to add. /// Additional errors that apply to the candidate. /// This is used to represent errors during member lookup (e.g. OverloadResolutionErrors.Inaccessible) /// in overload resolution. /// The errors that prevent the member from being applicable, if any. /// Note: this method does not return errors that do not affect applicability. public OverloadResolutionErrors AddCandidate(IParameterizedMember member, OverloadResolutionErrors additionalErrors) { if (member == null) throw new ArgumentNullException(nameof(member)); Candidate c = new Candidate(member, false); c.AddError(additionalErrors); if (CalculateCandidate(c)) { //candidates.Add(c); } if (this.AllowExpandingParams && member.Parameters.Count > 0 && member.Parameters[member.Parameters.Count - 1].IsParams) { Candidate expandedCandidate = new Candidate(member, true); expandedCandidate.AddError(additionalErrors); // consider expanded form only if it isn't obviously wrong if (CalculateCandidate(expandedCandidate)) { //candidates.Add(expandedCandidate); if (expandedCandidate.ErrorCount < c.ErrorCount) return expandedCandidate.Errors; } } return c.Errors; } /// /// Calculates applicability etc. for the candidate. /// /// True if the calculation was successful, false if the candidate should be removed without reporting an error bool CalculateCandidate(Candidate candidate) { if (!ResolveParameterTypes(candidate, false)) return false; MapCorrespondingParameters(candidate); RunTypeInference(candidate); CheckApplicability(candidate); ConsiderIfNewCandidateIsBest(candidate); return true; } bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters) { for (int i = 0; i < candidate.Parameters.Count; i++) { IType type; if (useSpecializedParameters) { // Use the parameter type of the specialized non-generic method or indexer Debug.Assert(!candidate.IsGenericMethod); type = candidate.Member.Parameters[i].Type; } else { // Use the type of the original formal parameter type = candidate.Parameters[i].Type; } if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) { if (type is ArrayType arrayType && arrayType.Dimensions == 1) type = arrayType.ElementType; else if (type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) || type.IsKnownType(KnownTypeCode.SpanOfT)) type = type.TypeArguments[0]; else if (type.IsArrayInterfaceType()) type = type.TypeArguments[0]; else return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate } candidate.ParameterTypes[i] = type; } return true; } #endregion #region AddMethodLists /// /// Adds all candidates from the method lists. /// /// This method implements the logic that causes applicable methods in derived types to hide /// all methods in base types. /// /// The methods, grouped by declaring type. Base types must come first in the list. public void AddMethodLists(IReadOnlyList methodLists) { if (methodLists == null) throw new ArgumentNullException(nameof(methodLists)); // Base types come first, so go through the list backwards (derived types first) bool[] isHiddenByDerivedType; if (methodLists.Count > 1) isHiddenByDerivedType = new bool[methodLists.Count]; else isHiddenByDerivedType = null; for (int i = methodLists.Count - 1; i >= 0; i--) { if (isHiddenByDerivedType != null && isHiddenByDerivedType[i]) { Log.WriteLine(" Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType); continue; } MethodListWithDeclaringType methodList = methodLists[i]; bool foundApplicableCandidateInCurrentList = false; for (int j = 0; j < methodList.Count; j++) { IParameterizedMember method = methodList[j]; Log.Indent(); OverloadResolutionErrors errors = AddCandidate(method); Log.Unindent(); LogCandidateAddingResult(" Candidate", method, errors); foundApplicableCandidateInCurrentList |= IsApplicable(errors); } if (foundApplicableCandidateInCurrentList && i > 0) { foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes()) { for (int j = 0; j < i; j++) { if (!isHiddenByDerivedType[j] && baseType.Equals(methodLists[j].DeclaringType)) isHiddenByDerivedType[j] = true; } } } } } [Conditional("DEBUG")] internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors) { #if DEBUG Log.WriteLine(string.Format("{0} {1} = {2}{3}", text, method, errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(), this.BestCandidate == method ? " (best candidate so far)" : this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : "" )); #endif } #endregion #region MapCorrespondingParameters void MapCorrespondingParameters(Candidate candidate) { // C# 4.0 spec: §7.5.1.1 Corresponding parameters // Updated for C# 7.2 non-trailing named arguments candidate.ArgumentToParameterMap = new int[arguments.Length]; bool hasPositionalArgument = false; // go backwards, so that hasPositionalArgument tells us whether there // are non-trailing named arguments for (int i = arguments.Length - 1; i >= 0; i--) { candidate.ArgumentToParameterMap[i] = -1; if (argumentNames[i] == null || hasPositionalArgument) { hasPositionalArgument = true; // positional argument or non-trailing named argument if (i < candidate.ParameterTypes.Length) { candidate.ArgumentToParameterMap[i] = i; if (argumentNames[i] != null && argumentNames[i] != candidate.Parameters[i].Name) { // non-trailing named argument must match name candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument); } } else if (candidate.IsExpandedForm) { candidate.ArgumentToParameterMap[i] = candidate.ParameterTypes.Length - 1; if (argumentNames[i] != null) { // can't use non-trailing named argument here candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument); } } else { candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments); } } else { // (trailing) named argument for (int j = 0; j < candidate.Parameters.Count; j++) { if (argumentNames[i] == candidate.Parameters[j].Name) { candidate.ArgumentToParameterMap[i] = j; } } if (candidate.ArgumentToParameterMap[i] < 0) candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument); } } } #endregion #region RunTypeInference void RunTypeInference(Candidate candidate) { if (candidate.TypeParameters == null) { if (explicitlyGivenTypeArguments != null) { // method does not expect type arguments, but was given some candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); } // Grab new parameter types: ResolveParameterTypes(candidate, true); return; } ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType; IReadOnlyList classTypeArguments; if (parameterizedDeclaringType != null) { classTypeArguments = parameterizedDeclaringType.TypeArguments; } else { classTypeArguments = null; } // The method is generic: if (explicitlyGivenTypeArguments != null) { if (explicitlyGivenTypeArguments.Length == candidate.TypeParameters.Count) { candidate.InferredTypes = explicitlyGivenTypeArguments; } else { candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments); // wrong number of type arguments given, so truncate the list or pad with UnknownType candidate.InferredTypes = new IType[candidate.TypeParameters.Count]; for (int i = 0; i < candidate.InferredTypes.Length; i++) { if (i < explicitlyGivenTypeArguments.Length) candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i]; else candidate.InferredTypes[i] = SpecialType.UnknownType; } } } else { TypeInference ti = new TypeInference(compilation, conversions); IType[] parameterTypes = candidate.ArgumentToParameterMap .SelectReadOnlyArray(parameterIndex => parameterIndex >= 0 ? candidate.ParameterTypes[parameterIndex] : SpecialType.UnknownType); bool success; candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, parameterTypes, out success, classTypeArguments); if (!success) candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed); } // Now substitute in the formal parameters: var substitution = new ConstraintValidatingSubstitution(classTypeArguments, candidate.InferredTypes, this); for (int i = 0; i < candidate.ParameterTypes.Length; i++) { candidate.ParameterTypes[i] = candidate.ParameterTypes[i].AcceptVisitor(substitution); } if (!substitution.ConstraintsValid) candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint); } sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution { readonly CSharpConversions conversions; public bool ConstraintsValid = true; public ConstraintValidatingSubstitution(IReadOnlyList classTypeArguments, IReadOnlyList methodTypeArguments, OverloadResolution overloadResolution) : base(classTypeArguments, methodTypeArguments) { this.conversions = overloadResolution.conversions; } public override IType VisitParameterizedType(ParameterizedType type) { IType newType = base.VisitParameterizedType(type); if (newType != type && ConstraintsValid) { // something was changed, so we need to validate the constraints ParameterizedType newParameterizedType = newType as ParameterizedType; if (newParameterizedType != null) { // C# 4.0 spec: §4.4.4 Satisfying constraints var typeParameters = newParameterizedType.TypeParameters; var substitution = newParameterizedType.GetSubstitution(); for (int i = 0; i < typeParameters.Count; i++) { if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) { ConstraintsValid = false; break; } } } } return newType; } } #endregion #region Validate Constraints OverloadResolutionErrors ValidateMethodConstraints(Candidate candidate) { // If type inference already failed, we won't check the constraints: if ((candidate.Errors & OverloadResolutionErrors.TypeInferenceFailed) != 0) return OverloadResolutionErrors.None; if (candidate.TypeParameters == null || candidate.TypeParameters.Count == 0) return OverloadResolutionErrors.None; // the method isn't generic var substitution = GetSubstitution(candidate); for (int i = 0; i < candidate.TypeParameters.Count; i++) { if (!ValidateConstraints(candidate.TypeParameters[i], substitution.MethodTypeArguments[i], substitution)) return OverloadResolutionErrors.MethodConstraintsNotSatisfied; } return OverloadResolutionErrors.None; } /// /// Validates whether the given type argument satisfies the constraints for the given type parameter. /// /// The type parameter. /// The type argument. /// The substitution that defines how type parameters are replaced with type arguments. /// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter). /// May be null if no substitution should be used. /// True if the constraints are satisfied; false otherwise. public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution = null) { if (typeParameter == null) throw new ArgumentNullException(nameof(typeParameter)); if (typeArgument == null) throw new ArgumentNullException(nameof(typeArgument)); return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation)); } internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions) { switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments case TypeKind.Void: case TypeKind.Null: case TypeKind.Pointer: return false; } if (typeParameter.HasReferenceTypeConstraint) { if (typeArgument.IsReferenceType != true) return false; } if (typeParameter.HasValueTypeConstraint) { if (!NullableType.IsNonNullableValueType(typeArgument)) return false; } if (typeParameter.HasDefaultConstructorConstraint) { ITypeDefinition def = typeArgument.GetDefinition(); if (def != null && def.IsAbstract) return false; var ctors = typeArgument.GetConstructors( m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public, GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions ); if (!ctors.Any()) return false; } foreach (IType constraintType in typeParameter.DirectBaseTypes) { IType c = constraintType; if (substitution != null) c = c.AcceptVisitor(substitution); if (!conversions.IsConstraintConvertible(typeArgument, c)) return false; } return true; } #endregion #region CheckApplicability /// /// Returns whether a candidate with the given errors is still considered to be applicable. /// public static bool IsApplicable(OverloadResolutionErrors errors) { const OverloadResolutionErrors errorsThatDoNotMatterForApplicability = OverloadResolutionErrors.AmbiguousMatch | OverloadResolutionErrors.MethodConstraintsNotSatisfied; return (errors & ~errorsThatDoNotMatterForApplicability) == OverloadResolutionErrors.None; } void CheckApplicability(Candidate candidate) { // C# 4.0 spec: §7.5.3.1 Applicable function member // Test whether parameters were mapped the correct number of arguments: int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length]; foreach (int parameterIndex in candidate.ArgumentToParameterMap) { if (parameterIndex >= 0) argumentCountPerParameter[parameterIndex]++; } for (int i = 0; i < argumentCountPerParameter.Length; i++) { if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1) continue; // any number of arguments is fine for the params-array if (argumentCountPerParameter[i] == 0) { if (this.AllowOptionalParameters && candidate.Parameters[i].IsOptional) candidate.HasUnmappedOptionalParameters = true; else candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter); } else if (argumentCountPerParameter[i] > 1) { candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter); } } candidate.ArgumentConversions = new Conversion[arguments.Length]; // Test whether argument passing mode matches the parameter passing mode for (int i = 0; i < arguments.Length; i++) { int parameterIndex = candidate.ArgumentToParameterMap[i]; if (parameterIndex < 0) { candidate.ArgumentConversions[i] = Conversion.None; continue; } ReferenceKind paramRefKind = candidate.Parameters[parameterIndex].ReferenceKind; if (arguments[i] is ByReferenceResolveResult brrr) { if (brrr.ReferenceKind != paramRefKind) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } else if (arguments[i] is OutVarResolveResult) { if (paramRefKind != ReferenceKind.Out) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); // 'out var decl' arguments are compatible with any out parameter continue; } else { // AllowImplicitIn: `in` parameters can be filled implicitly without `in` DirectionExpression // IsExtensionMethodInvocation: `this ref` and `this in` parameters can be filled implicitly if (((paramRefKind is ReferenceKind.In or ReferenceKind.RefReadOnly && AllowImplicitIn) || (IsExtensionMethodInvocation && parameterIndex == 0 && (paramRefKind is ReferenceKind.In or ReferenceKind.Ref or ReferenceKind.RefReadOnly)) ) && candidate.ParameterTypes[parameterIndex].SkipModifiers() is ByReferenceType brt) { // Treat the parameter as if it was not declared "in" for the following steps // (applicability + better function member) candidate.ParameterTypes[parameterIndex] = brt.ElementType; } else if (paramRefKind != ReferenceKind.None) { candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } } IType parameterType = candidate.ParameterTypes[parameterIndex]; Conversion c = conversions.ImplicitConversion(arguments[i], parameterType); candidate.ArgumentConversions[i] = c; if (IsExtensionMethodInvocation && parameterIndex == 0) { // First parameter to extension method must be an identity, reference, boxing or span conversion if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion || c == Conversion.ImplicitSpanConversion)) candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); } else { if ((!c.IsValid && !c.IsUserDefined && !c.IsMethodGroupConversion) && parameterType.Kind != TypeKind.Unknown) candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); } } } #endregion #region BetterFunctionMember /// /// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better. /// int BetterFunctionMember(Candidate c1, Candidate c2) { // prefer applicable members (part of heuristic that produces a best candidate even if none is applicable) if (c1.ErrorCount == 0 && c2.ErrorCount > 0) return 1; if (c1.ErrorCount > 0 && c2.ErrorCount == 0) return 2; // C# 4.0 spec: §7.5.3.2 Better function member bool c1IsBetter = false; bool c2IsBetter = false; bool parameterTypesEqual = true; for (int i = 0; i < arguments.Length; i++) { int p1 = c1.ArgumentToParameterMap[i]; int p2 = c2.ArgumentToParameterMap[i]; if (p1 >= 0 && p2 < 0) { c1IsBetter = true; } else if (p1 < 0 && p2 >= 0) { c2IsBetter = true; } else if (p1 >= 0 && p2 >= 0) { if (!conversions.IdentityConversion(c1.ParameterTypes[p1], c2.ParameterTypes[p2])) parameterTypesEqual = false; switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) { case 1: c1IsBetter = true; break; case 2: c2IsBetter = true; break; } } } if (c1IsBetter && !c2IsBetter) return 1; if (!c1IsBetter && c2IsBetter) return 2; // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable) if (c1.ErrorCount < c2.ErrorCount) return 1; if (c1.ErrorCount > c2.ErrorCount) return 2; if (!c1IsBetter && !c2IsBetter && parameterTypesEqual) { // we need the tie-breaking rules // non-generic methods are better if (!c1.IsGenericMethod && c2.IsGenericMethod) return 1; else if (c1.IsGenericMethod && !c2.IsGenericMethod) return 2; // non-expanded members are better if (!c1.IsExpandedForm && c2.IsExpandedForm) return 1; else if (c1.IsExpandedForm && !c2.IsExpandedForm) return 2; // prefer the member with less arguments mapped to the params-collection int r = c1.ArgumentsPassedToParams.CompareTo(c2.ArgumentsPassedToParams); if (r < 0) return 1; else if (r > 0) return 2; // prefer the member where no default values need to be substituted if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters) return 1; else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters) return 2; // compare the formal parameters r = MoreSpecificFormalParameters(c1, c2); if (r != 0) return r; // prefer non-lifted operators ILiftedOperator lift1 = c1.Member as ILiftedOperator; ILiftedOperator lift2 = c2.Member as ILiftedOperator; if (lift1 == null && lift2 != null) return 1; if (lift1 != null && lift2 == null) return 2; // prefer by-value parameters over in-parameters r = BetterParameterPassingChoice(c1, c2); if (r != 0) return r; if (c1.IsExpandedForm) { Debug.Assert(c2.IsExpandedForm); r = BetterParamsCollectionType(c1.ParamsCollectionType, c2.ParamsCollectionType); if (r != 0) return r; } } return 0; } int BetterParamsCollectionType(IType paramsCollectionType1, IType paramsCollectionType2) { // see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-13.0/params-collections#better-function-member bool isSpan1 = paramsCollectionType1.IsKnownType(KnownTypeCode.SpanOfT) || paramsCollectionType1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT); bool isSpan2 = paramsCollectionType2.IsKnownType(KnownTypeCode.SpanOfT) || paramsCollectionType2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT); if (!isSpan1 && !isSpan2) { bool implicitConversion1to2 = conversions.ImplicitConversion(paramsCollectionType1, paramsCollectionType2).IsValid; bool implicitConversion2to1 = conversions.ImplicitConversion(paramsCollectionType2, paramsCollectionType1).IsValid; if (implicitConversion1to2 && !implicitConversion2to1) return 1; if (!implicitConversion1to2 && implicitConversion2to1) return 2; } else if (paramsCollectionType1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && paramsCollectionType2.IsKnownType(KnownTypeCode.SpanOfT)) { if (conversions.IdentityConversion(paramsCollectionType1.TypeArguments[0], paramsCollectionType2.TypeArguments[0])) return 1; // ReadOnlySpan is better than Span if there exists an identity conversion between the element types } else if (paramsCollectionType2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && paramsCollectionType1.IsKnownType(KnownTypeCode.SpanOfT)) { if (conversions.IdentityConversion(paramsCollectionType2.TypeArguments[0], paramsCollectionType1.TypeArguments[0])) return 2; // ReadOnlySpan is better than Span if there exists an identity conversion between the element types } else if (isSpan1 && IsArrayOrArrayInterfaceType(paramsCollectionType2, out var elementType2)) { if (conversions.IdentityConversion(paramsCollectionType1.TypeArguments[0], elementType2)) return 1; // Span is better than an array type if there exists an identity conversion between the element types } else if (isSpan2 && IsArrayOrArrayInterfaceType(paramsCollectionType1, out var elementType1)) { if (conversions.IdentityConversion(paramsCollectionType2.TypeArguments[0], elementType1)) return 2; // Span is better than an array type if there exists an identity conversion between the element types } return 0; } bool IsArrayOrArrayInterfaceType(IType type, out IType elementType) { elementType = null; if (type is ArrayType arrayType) { elementType = arrayType.ElementType; return true; } if (type.IsArrayInterfaceType()) { elementType = type.TypeArguments[0]; return true; } return false; } int BetterParameterPassingChoice(Candidate c1, Candidate c2) { Debug.Assert(c1.Parameters.Count == c2.Parameters.Count, "c1 and c2 must have the same number of parameters"); bool c1IsBetter = false; bool c2IsBetter = false; for (int i = 0; i < c1.Parameters.Count; i++) { ReferenceKind refKind1 = c1.Parameters[i].ReferenceKind; ReferenceKind refKind2 = c2.Parameters[i].ReferenceKind; if (refKind1 == ReferenceKind.None && refKind2 == ReferenceKind.In) c1IsBetter = true; // by-value is better than in if (refKind1 == ReferenceKind.In && refKind2 == ReferenceKind.None) c2IsBetter = true; } if (c1IsBetter && !c2IsBetter) return 1; if (!c1IsBetter && c2IsBetter) return 2; return 0; } int MoreSpecificFormalParameters(Candidate c1, Candidate c2) { // prefer the member with more formal parameters (in case both have different number of optional parameters) int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count); if (r > 0) return 1; else if (r < 0) return 2; return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type)); } static int MoreSpecificFormalParameters(IEnumerable t1, IEnumerable t2) { bool c1IsBetter = false; bool c2IsBetter = false; foreach (var pair in t1.Zip(t2, (a, b) => new { Item1 = a, Item2 = b })) { switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) { case 1: c1IsBetter = true; break; case 2: c2IsBetter = true; break; } } if (c1IsBetter && !c2IsBetter) return 1; if (!c1IsBetter && c2IsBetter) return 2; return 0; } static int MoreSpecificFormalParameter(IType t1, IType t2) { if ((t1 is ITypeParameter) && !(t2 is ITypeParameter)) return 2; if ((t2 is ITypeParameter) && !(t1 is ITypeParameter)) return 1; ParameterizedType p1 = t1 as ParameterizedType; ParameterizedType p2 = t2 as ParameterizedType; if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) { int r = MoreSpecificFormalParameters(p1.TypeArguments, p2.TypeArguments); if (r > 0) return r; } TypeWithElementType tew1 = t1 as TypeWithElementType; TypeWithElementType tew2 = t2 as TypeWithElementType; if (tew1 != null && tew2 != null) { return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType); } return 0; } #endregion #region ConsiderIfNewCandidateIsBest void ConsiderIfNewCandidateIsBest(Candidate candidate) { if (bestCandidate == null) { bestCandidate = candidate; bestCandidateWasValidated = false; } else { switch (BetterFunctionMember(candidate, bestCandidate)) { case 0: // Overwrite 'bestCandidateAmbiguousWith' so that API users can // detect the set of all ambiguous methods if they look at // bestCandidateAmbiguousWith after each step. bestCandidateAmbiguousWith = candidate; break; case 1: bestCandidate = candidate; bestCandidateWasValidated = false; bestCandidateAmbiguousWith = null; break; // case 2: best candidate stays best } } } #endregion #region Output Properties public IParameterizedMember BestCandidate { get { return bestCandidate != null ? bestCandidate.Member : null; } } /// /// Returns the errors that apply to the best candidate. /// This includes additional errors that do not affect applicability (e.g. AmbiguousMatch, MethodConstraintsNotSatisfied) /// public OverloadResolutionErrors BestCandidateErrors { get { if (bestCandidate == null) return OverloadResolutionErrors.None; if (!bestCandidateWasValidated) { bestCandidateValidationResult = ValidateMethodConstraints(bestCandidate); bestCandidateWasValidated = true; } OverloadResolutionErrors err = bestCandidate.Errors | bestCandidateValidationResult; if (bestCandidateAmbiguousWith != null) err |= OverloadResolutionErrors.AmbiguousMatch; return err; } } public bool FoundApplicableCandidate { get { return bestCandidate != null && IsApplicable(bestCandidate.Errors); } } public IParameterizedMember BestCandidateAmbiguousWith { get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; } } public bool BestCandidateIsExpandedForm { get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; } } public bool IsAmbiguous { get { return bestCandidateAmbiguousWith != null; } } public IReadOnlyList InferredTypeArguments { get { if (bestCandidate != null && bestCandidate.InferredTypes != null) return bestCandidate.InferredTypes; else return EmptyList.Instance; } } /// /// Gets the implicit conversions that are being applied to the arguments. /// public IList ArgumentConversions { get { if (bestCandidate != null && bestCandidate.ArgumentConversions != null) return bestCandidate.ArgumentConversions; else return Enumerable.Repeat(Conversion.None, arguments.Length).ToList(); } } /// /// Gets an array that maps argument indices to parameter indices. /// For arguments that could not be mapped to any parameter, the value will be -1. /// /// parameterIndex = GetArgumentToParameterMap()[argumentIndex] /// public IReadOnlyList GetArgumentToParameterMap() { if (bestCandidate != null) return bestCandidate.ArgumentToParameterMap; else return null; } /// /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). /// Arguments are wrapped in a if an implicit conversion is being applied /// to them when calling the method. /// public IList GetArgumentsWithConversions() { if (bestCandidate == null) return arguments; else return GetArgumentsWithConversions(null, null); } /// /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). /// Arguments are wrapped in a if an implicit conversion is being applied /// to them when calling the method. /// For arguments where an explicit argument name was provided, the argument will /// be wrapped in a . /// public IList GetArgumentsWithConversionsAndNames() { if (bestCandidate == null) return arguments; else return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments()); } IList GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments) { var conversions = this.ArgumentConversions; ResolveResult[] args = new ResolveResult[arguments.Length]; for (int i = 0; i < args.Length; i++) { var argument = arguments[i]; if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null) argument = targetResolveResult; int parameterIndex = bestCandidate.ArgumentToParameterMap[i]; if (parameterIndex >= 0 && conversions[i] != Conversion.IdentityConversion) { // Wrap argument in ConversionResolveResult IType parameterType = bestCandidate.ParameterTypes[parameterIndex]; if (parameterType.Kind != TypeKind.Unknown) { if (arguments[i].IsCompileTimeConstant && conversions[i].IsValid && !conversions[i].IsUserDefined) { argument = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument); } else { argument = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow); } } } if (bestCandidateForNamedArguments != null && argumentNames[i] != null) { // Wrap argument in NamedArgumentResolveResult if (parameterIndex >= 0) { argument = new NamedArgumentResolveResult(bestCandidateForNamedArguments.Parameters[parameterIndex], argument, bestCandidateForNamedArguments); } else { argument = new NamedArgumentResolveResult(argumentNames[i], argument); } } args[i] = argument; } return args; } public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments() { if (bestCandidate == null) return null; IMethod method = bestCandidate.Member as IMethod; if (method != null && method.TypeParameters.Count > 0) { return ((IMethod)method.MemberDefinition).Specialize(GetSubstitution(bestCandidate)); } else { return bestCandidate.Member; } } TypeParameterSubstitution GetSubstitution(Candidate candidate) { // Do not compose the substitutions, but merge them. // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce return new TypeParameterSubstitution(candidate.Member.Substitution.ClassTypeArguments, candidate.InferredTypes); } /// /// Creates a ResolveResult representing the result of overload resolution. /// /// /// The target expression of the call. May be null for static methods/constructors. /// /// /// Statements for Objects/Collections initializer. /// /// /// /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList initializerStatements = null, IType returnTypeOverride = null) { IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments(); if (member == null) throw new InvalidOperationException(); return new CSharpInvocationResolveResult( this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType ?? SpecialType.UnknownType) : targetResolveResult, member, GetArgumentsWithConversions(targetResolveResult, member), this.BestCandidateErrors, this.IsExtensionMethodInvocation, this.BestCandidateIsExpandedForm, isDelegateInvocation: false, argumentToParameterMap: this.GetArgumentToParameterMap(), initializerStatements: initializerStatements, returnTypeOverride: returnTypeOverride); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolutionErrors.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Resolver { [Flags] public enum OverloadResolutionErrors { None = 0, /// /// Too many positional arguments (some could not be mapped to any parameter). /// TooManyPositionalArguments = 0x0001, /// /// A named argument could not be mapped to any parameter /// NoParameterFoundForNamedArgument = 0x0002, /// /// Type inference failed for a generic method. /// TypeInferenceFailed = 0x0004, /// /// Type arguments were explicitly specified, but did not match the number of type parameters. /// WrongNumberOfTypeArguments = 0x0008, /// /// After substituting type parameters with the inferred types; a constructed type within the formal parameters /// does not satisfy its constraint. /// ConstructedTypeDoesNotSatisfyConstraint = 0x0010, /// /// No argument was mapped to a non-optional parameter /// MissingArgumentForRequiredParameter = 0x0020, /// /// Several arguments were mapped to a single (non-params-array) parameter /// MultipleArgumentsForSingleParameter = 0x0040, /// /// 'ref'/'out' passing mode doesn't match for at least 1 parameter /// ParameterPassingModeMismatch = 0x0080, /// /// Argument type cannot be converted to parameter type /// ArgumentTypeMismatch = 0x0100, /// /// There is no unique best overload. /// This error does not apply to any single candidate, but only to the overall result of overload resolution. /// /// /// This error does not prevent a candidate from being applicable. /// AmbiguousMatch = 0x0200, /// /// The member is not accessible. /// /// /// This error is generated by member lookup; not by overload resolution. /// Inaccessible = 0x0400, /// /// A generic method /// /// /// This error does not prevent a candidate from being applicable. /// MethodConstraintsNotSatisfied = 0x0800, /// /// Using 'out var' instead of 'out T' would result in loss of type information. /// OutVarTypeMismatch = 0x1000, } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Resolver { public enum TypeInferenceAlgorithm { /// /// C# 4.0 type inference. /// CSharp4, /// /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing. /// Improved, /// /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing; /// uses to report all results (in case of ambiguities). /// ImprovedReturnAllResults } /// /// Implements C# 4.0 Type Inference (§7.5.2). /// public sealed class TypeInference { readonly ICompilation compilation; readonly CSharpConversions conversions; TypeInferenceAlgorithm algorithm = TypeInferenceAlgorithm.CSharp4; // determines the maximum generic nesting level; necessary to avoid infinite recursion in 'Improved' mode. const int maxNestingLevel = 5; int nestingLevel; #region Constructor public TypeInference(ICompilation compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.conversions = CSharpConversions.Get(compilation); } internal TypeInference(ICompilation compilation, CSharpConversions conversions) { Debug.Assert(compilation != null); Debug.Assert(conversions != null); this.compilation = compilation; this.conversions = conversions; } #endregion #region Properties /// /// Gets/Sets the type inference algorithm used. /// public TypeInferenceAlgorithm Algorithm { get { return algorithm; } set { algorithm = value; } } TypeInference CreateNestedInstance() { TypeInference c = new TypeInference(compilation, conversions); c.algorithm = algorithm; c.nestingLevel = nestingLevel + 1; return c; } #endregion TP[] typeParameters; IType[] parameterTypes; ResolveResult[] arguments; bool[,] dependencyMatrix; IReadOnlyList classTypeArguments; #region InferTypeArguments (main function) /// /// Performs type inference. /// /// The method type parameters that should be inferred. /// The arguments passed to the method. /// The parameter types of the method. /// Out: whether type inference was successful /// /// Class type arguments. These are substituted for class type parameters in the formal parameter types /// when inferring a method group or lambda. /// /// The inferred type arguments. public IType[] InferTypeArguments(IReadOnlyList typeParameters, IReadOnlyList arguments, IReadOnlyList parameterTypes, out bool success, IReadOnlyList classTypeArguments = null) { if (typeParameters == null) throw new ArgumentNullException(nameof(typeParameters)); if (arguments == null) throw new ArgumentNullException(nameof(arguments)); if (parameterTypes == null) throw new ArgumentNullException(nameof(parameterTypes)); try { this.typeParameters = new TP[typeParameters.Count]; for (int i = 0; i < this.typeParameters.Length; i++) { if (i != typeParameters[i].Index) throw new ArgumentException("Type parameter has wrong index"); if (typeParameters[i].OwnerType != SymbolKind.Method) throw new ArgumentException("Type parameter must be owned by a method"); this.typeParameters[i] = new TP(typeParameters[i]); } this.parameterTypes = new IType[Math.Min(arguments.Count, parameterTypes.Count)]; this.arguments = new ResolveResult[this.parameterTypes.Length]; for (int i = 0; i < this.parameterTypes.Length; i++) { if (arguments[i] == null || parameterTypes[i] == null) throw new ArgumentNullException(); this.arguments[i] = arguments[i]; this.parameterTypes[i] = parameterTypes[i]; } this.classTypeArguments = classTypeArguments; Log.WriteLine("Type Inference"); Log.WriteLine(" Signature: M<" + string.Join(", ", this.typeParameters) + ">" + "(" + string.Join(", ", this.parameterTypes) + ")"); Log.WriteCollection(" Arguments: ", arguments); Log.Indent(); PhaseOne(); success = PhaseTwo(); Log.Unindent(); Log.WriteLine(" Type inference finished " + (success ? "successfully" : "with errors") + ": " + "M<" + string.Join(", ", this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType)) + ">"); return this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType).ToArray(); } finally { Reset(); } } void Reset() { // clean up so that memory used by the operation can be garbage collected as soon as possible this.typeParameters = null; this.parameterTypes = null; this.arguments = null; this.dependencyMatrix = null; this.classTypeArguments = null; } /// /// Infers type arguments for the occurring in the /// so that the resulting type (after substition) satisfies the given bounds. /// public IType[] InferTypeArgumentsFromBounds(IReadOnlyList typeParameters, IType targetType, IEnumerable lowerBounds, IEnumerable upperBounds, out bool success) { if (typeParameters == null) throw new ArgumentNullException(nameof(typeParameters)); if (targetType == null) throw new ArgumentNullException(nameof(targetType)); if (lowerBounds == null) throw new ArgumentNullException(nameof(lowerBounds)); if (upperBounds == null) throw new ArgumentNullException(nameof(upperBounds)); this.typeParameters = new TP[typeParameters.Count]; for (int i = 0; i < this.typeParameters.Length; i++) { if (i != typeParameters[i].Index) throw new ArgumentException("Type parameter has wrong index"); this.typeParameters[i] = new TP(typeParameters[i]); } foreach (IType b in lowerBounds) { MakeLowerBoundInference(b, targetType); } foreach (IType b in upperBounds) { MakeUpperBoundInference(b, targetType); } IType[] result = new IType[this.typeParameters.Length]; success = true; for (int i = 0; i < result.Length; i++) { success &= Fix(this.typeParameters[i]); result[i] = this.typeParameters[i].FixedTo ?? SpecialType.UnknownType; } Reset(); return result; } #endregion sealed class TP { public readonly HashSet LowerBounds = new HashSet(); public readonly HashSet UpperBounds = new HashSet(); public IType ExactBound; public bool MultipleDifferentExactBounds; public readonly ITypeParameter TypeParameter; public IType FixedTo; public bool IsFixed { get { return FixedTo != null; } } public bool HasBounds { get { return LowerBounds.Count > 0 || UpperBounds.Count > 0 || ExactBound != null; } } public TP(ITypeParameter typeParameter) { if (typeParameter == null) throw new ArgumentNullException(nameof(typeParameter)); this.TypeParameter = typeParameter; } public void AddExactBound(IType type) { // Exact bounds need to stored separately, not just as Lower+Upper bounds, // due to TypeInferenceTests.GenericArgumentImplicitlyConvertibleToAndFromAnotherTypeList (see #281) if (ExactBound == null) ExactBound = type; else if (!ExactBound.Equals(type)) MultipleDifferentExactBounds = true; } public override string ToString() { return TypeParameter.Name; } } sealed class OccursInVisitor : TypeVisitor { readonly TP[] tp; public readonly bool[] Occurs; public OccursInVisitor(TypeInference typeInference) { this.tp = typeInference.typeParameters; this.Occurs = new bool[tp.Length]; } public override IType VisitTypeParameter(ITypeParameter type) { int index = type.Index; if (index < tp.Length && tp[index].TypeParameter == type) Occurs[index] = true; return base.VisitTypeParameter(type); } } #region Inference Phases void PhaseOne() { // C# 4.0 spec: §7.5.2.1 The first phase Log.WriteLine("Phase One"); for (int i = 0; i < arguments.Length; i++) { ResolveResult Ei = arguments[i]; IType Ti = parameterTypes[i]; LambdaResolveResult lrr = Ei as LambdaResolveResult; if (lrr != null) { MakeExplicitParameterTypeInference(lrr, Ti); } if (lrr != null || Ei is MethodGroupResolveResult) { // this is not in the spec??? if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { MakeOutputTypeInference(Ei, Ti); } } if (IsValidType(Ei.Type)) { if (Ti is ByReferenceType) { MakeExactInference(Ei.Type, Ti); } else { MakeLowerBoundInference(Ei.Type, Ti); } } } } static bool IsValidType(IType type) { return type.Kind != TypeKind.Unknown && type.Kind != TypeKind.Null && type.Kind != TypeKind.None; } bool PhaseTwo() { // C# 4.0 spec: §7.5.2.2 The second phase Log.WriteLine("Phase Two"); // All unfixed type variables Xi which do not depend on any Xj are fixed. List typeParametersToFix = new List(); foreach (TP Xi in typeParameters) { if (Xi.IsFixed == false) { if (!typeParameters.Any((TP Xj) => !Xj.IsFixed && DependsOn(Xi, Xj))) { typeParametersToFix.Add(Xi); } } } // If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold: if (typeParametersToFix.Count == 0) { Log.WriteLine("Type parameters cannot be fixed due to dependency cycles"); Log.WriteLine("Trying to break the cycle by fixing any TPs that have non-empty bounds..."); foreach (TP Xi in typeParameters) { // Xi has a non­empty set of bounds if (!Xi.IsFixed && Xi.HasBounds) { // There is at least one type variable Xj that depends on Xi if (typeParameters.Any((TP Xj) => DependsOn(Xj, Xi))) { typeParametersToFix.Add(Xi); } } } } // now fix 'em bool errorDuringFix = false; foreach (TP tp in typeParametersToFix) { if (!Fix(tp)) errorDuringFix = true; } if (errorDuringFix) return false; bool unfixedTypeVariablesExist = typeParameters.Any((TP X) => X.IsFixed == false); if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) { // If no such type variables exist and there are still unfixed type variables, type inference fails. Log.WriteLine("Type inference fails: there are still unfixed TPs remaining"); return false; } else if (!unfixedTypeVariablesExist) { // Otherwise, if no further unfixed type variables exist, type inference succeeds. return true; } else { // Otherwise, for all arguments ei with corresponding parameter type Ti for (int i = 0; i < arguments.Length; i++) { ResolveResult Ei = arguments[i]; IType Ti = parameterTypes[i]; // where the output types (§7.4.2.4) contain unfixed type variables Xj // but the input types (§7.4.2.3) do not if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { // an output type inference (§7.4.2.6) is made for ei with type Ti. Log.WriteLine("MakeOutputTypeInference for argument #" + i); MakeOutputTypeInference(Ei, Ti); } } // Then the second phase is repeated. return PhaseTwo(); } } #endregion #region Input Types / Output Types (§7.5.2.3 + §7.5.2.4) IType[] InputTypes(ResolveResult e, IType t) { // C# 4.0 spec: §7.5.2.3 Input types LambdaResolveResult lrr = e as LambdaResolveResult; if (lrr != null && lrr.IsImplicitlyTyped || e is MethodGroupResolveResult) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { IType[] inputTypes = new IType[m.Parameters.Count]; for (int i = 0; i < inputTypes.Length; i++) { inputTypes[i] = m.Parameters[i].Type; } return inputTypes; } } return Empty.Array; } IType[] OutputTypes(ResolveResult e, IType t) { // C# 4.0 spec: §7.5.2.4 Output types LambdaResolveResult lrr = e as LambdaResolveResult; if (lrr != null || e is MethodGroupResolveResult) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { return new[] { m.ReturnType }; } } return Empty.Array; } static IMethod GetDelegateOrExpressionTreeSignature(IType t) { if (t.TypeParameterCount == 1 && t.Name == "Expression" && t.Namespace == "System.Linq.Expressions") { t = t.TypeArguments[0]; } return t.GetDelegateInvokeMethod(); } bool InputTypesContainsUnfixed(ResolveResult argument, IType parameterType) { return AnyTypeContainsUnfixedParameter(InputTypes(argument, parameterType)); } bool OutputTypeContainsUnfixed(ResolveResult argument, IType parameterType) { return AnyTypeContainsUnfixedParameter(OutputTypes(argument, parameterType)); } bool AnyTypeContainsUnfixedParameter(IEnumerable types) { OccursInVisitor o = new OccursInVisitor(this); foreach (var type in types) { type.AcceptVisitor(o); } for (int i = 0; i < typeParameters.Length; i++) { if (!typeParameters[i].IsFixed && o.Occurs[i]) return true; } return false; } #endregion #region DependsOn (§7.5.2.5) // C# 4.0 spec: §7.5.2.5 Dependance void CalculateDependencyMatrix() { int n = typeParameters.Length; dependencyMatrix = new bool[n, n]; for (int k = 0; k < arguments.Length; k++) { OccursInVisitor input = new OccursInVisitor(this); OccursInVisitor output = new OccursInVisitor(this); foreach (var type in InputTypes(arguments[k], parameterTypes[k])) { type.AcceptVisitor(input); } foreach (var type in OutputTypes(arguments[k], parameterTypes[k])) { type.AcceptVisitor(output); } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { dependencyMatrix[i, j] |= input.Occurs[j] && output.Occurs[i]; } } } // calculate transitive closure using Warshall's algorithm: for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (dependencyMatrix[i, j]) { for (int k = 0; k < n; k++) { if (dependencyMatrix[j, k]) dependencyMatrix[i, k] = true; } } } } } bool DependsOn(TP x, TP y) { if (dependencyMatrix == null) CalculateDependencyMatrix(); // x depends on y return dependencyMatrix[x.TypeParameter.Index, y.TypeParameter.Index]; } #endregion #region MakeOutputTypeInference (§7.5.2.6) void MakeOutputTypeInference(ResolveResult e, IType t) { Log.WriteLine(" MakeOutputTypeInference from " + e + " to " + t); // If E is an anonymous function with inferred return type U (§7.5.2.12) and T is a delegate type or expression // tree type with return type Tb, then a lower-bound inference (§7.5.2.9) is made from U to Tb. LambdaResolveResult lrr = e as LambdaResolveResult; if (lrr != null) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { IType inferredReturnType; if (lrr.IsImplicitlyTyped) { if (m.Parameters.Count != lrr.Parameters.Count) return; // cannot infer due to mismatched parameter lists TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); IType[] inferredParameterTypes = new IType[m.Parameters.Count]; for (int i = 0; i < inferredParameterTypes.Length; i++) { IType parameterType = m.Parameters[i].Type; inferredParameterTypes[i] = parameterType.AcceptVisitor(substitution); } inferredReturnType = lrr.GetInferredReturnType(inferredParameterTypes); } else { inferredReturnType = lrr.GetInferredReturnType(null); } MakeLowerBoundInference(inferredReturnType, m.ReturnType); return; } } // Otherwise, if E is a method group and T is a delegate type or expression tree type // with parameter types T1…Tk and return type Tb, and overload resolution // of E with the types T1…Tk yields a single method with return type U, then a lower­-bound // inference is made from U to Tb. MethodGroupResolveResult mgrr = e as MethodGroupResolveResult; if (mgrr != null) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { ResolveResult[] args = new ResolveResult[m.Parameters.Count]; TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); for (int i = 0; i < args.Length; i++) { IParameter param = m.Parameters[i]; IType parameterType = param.Type.AcceptVisitor(substitution); if ((param.ReferenceKind != ReferenceKind.None) && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else { args[i] = new ResolveResult(parameterType); } } var or = mgrr.PerformOverloadResolution( compilation, args, allowExpandingParams: false, allowOptionalParameters: false, allowImplicitIn: false ); if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; MakeLowerBoundInference(returnType, m.ReturnType); } } return; } // Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T. if (IsValidType(e.Type)) { MakeLowerBoundInference(e.Type, t); } } TypeParameterSubstitution GetSubstitutionForFixedTPs() { IType[] fixedTypes = new IType[typeParameters.Length]; for (int i = 0; i < fixedTypes.Length; i++) { fixedTypes[i] = typeParameters[i].FixedTo ?? SpecialType.UnknownType; } return new TypeParameterSubstitution(classTypeArguments, fixedTypes); } #endregion #region MakeExplicitParameterTypeInference (§7.5.2.7) void MakeExplicitParameterTypeInference(LambdaResolveResult e, IType t) { // C# 4.0 spec: §7.5.2.7 Explicit parameter type inferences if (e.IsImplicitlyTyped || !e.HasParameterList) return; Log.WriteLine(" MakeExplicitParameterTypeInference from " + e + " to " + t); IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m == null) return; for (int i = 0; i < e.Parameters.Count && i < m.Parameters.Count; i++) { MakeExactInference(e.Parameters[i].Type, m.Parameters[i].Type); } } #endregion #region MakeExactInference (§7.5.2.8) /// /// Make exact inference from U to V. /// C# 4.0 spec: §7.5.2.8 Exact inferences /// void MakeExactInference(IType U, IType V) { Log.WriteLine("MakeExactInference from " + U + " to " + V); if (U.Nullability == V.Nullability) { U = U.WithoutNullability(); V = V.WithoutNullability(); } // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { Log.WriteLine(" Add exact bound '" + U + "' to " + tp); tp.AddExactBound(U); return; } // Handle by reference types: ByReferenceType brU = U as ByReferenceType; ByReferenceType brV = V as ByReferenceType; if (brU != null && brV != null) { MakeExactInference(brU.ElementType, brV.ElementType); return; } // Handle array types: U = U.TupleUnderlyingTypeOrSelf(); V = V.TupleUnderlyingTypeOrSelf(); switch ((U, V)) { case (ArrayType arrU, ArrayType arrV) when arrU.Dimensions == arrV.Dimensions: MakeExactInference(arrU.ElementType, arrV.ElementType); return; case (ArrayType arrU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanV.IsKnownType(KnownTypeCode.SpanOfT): MakeExactInference(arrU.ElementType, spanV.TypeArguments[0]); return; case (ParameterizedType spanU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && spanV.IsKnownType(KnownTypeCode.SpanOfT): MakeExactInference(spanU.TypeArguments[0], spanV.TypeArguments[0]); return; case (ArrayType arrU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeExactInference(arrU.ElementType, rosV.TypeArguments[0]); return; case (ParameterizedType spanU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeExactInference(spanU.TypeArguments[0], rosV.TypeArguments[0]); return; case (ParameterizedType rosU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosU.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeExactInference(rosU.TypeArguments[0], rosV.TypeArguments[0]); return; } // Handle parameterized type: if (U is ParameterizedType pU && V is ParameterizedType pV && object.Equals(pU.GenericType, pV.GenericType) && pU.TypeParameterCount == pV.TypeParameterCount) { Log.Indent(); for (int i = 0; i < pU.TypeParameterCount; i++) { MakeExactInference(pU.GetTypeArgument(i), pV.GetTypeArgument(i)); } Log.Unindent(); return; } // Handle pointer types: if (U is PointerType ptrU && V is PointerType ptrV) { MakeExactInference(ptrU.ElementType, ptrV.ElementType); return; } if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV) { MakeExactInference(fnPtrU.ReturnType, fnPtrV.ReturnType); foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes)) { MakeExactInference(ptU, ptV); } return; } } TP GetTPForType(IType v) { if (v is NullabilityAnnotatedTypeParameter natp) { v = natp.OriginalTypeParameter; } if (v is ITypeParameter p) { int index = p.Index; if (index < typeParameters.Length && typeParameters[index].TypeParameter == p) return typeParameters[index]; } return null; } #endregion #region MakeLowerBoundInference (§7.5.2.9) /// /// Make lower bound inference from U to V. /// C# 4.0 spec: §7.5.2.9 Lower-bound inferences /// void MakeLowerBoundInference(IType U, IType V) { Log.WriteLine(" MakeLowerBoundInference from " + U + " to " + V); if (U.Nullability == V.Nullability) { U = U.WithoutNullability(); V = V.WithoutNullability(); } // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { Log.WriteLine(" Add lower bound '" + U + "' to " + tp); tp.LowerBounds.Add(U); return; } // Handle nullable covariance: if (NullableType.IsNullable(U) && NullableType.IsNullable(V)) { MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V)); return; } // Handle by reference types: ByReferenceType brU = U as ByReferenceType; ByReferenceType brV = V as ByReferenceType; if (brU != null && brV != null) { MakeExactInference(brU.ElementType, brV.ElementType); return; } // Handle array types: V = V.TupleUnderlyingTypeOrSelf(); switch ((U, V)) { case (ArrayType arrU, ArrayType arrV) when arrU.Dimensions == arrV.Dimensions: MakeLowerBoundInference(arrU.ElementType, arrV.ElementType); return; case (ArrayType arrU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanV.IsKnownType(KnownTypeCode.SpanOfT): MakeLowerBoundInference(arrU.ElementType, spanV.TypeArguments[0]); return; case (ParameterizedType spanU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && spanV.IsKnownType(KnownTypeCode.SpanOfT): MakeLowerBoundInference(spanU.TypeArguments[0], spanV.TypeArguments[0]); return; case (ArrayType arrU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeLowerBoundInference(arrU.ElementType, rosV.TypeArguments[0]); return; case (ParameterizedType spanU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeLowerBoundInference(spanU.TypeArguments[0], rosV.TypeArguments[0]); return; case (ParameterizedType rosU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosU.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT): MakeLowerBoundInference(rosU.TypeArguments[0], rosV.TypeArguments[0]); return; case (ArrayType arrU, ParameterizedType arrIntfV) when arrIntfV.IsArrayInterfaceType() && arrU.Dimensions == 1: MakeLowerBoundInference(arrU.ElementType, arrIntfV.TypeArguments[0]); return; } // Handle parameterized types: if (V is ParameterizedType pV) { ParameterizedType uniqueBaseType = null; foreach (IType baseU in U.GetAllBaseTypes()) { ParameterizedType pU = baseU.TupleUnderlyingTypeOrSelf() as ParameterizedType; if (pU != null && object.Equals(pU.GenericType, pV.GenericType) && pU.TypeParameterCount == pV.TypeParameterCount) { if (uniqueBaseType == null) uniqueBaseType = pU; else return; // cannot make an inference because it's not unique } } Log.Indent(); if (uniqueBaseType != null) { for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { IType Ui = uniqueBaseType.GetTypeArgument(i); IType Vi = pV.GetTypeArgument(i); if (Ui.IsReferenceType == true) { // look for variance ITypeParameter Xi = pV.TypeParameters[i]; switch (Xi.Variance) { case VarianceModifier.Covariant: MakeLowerBoundInference(Ui, Vi); break; case VarianceModifier.Contravariant: MakeUpperBoundInference(Ui, Vi); break; default: // invariant MakeExactInference(Ui, Vi); break; } } else { // not known to be a reference type MakeExactInference(Ui, Vi); } } } Log.Unindent(); return; } // Handle pointer types: if (U is PointerType ptrU && V is PointerType ptrV) { MakeExactInference(ptrU.ElementType, ptrV.ElementType); return; } if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV) { MakeLowerBoundInference(fnPtrU.ReturnType, fnPtrV.ReturnType); foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes)) { MakeUpperBoundInference(ptU, ptV); } return; } } #endregion #region MakeUpperBoundInference (§7.5.2.10) /// /// Make upper bound inference from U to V. /// C# 4.0 spec: §7.5.2.10 Upper-bound inferences /// void MakeUpperBoundInference(IType U, IType V) { Log.WriteLine(" MakeUpperBoundInference from " + U + " to " + V); if (U.Nullability == V.Nullability) { U = U.WithoutNullability(); V = V.WithoutNullability(); } // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { Log.WriteLine(" Add upper bound '" + U + "' to " + tp); tp.UpperBounds.Add(U); return; } // Handle array types: ArrayType arrU = U as ArrayType; ArrayType arrV = V as ArrayType; ParameterizedType pU = U.TupleUnderlyingTypeOrSelf() as ParameterizedType; if (arrV != null && arrU != null && arrU.Dimensions == arrV.Dimensions) { MakeUpperBoundInference(arrU.ElementType, arrV.ElementType); return; } else if (arrV != null && pU.IsArrayInterfaceType() && arrV.Dimensions == 1) { MakeUpperBoundInference(pU.GetTypeArgument(0), arrV.ElementType); return; } // Handle parameterized types: if (pU != null) { ParameterizedType uniqueBaseType = null; foreach (IType baseV in V.GetAllBaseTypes()) { ParameterizedType pV = baseV.TupleUnderlyingTypeOrSelf() as ParameterizedType; if (pV != null && object.Equals(pU.GenericType, pV.GenericType) && pU.TypeParameterCount == pV.TypeParameterCount) { if (uniqueBaseType == null) uniqueBaseType = pV; else return; // cannot make an inference because it's not unique } } Log.Indent(); if (uniqueBaseType != null) { for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { IType Ui = pU.GetTypeArgument(i); IType Vi = uniqueBaseType.GetTypeArgument(i); if (Ui.IsReferenceType == true) { // look for variance ITypeParameter Xi = pU.TypeParameters[i]; switch (Xi.Variance) { case VarianceModifier.Covariant: MakeUpperBoundInference(Ui, Vi); break; case VarianceModifier.Contravariant: MakeLowerBoundInference(Ui, Vi); break; default: // invariant MakeExactInference(Ui, Vi); break; } } else { // not known to be a reference type MakeExactInference(Ui, Vi); } } } Log.Unindent(); return; } // Handle pointer types: if (U is PointerType ptrU && V is PointerType ptrV) { MakeExactInference(ptrU.ElementType, ptrV.ElementType); return; } if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV) { MakeUpperBoundInference(fnPtrU.ReturnType, fnPtrV.ReturnType); foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes)) { MakeLowerBoundInference(ptU, ptV); } return; } } #endregion #region Fixing (§7.5.2.11) bool Fix(TP tp) { Log.WriteLine(" Trying to fix " + tp); Debug.Assert(!tp.IsFixed); if (tp.ExactBound != null) { // the exact bound will always be the result tp.FixedTo = tp.ExactBound; // check validity if (tp.MultipleDifferentExactBounds) return false; return tp.LowerBounds.All(b => conversions.ImplicitConversion(b, tp.FixedTo).IsValid) && tp.UpperBounds.All(b => conversions.ImplicitConversion(tp.FixedTo, b).IsValid); } Log.Indent(); var types = CreateNestedInstance().FindTypesInBounds(tp.LowerBounds.ToArray(), tp.UpperBounds.ToArray()); Log.Unindent(); if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { tp.FixedTo = IntersectionType.Create(types); Log.WriteLine(" T was fixed " + (types.Count >= 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); return types.Count >= 1; } else { tp.FixedTo = GetFirstTypePreferNonInterfaces(types); Log.WriteLine(" T was fixed " + (types.Count == 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); return types.Count == 1; } } #endregion #region Finding the best common type of a set of expresssions /// /// Gets the best common type (C# 4.0 spec: §7.5.2.14) of a set of expressions. /// public IType GetBestCommonType(IList expressions, out bool success) { if (expressions == null) throw new ArgumentNullException(nameof(expressions)); if (expressions.Count == 1) { success = IsValidType(expressions[0].Type); return expressions[0].Type; } Log.WriteCollection("GetBestCommonType() for ", expressions); try { ITypeParameter tp = DummyTypeParameter.GetMethodTypeParameter(0); this.typeParameters = new TP[1] { new TP(tp) }; foreach (ResolveResult r in expressions) { MakeOutputTypeInference(r, tp); } success = Fix(typeParameters[0]); return typeParameters[0].FixedTo ?? SpecialType.UnknownType; } finally { Reset(); } } #endregion #region FindTypeInBounds /// /// Finds a type that satisfies the given lower and upper bounds. /// public IType FindTypeInBounds(IReadOnlyList lowerBounds, IReadOnlyList upperBounds) { if (lowerBounds == null) throw new ArgumentNullException(nameof(lowerBounds)); if (upperBounds == null) throw new ArgumentNullException(nameof(upperBounds)); var result = FindTypesInBounds(lowerBounds, upperBounds); if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { return IntersectionType.Create(result); } else { // return any of the candidates (prefer non-interfaces) return GetFirstTypePreferNonInterfaces(result); } } static IType GetFirstTypePreferNonInterfaces(IReadOnlyList result) { return result.FirstOrDefault(c => c.Kind != TypeKind.Interface) ?? result.FirstOrDefault() ?? SpecialType.UnknownType; } IReadOnlyList FindTypesInBounds(IReadOnlyList lowerBounds, IReadOnlyList upperBounds) { // If there's only a single type; return that single type. // If both inputs are empty, return the empty list. if (lowerBounds.Count == 0 && upperBounds.Count <= 1) return upperBounds; if (upperBounds.Count == 0 && lowerBounds.Count <= 1) return lowerBounds; if (nestingLevel > maxNestingLevel) return EmptyList.Instance; // Finds a type X so that "LB <: X <: UB" Log.WriteCollection("FindTypesInBound, LowerBounds=", lowerBounds); Log.WriteCollection("FindTypesInBound, UpperBounds=", upperBounds); // First try the Fixing algorithm from the C# spec (§7.5.2.11) List candidateTypes = lowerBounds.Union(upperBounds) .Where(c => lowerBounds.All(b => conversions.ImplicitConversion(b, c).IsValid)) .Where(c => upperBounds.All(b => conversions.ImplicitConversion(c, b).IsValid)) .ToList(); // evaluate the query only once Log.WriteCollection("FindTypesInBound, Candidates=", candidateTypes); // According to the C# specification, we need to pick the most specific // of the candidate types. (the type which has conversions to all others) // However, csc actually seems to choose the least specific. candidateTypes = candidateTypes.Where( c => candidateTypes.All(o => conversions.ImplicitConversion(o, c).IsValid) ).ToList(); // If the specified algorithm produces a single candidate, we return // that candidate. // We also return the whole candidate list if we're not using the improved // algorithm. if (candidateTypes.Count == 1 || !(algorithm == TypeInferenceAlgorithm.Improved || algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults)) { return candidateTypes; } candidateTypes.Clear(); // Now try the improved algorithm Log.Indent(); List candidateTypeDefinitions; if (lowerBounds.Count > 0) { // Find candidates by using the lower bounds: var hashSet = new HashSet(lowerBounds[0].GetAllBaseTypeDefinitions()); for (int i = 1; i < lowerBounds.Count; i++) { hashSet.IntersectWith(lowerBounds[i].GetAllBaseTypeDefinitions()); } candidateTypeDefinitions = hashSet.ToList(); } else { // Find candidates by looking at all classes in the project: candidateTypeDefinitions = compilation.GetAllTypeDefinitions().ToList(); } // Now filter out candidates that violate the upper bounds: foreach (IType ub in upperBounds) { ITypeDefinition ubDef = ub.GetDefinition(); if (ubDef != null) { candidateTypeDefinitions.RemoveAll(c => !c.IsDerivedFrom(ubDef)); } } foreach (ITypeDefinition candidateDef in candidateTypeDefinitions) { // determine the type parameters for the candidate: IType candidate; if (candidateDef.TypeParameterCount == 0) { candidate = candidateDef; } else { Log.WriteLine("Inferring arguments for candidate type definition: " + candidateDef); bool success; IType[] result = InferTypeArgumentsFromBounds( candidateDef.TypeParameters, new ParameterizedType(candidateDef, candidateDef.TypeParameters), lowerBounds, upperBounds, out success); if (success) { candidate = new ParameterizedType(candidateDef, result); } else { Log.WriteLine("Inference failed; ignoring candidate"); continue; } } Log.WriteLine("Candidate type: " + candidate); if (upperBounds.Count == 0) { // if there were only lower bounds, we aim for the most specific candidate: // if this candidate isn't made redundant by an existing, more specific candidate: if (!candidateTypes.Any(c => c.GetDefinition().IsDerivedFrom(candidateDef))) { // remove all existing candidates made redundant by this candidate: candidateTypes.RemoveAll(c => candidateDef.IsDerivedFrom(c.GetDefinition())); // add new candidate candidateTypes.Add(candidate); } } else { // if there were upper bounds, we aim for the least specific candidate: // if this candidate isn't made redundant by an existing, less specific candidate: if (!candidateTypes.Any(c => candidateDef.IsDerivedFrom(c.GetDefinition()))) { // remove all existing candidates made redundant by this candidate: candidateTypes.RemoveAll(c => c.GetDefinition().IsDerivedFrom(candidateDef)); // add new candidate candidateTypes.Add(candidate); } } } Log.Unindent(); return candidateTypes; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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 ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { /// /// Given a SyntaxTree that was output from the decompiler, constructs the list of sequence points. /// // Each statement / expression AST node is annotated with the ILInstruction(s) it was constructed from. // Each ILInstruction has a list of IL offsets corresponding to the original IL range(s). Note that the ILAst // instructions form a tree. // // This visitor constructs a list of sequence points from the syntax tree by visiting each node, // calling // 1. StartSequencePoint(AstNode) // 2. AddToSequencePoint(AstNode) (possibly multiple times) // 3. EndSequencePoint(TextLocation, TextLocation) // on each node. // // The VisitAsSequencePoint(AstNode) method encapsulates the steps above. // // The state we record for each sequence point is decribed in StatePerSequencePoint: // 1. primary AST node // 2. IL range intervals // 3. parent ILFunction (either a method or lambda) // // For each statement (at least) one sequence point is created and all expressions and their IL ranges // are added to it. Currently the debugger seems not to support breakpoints at an expression level, so // we stop at the statement level and add all sub-expressions to the same sequence point. // // LambdaExpression is one exception: we create new sequence points for the expression/statements of the lambda, // note however, that these are added to a different ILFunction. // // AddToSequencePoint(AstNode) handles the list of ILInstructions and visits each ILInstruction and its descendants. // We do not descend into nested ILFunctions as these create their own list of sequence points. class SequencePointBuilder : DepthFirstAstVisitor { struct StatePerSequencePoint { /// /// Main AST node associated with this sequence point. /// internal readonly AstNode PrimaryNode; /// /// List of IL intervals that are associated with this sequence point. /// internal readonly List Intervals; /// /// The function containing this sequence point. /// internal ILFunction Function; public StatePerSequencePoint(AstNode primaryNode) { this.PrimaryNode = primaryNode; this.Intervals = new List(); this.Function = null; } } readonly List<(ILFunction, DebugInfo.SequencePoint)> sequencePoints = new List<(ILFunction, DebugInfo.SequencePoint)>(); readonly HashSet mappedInstructions = new HashSet(); // Stack holding information for outer statements. readonly Stack outerStates = new Stack(); // Collects information for the current sequence point. StatePerSequencePoint current; void VisitAsSequencePoint(AstNode node) { if (node.IsNull) return; StartSequencePoint(node); node.AcceptVisitor(this); EndSequencePoint(node.StartLocation, node.EndLocation); } protected override void VisitChildren(AstNode node) { base.VisitChildren(node); AddToSequencePoint(node); } public override void VisitBlockStatement(BlockStatement blockStatement) { // enhanced using variables need special-casing here, because we omit the block syntax from the // text output, so we cannot use positions of opening/closing braces here. bool isEnhancedUsing = blockStatement.Parent is UsingStatement us && us.IsEnhanced; if (!isEnhancedUsing) { var blockContainer = blockStatement.Annotation(); if (blockContainer != null) { StartSequencePoint(blockStatement.LBraceToken); int intervalStart; if (blockContainer.Parent is TryCatchHandler handler && !handler.ExceptionSpecifierILRange.IsEmpty) { // if this block container is part of a TryCatchHandler, do not steal the // exception-specifier IL range intervalStart = handler.ExceptionSpecifierILRange.End; } else { intervalStart = blockContainer.StartILOffset; } // The end will be set to the first sequence point candidate location before the first // statement of the function when the seqeunce point is adjusted int intervalEnd = intervalStart + 1; Interval interval = new Interval(intervalStart, intervalEnd); List intervals = new List(); intervals.Add(interval); current.Intervals.AddRange(intervals); current.Function = blockContainer.Ancestors.OfType().FirstOrDefault(); EndSequencePoint(blockStatement.LBraceToken.StartLocation, blockStatement.LBraceToken.EndLocation); } else { // Ideally, we'd be able to address this case. Blocks that are not the top-level function // block have no ILInstruction annotations. It isn't clear to me how to determine the il range. // For now, do not add the opening brace sequence in this case. } } foreach (var stmt in blockStatement.Statements) { VisitAsSequencePoint(stmt); } var implicitReturn = blockStatement.Annotation(); if (implicitReturn != null && !isEnhancedUsing) { StartSequencePoint(blockStatement.RBraceToken); AddToSequencePoint(implicitReturn.Leave); EndSequencePoint(blockStatement.RBraceToken.StartLocation, blockStatement.RBraceToken.EndLocation); } } public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (!propertyDeclaration.ExpressionBody.IsNull) { VisitAsSequencePoint(propertyDeclaration.ExpressionBody); } else { base.VisitPropertyDeclaration(propertyDeclaration); } } public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { if (!indexerDeclaration.ExpressionBody.IsNull) { VisitAsSequencePoint(indexerDeclaration.ExpressionBody); } else { base.VisitIndexerDeclaration(indexerDeclaration); } } public override void VisitForStatement(ForStatement forStatement) { // Every element of a for-statement is its own sequence point. foreach (var init in forStatement.Initializers) { VisitAsSequencePoint(init); } VisitAsSequencePoint(forStatement.Condition); foreach (var inc in forStatement.Iterators) { VisitAsSequencePoint(inc); } VisitAsSequencePoint(forStatement.EmbeddedStatement); } public override void VisitSwitchStatement(SwitchStatement switchStatement) { StartSequencePoint(switchStatement); switchStatement.Expression.AcceptVisitor(this); foreach (var section in switchStatement.SwitchSections) { // note: sections will not contribute to the current sequence point section.AcceptVisitor(this); } // add switch statement itself to sequence point // (call only after the sections are visited) AddToSequencePoint(switchStatement); EndSequencePoint(switchStatement.StartLocation, switchStatement.RParToken.EndLocation); } public override void VisitSwitchSection(Syntax.SwitchSection switchSection) { // every statement in the switch section is its own sequence point foreach (var stmt in switchSection.Statements) { VisitAsSequencePoint(stmt); } } public override void VisitLambdaExpression(LambdaExpression lambdaExpression) { AddToSequencePoint(lambdaExpression); VisitAsSequencePoint(lambdaExpression.Body); } public override void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { AddToSequencePoint(queryContinuationClause); VisitAsSequencePoint(queryContinuationClause.PrecedingQuery); } public override void VisitQueryFromClause(QueryFromClause queryFromClause) { if (queryFromClause.Parent.FirstChild != queryFromClause) { AddToSequencePoint(queryFromClause); VisitAsSequencePoint(queryFromClause.Expression); } else { base.VisitQueryFromClause(queryFromClause); } } public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { AddToSequencePoint(queryGroupClause); VisitAsSequencePoint(queryGroupClause.Projection); VisitAsSequencePoint(queryGroupClause.Key); } public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { AddToSequencePoint(queryJoinClause); VisitAsSequencePoint(queryJoinClause.OnExpression); VisitAsSequencePoint(queryJoinClause.EqualsExpression); } public override void VisitQueryLetClause(QueryLetClause queryLetClause) { AddToSequencePoint(queryLetClause); VisitAsSequencePoint(queryLetClause.Expression); } public override void VisitQueryOrdering(QueryOrdering queryOrdering) { AddToSequencePoint(queryOrdering); VisitAsSequencePoint(queryOrdering.Expression); } public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) { AddToSequencePoint(querySelectClause); VisitAsSequencePoint(querySelectClause.Expression); } public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { AddToSequencePoint(queryWhereClause); VisitAsSequencePoint(queryWhereClause.Condition); } public override void VisitUsingStatement(UsingStatement usingStatement) { StartSequencePoint(usingStatement); usingStatement.ResourceAcquisition.AcceptVisitor(this); VisitAsSequencePoint(usingStatement.EmbeddedStatement); AddToSequencePoint(usingStatement); if (usingStatement.IsEnhanced) EndSequencePoint(usingStatement.StartLocation, usingStatement.ResourceAcquisition.EndLocation); else EndSequencePoint(usingStatement.StartLocation, usingStatement.RParToken.EndLocation); } public override void VisitForeachStatement(ForeachStatement foreachStatement) { var foreachInfo = foreachStatement.Annotation(); if (foreachInfo == null) { base.VisitForeachStatement(foreachStatement); return; } // TODO : Add a sequence point on foreach token (mapped to nop before using instruction). StartSequencePoint(foreachStatement); foreachStatement.InExpression.AcceptVisitor(this); AddToSequencePoint(foreachInfo.GetEnumeratorCall); EndSequencePoint(foreachStatement.InExpression.StartLocation, foreachStatement.InExpression.EndLocation); StartSequencePoint(foreachStatement); AddToSequencePoint(foreachInfo.MoveNextCall); EndSequencePoint(foreachStatement.InToken.StartLocation, foreachStatement.InToken.EndLocation); StartSequencePoint(foreachStatement); AddToSequencePoint(foreachInfo.GetCurrentCall); EndSequencePoint(foreachStatement.VariableType.StartLocation, foreachStatement.VariableDesignation.EndLocation); VisitAsSequencePoint(foreachStatement.EmbeddedStatement); } public override void VisitLockStatement(LockStatement lockStatement) { StartSequencePoint(lockStatement); lockStatement.Expression.AcceptVisitor(this); VisitAsSequencePoint(lockStatement.EmbeddedStatement); AddToSequencePoint(lockStatement); EndSequencePoint(lockStatement.StartLocation, lockStatement.RParToken.EndLocation); } public override void VisitIfElseStatement(IfElseStatement ifElseStatement) { StartSequencePoint(ifElseStatement); ifElseStatement.Condition.AcceptVisitor(this); VisitAsSequencePoint(ifElseStatement.TrueStatement); VisitAsSequencePoint(ifElseStatement.FalseStatement); AddToSequencePoint(ifElseStatement); EndSequencePoint(ifElseStatement.StartLocation, ifElseStatement.RParToken.EndLocation); } public override void VisitWhileStatement(WhileStatement whileStatement) { StartSequencePoint(whileStatement); whileStatement.Condition.AcceptVisitor(this); VisitAsSequencePoint(whileStatement.EmbeddedStatement); AddToSequencePoint(whileStatement); EndSequencePoint(whileStatement.StartLocation, whileStatement.RParToken.EndLocation); } public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { StartSequencePoint(doWhileStatement); VisitAsSequencePoint(doWhileStatement.EmbeddedStatement); doWhileStatement.Condition.AcceptVisitor(this); AddToSequencePoint(doWhileStatement); EndSequencePoint(doWhileStatement.WhileToken.StartLocation, doWhileStatement.RParToken.EndLocation); } public override void VisitFixedStatement(FixedStatement fixedStatement) { foreach (var v in fixedStatement.Variables) { VisitAsSequencePoint(v); } VisitAsSequencePoint(fixedStatement.EmbeddedStatement); } public override void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { VisitAsSequencePoint(tryCatchStatement.TryBlock); foreach (var c in tryCatchStatement.CatchClauses) { VisitAsSequencePoint(c); } VisitAsSequencePoint(tryCatchStatement.FinallyBlock); } public override void VisitCatchClause(CatchClause catchClause) { if (catchClause.Condition.IsNull) { var tryCatchHandler = catchClause.Annotation(); if (tryCatchHandler != null && !tryCatchHandler.ExceptionSpecifierILRange.IsEmpty) { StartSequencePoint(catchClause.CatchToken); var function = tryCatchHandler.Ancestors.OfType().FirstOrDefault(); AddToSequencePointRaw(function, new[] { tryCatchHandler.ExceptionSpecifierILRange }); EndSequencePoint(catchClause.CatchToken.StartLocation, catchClause.RParToken.IsNull ? catchClause.CatchToken.EndLocation : catchClause.RParToken.EndLocation); } } else { StartSequencePoint(catchClause.WhenToken); AddToSequencePoint(catchClause.Condition); EndSequencePoint(catchClause.WhenToken.StartLocation, catchClause.CondRParToken.EndLocation); } VisitAsSequencePoint(catchClause.Body); } /// /// Start a new C# statement = new sequence point. /// void StartSequencePoint(AstNode primaryNode) { outerStates.Push(current); current = new StatePerSequencePoint(primaryNode); } void EndSequencePoint(TextLocation startLocation, TextLocation endLocation) { Debug.Assert(!startLocation.IsEmpty, "missing startLocation"); Debug.Assert(!endLocation.IsEmpty, "missing endLocation"); if (current.Intervals.Count > 0 && current.Function != null) { // use LongSet to deduplicate and merge the intervals var longSet = new LongSet(current.Intervals.Select(i => new LongInterval(i.Start, i.End))); Debug.Assert(!longSet.IsEmpty); sequencePoints.Add((current.Function, new DebugInfo.SequencePoint { Offset = (int)longSet.Intervals[0].Start, EndOffset = (int)longSet.Intervals[0].End, StartLine = startLocation.Line, StartColumn = startLocation.Column, EndLine = endLocation.Line, EndColumn = endLocation.Column })); } current = outerStates.Pop(); } void AddToSequencePointRaw(ILFunction function, IEnumerable ranges) { current.Intervals.AddRange(ranges); Debug.Assert(current.Function == null || current.Function == function); current.Function = function; } /// /// Add the ILAst instruction associated with the AstNode to the sequence point. /// Also add all its ILAst sub-instructions (unless they were already added to another sequence point). /// void AddToSequencePoint(AstNode node) { foreach (var inst in node.Annotations.OfType()) { AddToSequencePoint(inst); } } void AddToSequencePoint(ILInstruction inst) { if (!mappedInstructions.Add(inst)) { // inst was already used by a nested sequence point within this sequence point return; } // Add the IL range associated with this instruction to the current sequence point. if (HasUsableILRange(inst) && current.Intervals != null) { current.Intervals.AddRange(inst.ILRanges); var function = inst.Parent.Ancestors.OfType().FirstOrDefault(); Debug.Assert(current.Function == null || current.Function == function); current.Function = function; } // Do not add instructions of lambdas/delegates. if (inst is ILFunction) return; // Also add the child IL instructions, unless they were already processed by // another C# expression. foreach (var child in inst.Children) { AddToSequencePoint(child); } } internal static bool HasUsableILRange(ILInstruction inst) { if (inst.ILRangeIsEmpty) return false; if (inst.Parent == null) return false; return !(inst is BlockContainer || inst is Block); } /// /// Called after the visitor is done to return the results. /// internal Dictionary> GetSequencePoints() { var dict = new Dictionary>(); foreach (var (function, sequencePoint) in this.sequencePoints) { if (!dict.TryGetValue(function, out var list)) { dict.Add(function, list = new List()); } list.Add(sequencePoint); } foreach (var (function, list) in dict.ToList()) { // For each function, sort sequence points and fix overlaps var newList = new List(); int pos = 0; IOrderedEnumerable currFunctionSequencePoints = list.OrderBy(sp => sp.Offset).ThenBy(sp => sp.EndOffset); foreach (DebugInfo.SequencePoint sequencePoint in currFunctionSequencePoints) { if (sequencePoint.Offset < pos) { // overlapping sequence point? // delete previous sequence points that are after sequencePoint.Offset while (newList.Count > 0 && newList.Last().EndOffset > sequencePoint.Offset) { var last = newList.Last(); if (last.Offset >= sequencePoint.Offset) { newList.RemoveAt(newList.Count - 1); } else { last.EndOffset = sequencePoint.Offset; newList[newList.Count - 1] = last; } } } newList.Add(sequencePoint); pos = sequencePoint.EndOffset; } // Add a hidden sequence point to account for the epilog of the function if (pos < function.CodeSize) { var hidden = new DebugInfo.SequencePoint(); hidden.Offset = pos; hidden.EndOffset = function.CodeSize; hidden.SetHidden(); newList.Add(hidden); } List sequencePointCandidates = function.SequencePointCandidates; int currSPCandidateIndex = 0; for (int i = 0; i < newList.Count - 1; i++) { DebugInfo.SequencePoint currSequencePoint = newList[i]; DebugInfo.SequencePoint nextSequencePoint = newList[i + 1]; // Adjust the end offset of the current sequence point to the closest sequence point candidate // but do not create an overlapping sequence point. Moving the start of the current sequence // point is not required as it is 0 for the first sequence point and is moved during the last // iteration for all others. while (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex] < currSequencePoint.EndOffset) { currSPCandidateIndex++; } if (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex] <= nextSequencePoint.Offset) { currSequencePoint.EndOffset = sequencePointCandidates[currSPCandidateIndex]; } // Adjust the start offset of the next sequence point to the closest previous sequence point candidate // but do not create an overlapping sequence point. while (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex] < nextSequencePoint.Offset) { currSPCandidateIndex++; } if (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex - 1] >= currSequencePoint.EndOffset) { nextSequencePoint.Offset = sequencePointCandidates[currSPCandidateIndex - 1]; currSPCandidateIndex--; } // Fill in any gaps with a hidden sequence point if (currSequencePoint.EndOffset != nextSequencePoint.Offset) { SequencePoint newSP = new SequencePoint() { Offset = currSequencePoint.EndOffset, EndOffset = nextSequencePoint.Offset }; newSP.SetHidden(); newList.Insert(++i, newSP); } } dict[function] = newList; } return dict; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/StatementBuilder.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Threading; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { sealed class StatementBuilder : ILVisitor { internal readonly ExpressionBuilder exprBuilder; readonly ILFunction currentFunction; readonly IDecompilerTypeSystem typeSystem; internal readonly DecompileRun decompileRun; readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; internal BlockContainer currentReturnContainer; internal IType currentResultType; internal bool currentIsIterator; internal bool EmitAsRefReadOnly; public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, DecompileRun decompileRun, CancellationToken cancellationToken) { Debug.Assert(typeSystem != null && decompilationContext != null); this.exprBuilder = new ExpressionBuilder( this, typeSystem, decompilationContext, currentFunction, settings, decompileRun, cancellationToken ); this.currentFunction = currentFunction; this.currentReturnContainer = (BlockContainer)currentFunction.Body; this.currentIsIterator = currentFunction.IsIterator; this.currentResultType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType; this.typeSystem = typeSystem; this.settings = settings; this.decompileRun = decompileRun; this.cancellationToken = cancellationToken; } public Statement Convert(ILInstruction inst) { cancellationToken.ThrowIfCancellationRequested(); return inst.AcceptVisitor(this); } public BlockStatement ConvertAsBlock(ILInstruction inst) { Statement stmt = Convert(inst).WithILInstruction(inst); return stmt as BlockStatement ?? new BlockStatement { stmt }; } protected override TranslatedStatement Default(ILInstruction inst) { return new ExpressionStatement(exprBuilder.Translate(inst)) .WithILInstruction(inst); } protected internal override TranslatedStatement VisitIsInst(IsInst inst) { // isinst on top-level (unused result) can be translated in general // (even for value types) by using "is" instead of "as" // This can happen when the result of "expr is T" is unused // and the C# compiler optimizes away the null check portion of the "is" operator. var arg = exprBuilder.Translate(inst.Argument); arg = ExpressionBuilder.UnwrapBoxingConversion(arg); return new ExpressionStatement( new IsExpression( arg, exprBuilder.ConvertType(inst.Type) ) .WithRR(new ResolveResult(exprBuilder.compilation.FindType(KnownTypeCode.Boolean))) .WithILInstruction(inst) ) .WithILInstruction(inst); } protected internal override TranslatedStatement VisitStLoc(StLoc inst) { var expr = exprBuilder.Translate(inst); // strip top-level ref on ref re-assignment if (expr.Expression is DirectionExpression dirExpr) { expr = expr.UnwrapChild(dirExpr.Expression); } return new ExpressionStatement(expr).WithILInstruction(inst); } protected internal override TranslatedStatement VisitStObj(StObj inst) { var expr = exprBuilder.Translate(inst); // strip top-level ref on ref re-assignment if (expr.Expression is DirectionExpression dirExpr) { expr = expr.UnwrapChild(dirExpr.Expression); } return new ExpressionStatement(expr).WithILInstruction(inst); } protected internal override TranslatedStatement VisitNop(Nop inst) { var stmt = new EmptyStatement(); if (inst.Comment != null) { stmt.AddChild(new Comment(inst.Comment), Roles.Comment); } return stmt.WithILInstruction(inst); } protected internal override TranslatedStatement VisitIfInstruction(IfInstruction inst) { var condition = exprBuilder.TranslateCondition(inst.Condition); var trueStatement = Convert(inst.TrueInst); var falseStatement = inst.FalseInst.OpCode == OpCode.Nop ? null : Convert(inst.FalseInst); return new IfElseStatement(condition, trueStatement, falseStatement).WithILInstruction(inst); } internal IEnumerable CreateTypedCaseLabel(long i, IType type, List<(string Key, int Value)> map = null) { object value; // unpack nullable type, if necessary: // we need to do this in all cases, because there are nullable bools and enum types as well. type = NullableType.GetUnderlyingType(type); if (type.IsKnownType(KnownTypeCode.Boolean)) { value = i != 0; } else if (map != null) { Debug.Assert(type.IsKnownType(KnownTypeCode.String)); var keys = map.Where(entry => entry.Value == i).Select(entry => entry.Key); foreach (var key in keys) yield return new ConstantResolveResult(type, key); yield break; } else if (type.Kind == TypeKind.Enum) { var enumType = type.GetDefinition().EnumUnderlyingType; TypeCode typeCode = ReflectionHelper.GetTypeCode(enumType); if (typeCode != TypeCode.Empty) { value = CSharpPrimitiveCast.Cast(typeCode, i, false); } else { value = i; } } else { TypeCode typeCode = ReflectionHelper.GetTypeCode(type); if (typeCode != TypeCode.Empty) { value = CSharpPrimitiveCast.Cast(typeCode, i, false); } else { value = i; } } yield return new ConstantResolveResult(type, value); } protected internal override TranslatedStatement VisitSwitchInstruction(SwitchInstruction inst) { return TranslateSwitch(null, inst).WithILInstruction(inst); } SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstruction inst) { var oldBreakTarget = breakTarget; breakTarget = switchContainer; // 'break' within a switch would only leave the switch var oldCaseLabelMapping = caseLabelMapping; caseLabelMapping = new Dictionary(); var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, false); IL.SwitchSection defaultSection = inst.GetDefaultSection(); var stmt = new SwitchStatement() { Expression = value }; Dictionary translationDictionary = new Dictionary(); // initialize C# switch sections. foreach (var section in inst.Sections) { // This is used in the block-label mapping. ConstantResolveResult firstValueResolveResult; var astSection = new Syntax.SwitchSection(); // Create case labels: if (section == defaultSection) { astSection.CaseLabels.Add(new CaseLabel()); firstValueResolveResult = null; } else { var values = section.Labels.Values.SelectMany(i => CreateTypedCaseLabel(i, type, strToInt?.Map)).ToArray(); if (section.HasNullLabel) { astSection.CaseLabels.Add(new CaseLabel(new NullReferenceExpression())); firstValueResolveResult = new ConstantResolveResult(SpecialType.NullType, null); } else { Debug.Assert(values.Length > 0); firstValueResolveResult = values[0]; } astSection.CaseLabels.AddRange(values.Select(label => new CaseLabel(exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true)))); } switch (section.Body) { case Branch br: // we can only inline the block, if all branches are in the switchContainer. if (br.TargetContainer == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer)) caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); break; default: break; } translationDictionary.Add(section, astSection); stmt.SwitchSections.Add(astSection); } foreach (var section in inst.Sections) { var astSection = translationDictionary[section]; switch (section.Body) { case Branch br: // we can only inline the block, if all branches are in the switchContainer. if (br.TargetContainer == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer)) ConvertSwitchSectionBody(astSection, br.TargetBlock); else ConvertSwitchSectionBody(astSection, section.Body); break; case Leave leave: if (astSection.CaseLabels.Count == 1 && astSection.CaseLabels.First().Expression.IsNull && leave.TargetContainer == switchContainer) { stmt.SwitchSections.Remove(astSection); break; } goto default; default: ConvertSwitchSectionBody(astSection, section.Body); break; } } if (switchContainer != null && stmt.SwitchSections.Count > 0) { // Translate any remaining blocks: var lastSectionStatements = stmt.SwitchSections.Last().Statements; foreach (var block in switchContainer.Blocks.Skip(1)) { if (caseLabelMapping.ContainsKey(block)) continue; lastSectionStatements.Add(new LabelStatement { Label = EnsureUniqueLabel(block) }); foreach (var nestedInst in block.Instructions) { var nestedStmt = Convert(nestedInst); if (nestedStmt is BlockStatement b) { foreach (var nested in b.Statements) lastSectionStatements.Add(nested.Detach()); } else { lastSectionStatements.Add(nestedStmt); } } Debug.Assert(block.FinalInstruction.OpCode == OpCode.Nop); } if (endContainerLabels.TryGetValue(switchContainer, out string label)) { lastSectionStatements.Add(new LabelStatement { Label = label }); lastSectionStatements.Add(new BreakStatement()); } } breakTarget = oldBreakTarget; caseLabelMapping = oldCaseLabelMapping; return stmt; } private void ConvertSwitchSectionBody(Syntax.SwitchSection astSection, ILInstruction bodyInst) { var body = Convert(bodyInst); astSection.Statements.Add(body); if (!bodyInst.HasFlag(InstructionFlags.EndPointUnreachable)) { // we need to insert 'break;' BlockStatement block = body as BlockStatement; if (block != null) { block.Add(new BreakStatement()); } else { astSection.Statements.Add(new BreakStatement()); } } } /// Target block that a 'continue;' statement would jump to Block continueTarget; /// Number of ContinueStatements that were created for the current continueTarget int continueCount; /// Maps blocks to cases. Dictionary caseLabelMapping; protected internal override TranslatedStatement VisitBranch(Branch inst) { if (inst.TargetBlock == continueTarget) { continueCount++; return new ContinueStatement().WithILInstruction(inst); } if (caseLabelMapping != null && caseLabelMapping.TryGetValue(inst.TargetBlock, out var label)) { if (label == null) return new GotoDefaultStatement().WithILInstruction(inst); return new GotoCaseStatement() { LabelExpression = exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true) } .WithILInstruction(inst); } return new GotoStatement(EnsureUniqueLabel(inst.TargetBlock)).WithILInstruction(inst); } /// Target container that a 'break;' statement would break out of BlockContainer breakTarget; /// Dictionary from BlockContainer to label name for 'goto of_container'; readonly Dictionary endContainerLabels = new Dictionary(); protected internal override TranslatedStatement VisitLeave(Leave inst) { if (inst.TargetContainer == breakTarget) return new BreakStatement().WithILInstruction(inst); if (inst.TargetContainer == currentReturnContainer) { if (currentIsIterator) return new YieldBreakStatement().WithILInstruction(inst); else if (!inst.Value.MatchNop()) { bool isLambdaOrExprTree = currentFunction.Kind is ILFunctionKind.ExpressionTree or ILFunctionKind.Delegate; var expr = exprBuilder.Translate(inst.Value, typeHint: currentResultType) .ConvertTo(currentResultType, exprBuilder, allowImplicitConversion: true); if (isLambdaOrExprTree && IsPossibleLossOfTypeInformation(expr.Type, currentResultType)) { expr = new CastExpression(exprBuilder.ConvertType(currentResultType), expr) .WithRR(new ConversionResolveResult(currentResultType, expr.ResolveResult, Conversion.IdentityConversion)).WithoutILInstruction(); } return new ReturnStatement(expr).WithILInstruction(inst); } else return new ReturnStatement().WithILInstruction(inst); } if (!endContainerLabels.TryGetValue(inst.TargetContainer, out string label)) { label = "end_" + inst.TargetLabel; if (!duplicateLabels.TryGetValue(label, out int count)) { duplicateLabels.Add(label, 1); } else { duplicateLabels[label]++; label += "_" + (count + 1); } endContainerLabels.Add(inst.TargetContainer, label); } return new GotoStatement(label).WithILInstruction(inst); } private bool IsPossibleLossOfTypeInformation(IType givenType, IType expectedType) { if (NormalizeTypeVisitor.IgnoreNullability.EquivalentTypes(givenType, expectedType)) return false; if (expectedType is TupleType { ElementNames.IsEmpty: false }) return true; if (expectedType == SpecialType.Dynamic) return true; if (givenType == SpecialType.NullType) return true; return false; } protected internal override TranslatedStatement VisitThrow(Throw inst) { return new ThrowStatement(exprBuilder.Translate(inst.Argument)).WithILInstruction(inst); } protected internal override TranslatedStatement VisitRethrow(Rethrow inst) { return new ThrowStatement().WithILInstruction(inst); } protected internal override TranslatedStatement VisitYieldReturn(YieldReturn inst) { var elementType = currentFunction.ReturnType.GetElementTypeFromIEnumerable(typeSystem, true, out var isGeneric); var expr = exprBuilder.Translate(inst.Value, typeHint: elementType) .ConvertTo(elementType, exprBuilder, allowImplicitConversion: true); return new YieldReturnStatement { Expression = expr }.WithILInstruction(inst); } TryCatchStatement MakeTryCatch(ILInstruction tryBlock) { var tryBlockConverted = Convert(tryBlock); var tryCatch = tryBlockConverted as TryCatchStatement; if (tryCatch != null && tryCatch.FinallyBlock.IsNull) return tryCatch; // extend existing try-catch tryCatch = new TryCatchStatement(); tryCatch.TryBlock = tryBlockConverted as BlockStatement ?? new BlockStatement { tryBlockConverted }; return tryCatch; } protected internal override TranslatedStatement VisitTryCatch(TryCatch inst) { var tryCatch = new TryCatchStatement(); tryCatch.TryBlock = ConvertAsBlock(inst.TryBlock); foreach (var handler in inst.Handlers) { var catchClause = new CatchClause(); catchClause.AddAnnotation(handler); var v = handler.Variable; if (v != null) { catchClause.AddAnnotation(new ILVariableResolveResult(v, v.Type)); if (v.StoreCount > 1 || v.LoadCount > 0 || v.AddressCount > 0) { catchClause.VariableName = v.Name; catchClause.Type = exprBuilder.ConvertType(v.Type); } else if (!v.Type.IsKnownType(KnownTypeCode.Object)) { catchClause.Type = exprBuilder.ConvertType(v.Type); } } if (!handler.Filter.MatchLdcI4(1)) catchClause.Condition = exprBuilder.TranslateCondition(handler.Filter); catchClause.Body = ConvertAsBlock(handler.Body); tryCatch.CatchClauses.Add(catchClause); } return tryCatch.WithILInstruction(inst); } protected internal override TranslatedStatement VisitTryFinally(TryFinally inst) { var tryCatch = MakeTryCatch(inst.TryBlock); tryCatch.FinallyBlock = ConvertAsBlock(inst.FinallyBlock); return tryCatch.WithILInstruction(inst); } protected internal override TranslatedStatement VisitTryFault(TryFault inst) { var tryCatch = new TryCatchStatement(); tryCatch.TryBlock = ConvertAsBlock(inst.TryBlock); var faultBlock = ConvertAsBlock(inst.FaultBlock); faultBlock.InsertChildAfter(null, new Comment("try-fault"), Roles.Comment); faultBlock.Add(new ThrowStatement()); tryCatch.CatchClauses.Add(new CatchClause { Body = faultBlock }); return tryCatch.WithILInstruction(inst); } protected internal override TranslatedStatement VisitLockInstruction(LockInstruction inst) { return new LockStatement { Expression = exprBuilder.Translate(inst.OnExpression), EmbeddedStatement = ConvertAsBlock(inst.Body) }.WithILInstruction(inst); } #region foreach construction static readonly InvocationExpression getEnumeratorPattern = new InvocationExpression( new Choice { new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator"), new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetAsyncEnumerator") } ); static readonly InvocationExpression extensionGetEnumeratorPattern = new InvocationExpression( new Choice { new MemberReferenceExpression(new AnyNode("type").ToExpression(), "GetEnumerator"), new MemberReferenceExpression(new AnyNode("type").ToExpression(), "GetAsyncEnumerator") }, new AnyNode("collection") ); static readonly Expression moveNextConditionPattern = new Choice { new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNext")), new UnaryOperatorExpression(UnaryOperatorType.Await, new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNextAsync"))) }; protected internal override TranslatedStatement VisitUsingInstruction(UsingInstruction inst) { var resource = exprBuilder.Translate(inst.ResourceExpression).Expression; var transformed = TransformToForeach(inst, resource); if (transformed != null) return transformed.WithILInstruction(inst); AstNode usingInit = resource; var var = inst.Variable; KnownTypeCode knownTypeCode; IType disposeType; string disposeTypeMethodName; if (inst.IsAsync) { knownTypeCode = KnownTypeCode.IAsyncDisposable; disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IAsyncDisposable); disposeTypeMethodName = "DisposeAsync"; } else { knownTypeCode = KnownTypeCode.IDisposable; disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); disposeTypeMethodName = "Dispose"; } if (!IsValidInCSharp(inst, knownTypeCode)) { Debug.Assert(var.Kind == VariableKind.UsingLocal); var.Kind = VariableKind.Local; var disposeVariable = currentFunction.RegisterVariable( VariableKind.Local, disposeType, AssignVariableNames.GenerateVariableName(currentFunction, disposeType, decompileRun.UsingScope) ); Expression disposeInvocation = new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, disposeTypeMethodName)); if (inst.IsAsync) { disposeInvocation = new UnaryOperatorExpression { Expression = disposeInvocation, Operator = UnaryOperatorType.Await }; } return new BlockStatement { new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())), new TryCatchStatement { TryBlock = ConvertAsBlock(inst.Body), FinallyBlock = new BlockStatement() { new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))), new IfElseStatement { Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()), TrueStatement = new ExpressionStatement(disposeInvocation) } } }, }.WithILInstruction(inst); } else { if (var.LoadCount > 0 || var.AddressCount > 0) { var type = settings.AnonymousTypes && var.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(var.Type); var vds = new VariableDeclarationStatement(type, var.Name, resource); vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(var, var.Type)); usingInit = vds; } return new UsingStatement { ResourceAcquisition = usingInit, IsAsync = inst.IsAsync, EmbeddedStatement = ConvertAsBlock(inst.Body) }.WithILInstruction(inst); } bool IsValidInCSharp(UsingInstruction inst, KnownTypeCode code) { if (inst.ResourceExpression.MatchLdNull()) return true; if (inst.IsRefStruct) return true; return NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(code)); } } Statement TransformToForeach(UsingInstruction inst, Expression resource) { if (!settings.ForEachStatement) { return null; } Match m; if (settings.ExtensionMethods && settings.ForEachWithGetEnumeratorExtension) { // Check if the using resource matches the GetEnumerator pattern ... m = getEnumeratorPattern.Match(resource); if (!m.Success) { // ... or the extension GetEnumeratorPattern. m = extensionGetEnumeratorPattern.Match(resource); if (!m.Success) return null; // Validate that the invocation is an extension method invocation. if (!(resource.GetSymbol() is IMethod method && exprBuilder.resolver.CanTransformToExtensionMethodCall(method, true))) { return null; } } } else { // Check if the using resource matches the GetEnumerator pattern. m = getEnumeratorPattern.Match(resource); if (!m.Success) return null; } // The using body must be a BlockContainer. if (!(inst.Body is BlockContainer container)) return null; bool isAsync = ((MemberReferenceExpression)((InvocationExpression)resource).Target).MemberName == "GetAsyncEnumerator"; if (isAsync != inst.IsAsync) return null; // The using-variable is the enumerator. var enumeratorVar = inst.Variable; // If there's another BlockContainer nested in this container and it only has one child block, unwrap it. // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop. var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalLeaveAfterLoop); // Detect whether we're dealing with a while loop with multiple embedded statements. if (loopContainer.Kind != ContainerKind.While) return null; if (!loopContainer.MatchConditionBlock(loopContainer.EntryPoint, out var conditionInst, out var body)) return null; // The loop condition must be a call to enumerator.MoveNext() var condition = exprBuilder.TranslateCondition(conditionInst); var m2 = moveNextConditionPattern.Match(condition.Expression); if (!m2.Success) return null; if (condition.Expression is UnaryOperatorExpression { Operator: UnaryOperatorType.Await } != isAsync) return null; // Check enumerator variable references. var enumeratorVar2 = m2.Get("enumerator").Single().GetILVariable(); if (enumeratorVar2 != enumeratorVar) return null; // Detect which foreach-variable transformation is necessary/possible. var transformation = DetectGetCurrentTransformation(container, body, loopContainer, enumeratorVar, conditionInst, out var singleGetter, out var foreachVariable); if (transformation == RequiredGetCurrentTransformation.NoForeach) return null; // Extract in-expression var collectionExpr = m.Get("collection").Single(); // Special case: foreach (var item in this) is decompiled as foreach (var item in base) // but a base reference is not valid in this context. if (collectionExpr is BaseReferenceExpression) { collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr); } else if (IsDynamicCastToIEnumerable(collectionExpr, out var dynamicExpr)) { collectionExpr = dynamicExpr.Detach(); } // Handle explicit casts: // This is the case if an explicit type different from the collection-item-type was used. // For example: foreach (ClassA item in nonGenericEnumerable) var type = singleGetter.Method.ReturnType; ILInstruction instToReplace = singleGetter; bool useVar = false; switch (instToReplace.Parent) { case CastClass cc: type = cc.Type; instToReplace = cc; break; case UnboxAny ua: type = ua.Type; instToReplace = ua; break; default: if (TupleType.IsTupleCompatible(type, out _)) { // foreach with get_Current returning a tuple type, let's check which type "var" would infer: var foreachRR = exprBuilder.resolver.ResolveForeach(collectionExpr.GetResolveResult()); if (EqualErasedType(type, foreachRR.ElementType)) { type = foreachRR.ElementType; useVar = true; } } break; } VariableDesignation designation = null; // Handle the required foreach-variable transformation: switch (transformation) { case RequiredGetCurrentTransformation.UseExistingVariable: if (foreachVariable.Type.Kind != TypeKind.Dynamic) foreachVariable.Type = type; foreachVariable.Kind = VariableKind.ForeachLocal; foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope, foreachVariable); break; case RequiredGetCurrentTransformation.IntroduceNewVariable: foreachVariable = currentFunction.RegisterVariable( VariableKind.ForeachLocal, type, AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope) ); instToReplace.ReplaceWith(new LdLoc(foreachVariable)); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); break; case RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy: foreachVariable = currentFunction.RegisterVariable( VariableKind.ForeachLocal, type, AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope) ); var localCopyVariable = currentFunction.RegisterVariable( VariableKind.Local, type, AssignVariableNames.GenerateVariableName(currentFunction, type, decompileRun.UsingScope) ); instToReplace.Parent.ReplaceWith(new LdLoca(localCopyVariable)); body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable))); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); break; case RequiredGetCurrentTransformation.Deconstruction: useVar = true; designation = TranslateDeconstructionDesignation((DeconstructInstruction)body.Instructions[0], isForeach: true); break; } if (designation == null) { designation = new SingleVariableDesignation { Identifier = foreachVariable.Name }; // Add the variable annotation for highlighting designation.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type)); } // Convert the modified body to C# AST: var whileLoopBlock = ConvertAsBlock(container); var whileLoop = (WhileStatement)whileLoopBlock.First(); var foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach(); // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement. Statement firstStatement = foreachBody.Statements.First(); if (firstStatement is LabelStatement) { // skip the entry-point label, if any firstStatement = firstStatement.GetNextStatement(); } Debug.Assert(firstStatement is ExpressionStatement); firstStatement.Remove(); if (settings.AnonymousTypes && type.ContainsAnonymousType()) useVar = true; // Construct the foreach loop. var foreachStmt = new ForeachStatement { IsAsync = isAsync, VariableType = useVar ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type), VariableDesignation = designation, InExpression = collectionExpr.Detach(), EmbeddedStatement = foreachBody }; foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, conditionInst, singleGetter)); foreachStmt.CopyAnnotationsFrom(whileLoop); // If there was an optional return statement, return it as well. // If there were labels or any other statements in the whileLoopBlock, move them after the foreach // loop. if (optionalLeaveAfterLoop != null || whileLoopBlock.Statements.Count > 1) { var block = new BlockStatement { Statements = { foreachStmt } }; if (optionalLeaveAfterLoop != null) { block.Statements.Add(optionalLeaveAfterLoop.AcceptVisitor(this)); } if (whileLoopBlock.Statements.Count > 1) { block.Statements.AddRange(whileLoopBlock.Statements .Skip(1) .SkipWhile(s => s.Annotations.Any(a => a == optionalLeaveAfterLoop)) .Select(SyntaxExtensions.Detach)); } return block; } return foreachStmt; } internal static VariableDesignation TranslateDeconstructionDesignation(DeconstructInstruction inst, bool isForeach) { var assignments = inst.Assignments.Instructions; int assignmentPos = 0; return ConstructDesignation(inst.Pattern); VariableDesignation ConstructDesignation(MatchInstruction matchInstruction) { var designations = new ParenthesizedVariableDesignation(); foreach (var subPattern in matchInstruction.SubPatterns.Cast()) { if (subPattern.IsVar) { var designation = new SingleVariableDesignation(); if (subPattern.HasDesignator) { ILVariable v = ((StLoc)assignments[assignmentPos]).Variable; if (isForeach) v.Kind = VariableKind.ForeachLocal; designation.Identifier = v.Name; designation.AddAnnotation(new ILVariableResolveResult(v)); assignmentPos++; } else { designation.Identifier = "_"; } designations.VariableDesignations.Add(designation); } else { designations.VariableDesignations.Add(ConstructDesignation(subPattern)); } } designations.AddAnnotation(matchInstruction); return designations; } } static bool EqualErasedType(IType a, IType b) { return NormalizeTypeVisitor.TypeErasure.EquivalentTypes(a, b); } private bool IsDynamicCastToIEnumerable(Expression expr, out Expression dynamicExpr) { if (!(expr is CastExpression cast)) { dynamicExpr = null; return false; } dynamicExpr = cast.Expression; if (!(expr.GetResolveResult() is ConversionResolveResult crr)) return false; if (!crr.Type.IsKnownType(KnownTypeCode.IEnumerable)) return false; return crr.Input.Type.Kind == TypeKind.Dynamic; } /// /// Unwraps a nested BlockContainer, if container contains only a single block, /// and that single block contains only a BlockContainer followed by a Leave instruction. /// If the leave instruction is a return that carries a value, the container is unwrapped only /// if the value has no side-effects. /// Otherwise returns the unmodified container. /// /// If the leave is a return/break and has no side-effects, we can move the return out of the using-block and put it after the loop, otherwise returns null. BlockContainer UnwrapNestedContainerIfPossible(BlockContainer container, out Leave optionalLeaveInst) { optionalLeaveInst = null; // Check block structure: if (container.Blocks.Count != 1) return container; var nestedBlock = container.Blocks[0]; if (nestedBlock.Instructions.Count != 2 || !(nestedBlock.Instructions[0] is BlockContainer nestedContainer) || !(nestedBlock.Instructions[1] is Leave leave)) return container; // If the leave has no value, just unwrap the BlockContainer. if (leave.MatchLeave(container)) return nestedContainer; // If the leave is a return/break, we can move it out of the using-block and put it after the loop // (but only if the value doesn't have side-effects) if (SemanticHelper.IsPure(leave.Value.Flags)) { optionalLeaveInst = leave; return nestedContainer; } return container; } enum RequiredGetCurrentTransformation { /// /// Foreach transformation not possible. /// NoForeach, /// /// Uninline the stloc foreachVar(call get_Current()) and insert it as first statement in the loop body. /// /// ... (stloc foreachVar(call get_Current()) ... /// => /// stloc foreachVar(call get_Current()) /// ... (ldloc foreachVar) ... /// /// UseExistingVariable, /// /// No store was found, thus create a new variable and use it as foreach variable. /// /// ... (call get_Current()) ... /// => /// stloc foreachVar(call get_Current()) /// ... (ldloc foreachVar) ... /// /// IntroduceNewVariable, /// /// No store was found, thus create a new variable and use it as foreach variable. /// Additionally it is necessary to copy the value of the foreach variable to another local /// to allow safe modification of its value. /// /// ... addressof(call get_Current()) ... /// => /// stloc foreachVar(call get_Current()) /// stloc copy(ldloc foreachVar) /// ... (ldloca copy) ... /// /// IntroduceNewVariableAndLocalCopy, /// /// call get_Current() is the tested operand of a deconstruct instruction. /// and the deconstruct instruction is the first statement in the loop body. /// Deconstruction, } /// /// Determines whether is only used once inside for accessing the Current property. /// /// The using body container. This is only used for variable usage checks. /// The loop body. The first statement of this block is analyzed. /// The current enumerator. /// The call MoveNext(ldloc enumerator) pattern. /// Returns the call instruction invoking Current's getter. /// Returns the the foreach variable, if a suitable was found. This variable is only assigned once and its assignment is the first statement in . /// for details. RequiredGetCurrentTransformation DetectGetCurrentTransformation(BlockContainer usingContainer, Block loopBody, BlockContainer loopContainer, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out ILVariable foreachVariable) { singleGetter = null; foreachVariable = null; var loads = enumerator.LoadInstructions.OfType() .Concat(enumerator.AddressInstructions.OfType()) .Where(ld => !ld.IsDescendantOf(moveNextUsage)) .ToArray(); // enumerator is used in multiple locations or not in conjunction with get_Current // => no foreach if (loads.Length != 1 || !ParentIsCurrentGetter(loads[0])) return RequiredGetCurrentTransformation.NoForeach; singleGetter = (CallInstruction)loads[0].Parent; // singleGetter is not part of the first instruction in body or cannot be uninlined // => no foreach if (!(singleGetter.IsDescendantOf(loopBody.Instructions[0]) && ILInlining.CanUninline(singleGetter, loopBody.Instructions[0]))) { return RequiredGetCurrentTransformation.NoForeach; } if (loopBody.Instructions[0] is DeconstructInstruction deconstruction && CanBeDeconstructedInForeach(deconstruction, singleGetter, usingContainer, loopContainer)) { return RequiredGetCurrentTransformation.Deconstruction; } ILInstruction inst = singleGetter; // in some cases, i.e. foreach variable with explicit type different from the collection-item-type, // the result of call get_Current is casted. while (inst.Parent is UnboxAny || inst.Parent is CastClass) inst = inst.Parent; // One variable was found. if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) { // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once. if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer, loopContainer)) { foreachVariable = stloc.Variable; return RequiredGetCurrentTransformation.UseExistingVariable; } } // In optimized Roslyn code it can happen that the foreach variable is referenced via addressof // We only do this unwrapping if where dealing with a custom struct type. if (CurrentIsStructSetterTarget(inst, singleGetter)) { return RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy; } // No suitable variable was found: we need a new one. return RequiredGetCurrentTransformation.IntroduceNewVariable; } bool CanBeDeconstructedInForeach(DeconstructInstruction deconstruction, ILInstruction singleGetter, BlockContainer usingContainer, BlockContainer loopContainer) { ILInstruction testedOperand = deconstruction.Pattern.TestedOperand; if (testedOperand != singleGetter) { if (!(testedOperand is AddressOf addressOf && addressOf.Value == singleGetter)) return false; } if (deconstruction.Init.Count > 0) return false; if (deconstruction.Conversions.Instructions.Count > 0) return false; var operandType = singleGetter.InferType(this.typeSystem); var expectedType = deconstruction.Pattern.Variable.Type; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(operandType, expectedType)) return false; var usedVariables = new HashSet(ILVariableEqualityComparer.Instance); foreach (var item in deconstruction.Assignments.Instructions) { if (!item.MatchStLoc(out var v, out var value)) return false; expectedType = ((LdLoc)value).Variable.Type; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(v.Type, expectedType)) return false; if (!(v.Kind == VariableKind.StackSlot || v.Kind == VariableKind.Local)) return false; if (!VariableIsOnlyUsedInBlock((StLoc)item, usingContainer, loopContainer)) return false; if (!(v.CaptureScope == null || v.CaptureScope == usingContainer)) return false; if (!usedVariables.Add(v)) return false; } return true; } /// /// Determines whether storeInst.Variable is only assigned once and used only inside . /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions, /// or as target of ldobj. /// (This only applies to value types.) /// bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer, BlockContainer loopContainer) { if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) return false; if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst))) return false; if (storeInst.Variable.StoreInstructions.OfType().Any(st => st != storeInst)) return false; if (!(storeInst.Variable.CaptureScope == null || storeInst.Variable.CaptureScope == loopContainer)) return false; return true; bool AddressUseAllowed(LdLoca la) { if (!la.IsDescendantOf(usingContainer)) return false; if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type)) return true; var current = la.Parent; while (current is LdFlda next) { current = next.Parent; } return current is LdObj; } } /// /// Returns true if singleGetter is a value type and its address is used as setter target. /// bool CurrentIsStructSetterTarget(ILInstruction inst, CallInstruction singleGetter) { if (!(inst.Parent is AddressOf addr)) return false; return IsTargetOfSetterCall(addr, singleGetter.Method.ReturnType); } bool IsTargetOfSetterCall(ILInstruction inst, IType targetType) { if (inst.ChildIndex != 0) return false; if (targetType.IsReferenceType ?? false) return false; switch (inst.Parent.OpCode) { case OpCode.Call: case OpCode.CallVirt: var targetMethod = ((CallInstruction)inst.Parent).Method; if (!targetMethod.IsAccessor || targetMethod.IsStatic) return false; switch (targetMethod.AccessorOwner) { case IProperty p: return targetMethod.AccessorKind == System.Reflection.MethodSemanticsAttributes.Setter; default: return true; } default: return false; } } bool ParentIsCurrentGetter(ILInstruction inst) { return inst.Parent is CallInstruction cv && cv.Method.IsAccessor && cv.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter; } #endregion protected internal override TranslatedStatement VisitPinnedRegion(PinnedRegion inst) { var fixedStmt = new FixedStatement(); fixedStmt.Type = exprBuilder.ConvertType(inst.Variable.Type); Expression initExpr; if (inst.Init is GetPinnableReference gpr) { if (gpr.Method != null) { IType expectedType = gpr.Method.IsStatic ? gpr.Method.Parameters[0].Type : gpr.Method.DeclaringType; initExpr = exprBuilder.Translate(gpr.Argument, typeHint: expectedType).ConvertTo(expectedType, exprBuilder); } else { initExpr = exprBuilder.Translate(gpr.Argument); } } else { IType refType = inst.Variable.Type; if (refType is PointerType pointerType) { refType = new ByReferenceType(pointerType.ElementType); } initExpr = exprBuilder.Translate(inst.Init, typeHint: refType).ConvertTo(refType, exprBuilder); if (initExpr is DirectionExpression dirExpr) { if (dirExpr.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.Dereference) { initExpr = uoe.Expression.Detach(); } else { initExpr = new UnaryOperatorExpression(UnaryOperatorType.AddressOf, dirExpr.Expression.Detach()) .WithRR(new ResolveResult(inst.Variable.Type)); } } if (initExpr.GetResolveResult()?.Type.Kind == TypeKind.Pointer && !IsAddressOfMoveableVar(initExpr) && !IsFixedSizeBuffer(initExpr) && refType is ByReferenceType brt) { // C# doesn't allow pinning an already-unmanaged pointer // fixed (int* ptr = existing_ptr) {} -> invalid // fixed (int* ptr = &existing_ptr->field) {} -> invalid // fixed (int* ptr = &local_var) {} -> invalid // We work around this by instead doing: // fixed (int* ptr = &Unsafe.AsRef(existing_ptr)) var asRefCall = exprBuilder.CallUnsafeIntrinsic( name: "AsRef", arguments: new Expression[] { initExpr }, returnType: brt.ElementType, typeArguments: new IType[] { brt.ElementType } ); initExpr = new UnaryOperatorExpression(UnaryOperatorType.AddressOf, asRefCall) .WithRR(new ResolveResult(inst.Variable.Type)); } } fixedStmt.Variables.Add(new VariableInitializer(inst.Variable.Name, initExpr).WithILVariable(inst.Variable)); fixedStmt.EmbeddedStatement = Convert(inst.Body); return fixedStmt.WithILInstruction(inst); } private static bool IsAddressOfMoveableVar(Expression initExpr) { if (initExpr is UnaryOperatorExpression { Operator: UnaryOperatorType.AddressOf } uoe) { var inst = uoe.Expression.Annotation(); return !(inst != null && PointerArithmeticOffset.IsFixedVariable(inst)); } return false; } private static bool IsFixedSizeBuffer(Expression initExpr) { var mrr = initExpr.GetResolveResult() as MemberResolveResult; return mrr?.Member is IField f && CSharpDecompiler.IsFixedField(f, out _, out _); } protected internal override TranslatedStatement VisitBlock(Block block) { if (block.Kind != BlockKind.ControlFlow) return Default(block); // Block without container BlockStatement blockStatement = new BlockStatement(); foreach (var inst in block.Instructions) { blockStatement.Add(Convert(inst)); } if (block.FinalInstruction.OpCode != OpCode.Nop) blockStatement.Add(Convert(block.FinalInstruction)); return blockStatement.WithILInstruction(block); } protected internal override TranslatedStatement VisitBlockContainer(BlockContainer container) { if (container.Kind != ContainerKind.Normal && container.EntryPoint.IncomingEdgeCount > 1) { var oldContinueTarget = continueTarget; var oldContinueCount = continueCount; var oldBreakTarget = breakTarget; var loop = ConvertLoop(container); loop.AddAnnotation(container); continueTarget = oldContinueTarget; continueCount = oldContinueCount; breakTarget = oldBreakTarget; return loop.WithILInstruction(container); } else if (container.EntryPoint.Instructions.Count == 1 && container.EntryPoint.Instructions[0] is SwitchInstruction switchInst) { return TranslateSwitch(container, switchInst).WithILInstruction(container); } else { var blockStmt = ConvertBlockContainer(container, false); return blockStmt.WithILInstruction(container); } } Statement ConvertLoop(BlockContainer container) { ILInstruction condition; Block loopBody; BlockStatement blockStatement; continueCount = 0; breakTarget = container; switch (container.Kind) { case ContainerKind.Loop: continueTarget = container.EntryPoint; blockStatement = ConvertBlockContainer(container, true); Debug.Assert(continueCount < container.EntryPoint.IncomingEdgeCount); Debug.Assert(blockStatement.Statements.First() is LabelStatement); if (container.EntryPoint.IncomingEdgeCount == continueCount + 1) { // Remove the entrypoint label if all jumps to the label were replaced with 'continue;' statements blockStatement.Statements.First().Remove(); } if (blockStatement.LastOrDefault() is ContinueStatement continueStmt) continueStmt.Remove(); DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(new PrimitiveExpression(true), blockStatement); case ContainerKind.While: continueTarget = container.EntryPoint; if (!container.MatchConditionBlock(continueTarget, out condition, out loopBody)) throw new NotSupportedException("Invalid condition block in while loop."); blockStatement = ConvertAsBlock(loopBody); if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) blockStatement.Add(new BreakStatement()); blockStatement = ConvertBlockContainer(blockStatement, container, container.Blocks.Skip(1).Except(new[] { loopBody }), true); Debug.Assert(continueCount < container.EntryPoint.IncomingEdgeCount); if (continueCount + 1 < container.EntryPoint.IncomingEdgeCount) { // There's an incoming edge to the entry point (=while condition) that wasn't represented as "continue;" // -> emit a real label // We'll also remove any "continue;" in front of the label, as it's redundant. if (blockStatement.LastOrDefault() is ContinueStatement) blockStatement.Last().Remove(); blockStatement.Add(new LabelStatement { Label = EnsureUniqueLabel(container.EntryPoint) }); } if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2) continueStmt2.Remove(); DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement); case ContainerKind.DoWhile: continueTarget = container.Blocks.Last(); if (!container.MatchConditionBlock(continueTarget, out condition, out _)) throw new NotSupportedException("Invalid condition block in do-while loop."); blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks.SkipLast(1), true); if (container.EntryPoint.IncomingEdgeCount == 2) { // Remove the entry-point label, if there are only two jumps to the entry-point: // from outside the loop and from the condition-block. blockStatement.Statements.First().Remove(); } if (blockStatement.LastOrDefault() is ContinueStatement continueStmt3) continueStmt3.Remove(); if (continueTarget.IncomingEdgeCount > continueCount) { // if there are branches to the condition block, that were not converted // to continue statements, we have to introduce an extra label. blockStatement.Add(new LabelStatement { Label = EnsureUniqueLabel(continueTarget) }); } DeclareLocalFunctions(currentFunction, container, blockStatement); if (blockStatement.Statements.Count == 0) { return new WhileStatement { Condition = exprBuilder.TranslateCondition(condition), EmbeddedStatement = blockStatement }; } return new DoWhileStatement { EmbeddedStatement = blockStatement, Condition = exprBuilder.TranslateCondition(condition) }; case ContainerKind.For: continueTarget = container.Blocks.Last(); if (!container.MatchConditionBlock(container.EntryPoint, out condition, out loopBody)) throw new NotSupportedException("Invalid condition block in for loop."); blockStatement = ConvertAsBlock(loopBody); if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) blockStatement.Add(new BreakStatement()); if (!container.MatchIncrementBlock(continueTarget)) throw new NotSupportedException("Invalid increment block in for loop."); blockStatement = ConvertBlockContainer(blockStatement, container, container.Blocks.SkipLast(1).Skip(1).Except(new[] { loopBody }), true); var forStmt = new ForStatement() { Condition = exprBuilder.TranslateCondition(condition), EmbeddedStatement = blockStatement }; if (blockStatement.LastOrDefault() is ContinueStatement continueStmt4) continueStmt4.Remove(); for (int i = 0; i < continueTarget.Instructions.Count - 1; i++) { forStmt.Iterators.Add(Convert(continueTarget.Instructions[i])); } if (continueTarget.IncomingEdgeCount > continueCount) blockStatement.Add(new LabelStatement { Label = EnsureUniqueLabel(continueTarget) }); DeclareLocalFunctions(currentFunction, container, blockStatement); return forStmt; default: throw new ArgumentOutOfRangeException(); } } BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop) { var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); DeclareLocalFunctions(currentFunction, container, blockStatement); if (currentFunction.Body == container) { if (EmitAsRefReadOnly) { var methodDecl = new MethodDeclaration(); if (settings.StaticLocalFunctions) { methodDecl.Modifiers = Modifiers.Static; } methodDecl.ReturnType = new ComposedType() { HasReadOnlySpecifier = true, HasRefSpecifier = true, BaseType = new SimpleType("T") }; methodDecl.Name = "ILSpyHelper_AsRefReadOnly"; methodDecl.TypeParameters.Add(new TypeParameterDeclaration("T")); methodDecl.Parameters.Add(new ParameterDeclaration { ParameterModifier = ReferenceKind.In, Type = new SimpleType("T"), Name = "temp" }); methodDecl.Body = new BlockStatement(); methodDecl.Body.AddChild(new Comment( "ILSpy generated this function to help ensure overload resolution can pick the overload using 'in'"), Roles.Comment); methodDecl.Body.Add(new ReturnStatement(new DirectionExpression(FieldDirection.Ref, new IdentifierExpression("temp")))); blockStatement.Statements.Add( new LocalFunctionDeclarationStatement(methodDecl) ); } } return blockStatement; } void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement) { foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) { if (localFunction.DeclarationScope != container) continue; blockStatement.Add(TranslateFunction(localFunction)); } LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) { var astBuilder = exprBuilder.astBuilder; var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod); var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); foreach (var (i, p) in method.Parameters.WithIndex()) { if (variables.TryGetValue(i, out var v)) { p.Name = v.Name; } } if (function.Method.HasBody) { var nestedBuilder = new StatementBuilder( typeSystem, exprBuilder.decompilationContext, function, settings, decompileRun, cancellationToken ); method.Body = nestedBuilder.ConvertAsBlock(function.Body); Comment prev = null; foreach (string warning in function.Warnings) { method.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); } } else { method.Modifiers |= Modifiers.Extern; } CSharpDecompiler.AddAnnotationsToDeclaration(function.ReducedMethod, method, function); CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function, function.Method.HasBody); CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated); var stmt = new LocalFunctionDeclarationStatement(method); stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); stmt.WithILInstruction(function); return stmt; } } BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable blocks, bool isLoop) { foreach (var block in blocks) { if (block.IncomingEdgeCount > 1 || block != container.EntryPoint) { // If there are any incoming branches to this block, add a label: blockStatement.Add(new LabelStatement { Label = EnsureUniqueLabel(block) }); } foreach (var inst in block.Instructions) { if (!isLoop && inst is Leave leave && IsFinalLeave(leave)) { // skip the final 'leave' instruction and just fall out of the BlockStatement blockStatement.AddAnnotation(new ImplicitReturnAnnotation(leave)); continue; } var stmt = Convert(inst); if (stmt is BlockStatement b) { foreach (var nested in b.Statements) blockStatement.Add(nested.Detach()); } else { blockStatement.Add(stmt.Detach()); } } if (block.FinalInstruction.OpCode != OpCode.Nop) { blockStatement.Add(Convert(block.FinalInstruction)); } } if (endContainerLabels.TryGetValue(container, out string label)) { if (isLoop && !(blockStatement.LastOrDefault() is ContinueStatement)) { blockStatement.Add(new ContinueStatement()); } blockStatement.Add(new LabelStatement { Label = label }); if (isLoop) { blockStatement.Add(new BreakStatement()); } } return blockStatement; } readonly Dictionary labels = new Dictionary(); readonly Dictionary duplicateLabels = new Dictionary(); string EnsureUniqueLabel(Block block) { if (labels.TryGetValue(block, out string label)) return label; if (!duplicateLabels.TryGetValue(block.Label, out int count)) { labels.Add(block, block.Label); duplicateLabels.Add(block.Label, 1); return block.Label; } label = $"{block.Label}_{count + 1}"; duplicateLabels[block.Label]++; labels.Add(block, label); return label; } static bool IsFinalLeave(Leave leave) { if (!leave.Value.MatchNop()) return false; Block block = (Block)leave.Parent; if (leave.ChildIndex != block.Instructions.Count - 1 || block.FinalInstruction.OpCode != OpCode.Nop) return false; BlockContainer container = (BlockContainer)block.Parent; return block.ChildIndex == container.Blocks.Count - 1 && container == leave.TargetContainer; } protected internal override TranslatedStatement VisitInitblk(Initblk inst) { var stmt = new ExpressionStatement( exprBuilder.CallUnsafeIntrinsic( inst.UnalignedPrefix != 0 ? "InitBlockUnaligned" : "InitBlock", new Expression[] { exprBuilder.Translate(inst.Address), exprBuilder.Translate(inst.Value), exprBuilder.Translate(inst.Size) }, exprBuilder.compilation.FindType(KnownTypeCode.Void), inst ) ); stmt.InsertChildAfter(null, new Comment(" IL initblk instruction"), Roles.Comment); return stmt.WithILInstruction(inst); } protected internal override TranslatedStatement VisitCpblk(Cpblk inst) { var stmt = new ExpressionStatement( exprBuilder.CallUnsafeIntrinsic( inst.UnalignedPrefix != 0 ? "CopyBlockUnaligned" : "CopyBlock", new Expression[] { exprBuilder.Translate(inst.DestAddress), exprBuilder.Translate(inst.SourceAddress), exprBuilder.Translate(inst.Size) }, exprBuilder.compilation.FindType(KnownTypeCode.Void), inst ) ); stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment); return stmt.WithILInstruction(inst); } protected internal override TranslatedStatement VisitCkfinite(Ckfinite inst) { var isFiniteCall = new InvocationExpression { Target = new MemberReferenceExpression { Target = new TypeReferenceExpression(new Syntax.PrimitiveType("float")), MemberName = "IsFinite" }, Arguments = { exprBuilder.Translate(inst.Argument) } }; var arithmeticException = typeSystem.FindType(typeof(ArithmeticException)); var arithmeticExceptionSyntax = new SimpleType("ArithmeticException"); arithmeticExceptionSyntax.AddAnnotation(new TypeResolveResult(arithmeticException)); return new IfElseStatement { Condition = new UnaryOperatorExpression { Operator = UnaryOperatorType.Not, Expression = isFiniteCall }, TrueStatement = new ThrowStatement( new ObjectCreateExpression { Type = arithmeticExceptionSyntax } ), }.WithILInstruction(inst); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs ================================================ #nullable enable // // AstNode.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.IO; using System.Linq; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public abstract class AstNode : AbstractAnnotatable, IFreezable, INode, ICloneable { // the Root role must be available when creating the null nodes, so we can't put it in the Roles class internal static readonly Role RootRole = new Role("Root", null); #region Null public static readonly AstNode Null = new NullAstNode(); sealed class NullAstNode : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator AstNode?(PatternMatching.Pattern? pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : AstNode, INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role? role, PatternMatching.INode? pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion AstNode? parent; AstNode? prevSibling; AstNode? nextSibling; AstNode? firstChild; AstNode? lastChild; // Flags, from least significant to most significant bits: // - Role.RoleIndexBits: role index // - 1 bit: IsFrozen protected uint flags = RootRole.Index; // Derived classes may also use a few bits, // for example Identifier uses 1 bit for IsVerbatim const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; const uint frozenBit = 1u << Role.RoleIndexBits; protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; protected AstNode() { if (IsNull) Freeze(); } public bool IsFrozen { get { return (flags & frozenBit) != 0; } } public void Freeze() { if (!IsFrozen) { for (AstNode? child = firstChild; child != null; child = child.nextSibling) child.Freeze(); flags |= frozenBit; } } protected void ThrowIfFrozen() { if (IsFrozen) throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); } public abstract NodeType NodeType { get; } public virtual bool IsNull { get { return false; } } public virtual TextLocation StartLocation { get { var child = firstChild; if (child == null) return TextLocation.Empty; return child.StartLocation; } } public virtual TextLocation EndLocation { get { var child = lastChild; if (child == null) return TextLocation.Empty; return child.EndLocation; } } public AstNode? Parent { get { return parent; } } public Role Role { get { return Role.GetByIndex(flags & roleIndexMask); } set { if (value == null) throw new ArgumentNullException(nameof(value)); if (!value.IsValid(this)) throw new ArgumentException("This node is not valid in the new role."); ThrowIfFrozen(); SetRole(value); } } internal uint RoleIndex { get { return flags & roleIndexMask; } } void SetRole(Role role) { flags = (flags & ~roleIndexMask) | role.Index; } public AstNode? NextSibling { get { return nextSibling; } } public AstNode? PrevSibling { get { return prevSibling; } } public AstNode? FirstChild { get { return firstChild; } } public AstNode? LastChild { get { return lastChild; } } public bool HasChildren { get { return firstChild != null; } } public IEnumerable Children { get { AstNode? next; for (AstNode? cur = firstChild; cur != null; cur = next) { Debug.Assert(cur.parent == this); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.nextSibling; yield return cur; } } } /// /// Gets the ancestors of this node (excluding this node itself) /// public IEnumerable Ancestors { get { for (AstNode? cur = parent; cur != null; cur = cur.parent) { yield return cur; } } } /// /// Gets the ancestors of this node (including this node itself) /// public IEnumerable AncestorsAndSelf { get { for (AstNode? cur = this; cur != null; cur = cur.parent) { yield return cur; } } } /// /// Gets all descendants of this node (excluding this node itself) in pre-order. /// public IEnumerable Descendants { get { return GetDescendantsImpl(false); } } /// /// Gets all descendants of this node (including this node itself) in pre-order. /// public IEnumerable DescendantsAndSelf { get { return GetDescendantsImpl(true); } } public IEnumerable DescendantNodes(Func? descendIntoChildren = null) { return GetDescendantsImpl(false, descendIntoChildren); } public IEnumerable DescendantNodesAndSelf(Func? descendIntoChildren = null) { return GetDescendantsImpl(true, descendIntoChildren); } IEnumerable GetDescendantsImpl(bool includeSelf, Func? descendIntoChildren = null) { if (includeSelf) { yield return this; if (descendIntoChildren != null && !descendIntoChildren(this)) yield break; } Stack nextStack = new Stack(); nextStack.Push(null); AstNode? pos = firstChild; while (pos != null) { // Remember next before yielding pos. // This allows removing/replacing nodes while iterating through the list. if (pos.nextSibling != null) nextStack.Push(pos.nextSibling); yield return pos; if (pos.firstChild != null && (descendIntoChildren == null || descendIntoChildren(pos))) pos = pos.firstChild; else pos = nextStack.Pop(); } } /// /// Gets the first child with the specified role. /// Returns the role's null object if the child is not found. /// public T GetChildByRole(Role role) where T : AstNode? { if (role == null) throw new ArgumentNullException(nameof(role)); uint roleIndex = role.Index; for (var cur = firstChild; cur != null; cur = cur.nextSibling) { if ((cur.flags & roleIndexMask) == roleIndex) return (T)cur; } return role.NullObject; } public T? GetParent() where T : AstNode { return Ancestors.OfType().FirstOrDefault(); } public AstNode? GetParent(Func? pred) { return pred != null ? Ancestors.FirstOrDefault(pred) : Ancestors.FirstOrDefault(); } public AstNodeCollection GetChildrenByRole(Role role) where T : AstNode { return new AstNodeCollection(this, role); } protected void SetChildByRole(Role role, T newChild) where T : AstNode { AstNode oldChild = GetChildByRole(role); if (oldChild.IsNull) AddChild(newChild, role); else oldChild.ReplaceWith(newChild); } public void AddChild(T child, Role role) where T : AstNode { if (role == null) throw new ArgumentNullException(nameof(role)); if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child == this) throw new ArgumentException("Cannot add a node to itself as a child.", nameof(child)); if (child.parent != null) throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) throw new ArgumentException("Cannot add a frozen node.", nameof(child)); AddChildUnsafe(child, role); } public void AddChildWithExistingRole(AstNode? child) { if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child == this) throw new ArgumentException("Cannot add a node to itself as a child.", nameof(child)); if (child.parent != null) throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) throw new ArgumentException("Cannot add a frozen node.", nameof(child)); AddChildUnsafe(child, child.Role); } /// /// Adds a child without performing any safety checks. /// internal void AddChildUnsafe(AstNode child, Role role) { child.parent = this; child.SetRole(role); if (firstChild == null) { lastChild = firstChild = child; } else { lastChild!.nextSibling = child; child.prevSibling = lastChild; lastChild = child; } } public void InsertChildBefore(AstNode? nextSibling, T child, Role role) where T : AstNode { if (role == null) throw new ArgumentNullException(nameof(role)); if (nextSibling == null || nextSibling.IsNull) { AddChild(child, role); return; } if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child.parent != null) throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) throw new ArgumentException("Cannot add a frozen node.", nameof(child)); if (nextSibling.parent != this) throw new ArgumentException("NextSibling is not a child of this node.", nameof(nextSibling)); // No need to test for "Cannot add children to null nodes", // as there isn't any valid nextSibling in null nodes. InsertChildBeforeUnsafe(nextSibling, child, role); } internal void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, Role role) { child.parent = this; child.SetRole(role); child.nextSibling = nextSibling; child.prevSibling = nextSibling.prevSibling; if (nextSibling.prevSibling != null) { Debug.Assert(nextSibling.prevSibling.nextSibling == nextSibling); nextSibling.prevSibling.nextSibling = child; } else { Debug.Assert(firstChild == nextSibling); firstChild = child; } nextSibling.prevSibling = child; } public void InsertChildAfter(AstNode? prevSibling, T child, Role role) where T : AstNode { InsertChildBefore((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); } /// /// Removes this node from its parent. /// public void Remove() { if (parent != null) { ThrowIfFrozen(); if (prevSibling != null) { Debug.Assert(prevSibling.nextSibling == this); prevSibling.nextSibling = nextSibling; } else { Debug.Assert(parent.firstChild == this); parent.firstChild = nextSibling; } if (nextSibling != null) { Debug.Assert(nextSibling.prevSibling == this); nextSibling.prevSibling = prevSibling; } else { Debug.Assert(parent.lastChild == this); parent.lastChild = prevSibling; } parent = null; prevSibling = null; nextSibling = null; } } /// /// Replaces this node with the new node. /// public void ReplaceWith(AstNode? newNode) { if (newNode == null || newNode.IsNull) { Remove(); return; } if (newNode == this) return; // nothing to do... if (parent == null) { throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } ThrowIfFrozen(); // Because this method doesn't statically check the new node's type with the role, // we perform a runtime test: if (!this.Role.IsValid(newNode)) { throw new ArgumentException(string.Format("The new node '{0}' is not valid in the role {1}", newNode.GetType().Name, this.Role.ToString()), nameof(newNode)); } if (newNode.parent != null) { // newNode is used within this tree? if (newNode.Ancestors.Contains(this)) { // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" // enable automatic removal newNode.Remove(); } else { throw new ArgumentException("Node is already used in another tree.", nameof(newNode)); } } if (newNode.IsFrozen) throw new ArgumentException("Cannot add a frozen node.", nameof(newNode)); newNode.parent = parent; newNode.SetRole(this.Role); newNode.prevSibling = prevSibling; newNode.nextSibling = nextSibling; if (prevSibling != null) { Debug.Assert(prevSibling.nextSibling == this); prevSibling.nextSibling = newNode; } else { Debug.Assert(parent.firstChild == this); parent.firstChild = newNode; } if (nextSibling != null) { Debug.Assert(nextSibling.prevSibling == this); nextSibling.prevSibling = newNode; } else { Debug.Assert(parent.lastChild == this); parent.lastChild = newNode; } parent = null; prevSibling = null; nextSibling = null; } public AstNode? ReplaceWith(Func replaceFunction) { if (replaceFunction == null) throw new ArgumentNullException(nameof(replaceFunction)); if (parent == null) { throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } AstNode oldParent = parent; AstNode? oldSuccessor = nextSibling; Role oldRole = this.Role; Remove(); AstNode? replacement = replaceFunction(this); if (oldSuccessor != null && oldSuccessor.parent != oldParent) throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); if (!(replacement == null || replacement.IsNull)) { if (replacement.parent != null) throw new InvalidOperationException("replace function must return the root of a tree"); if (!oldRole.IsValid(replacement)) { throw new InvalidOperationException(string.Format("The new node '{0}' is not valid in the role {1}", replacement.GetType().Name, oldRole.ToString())); } if (oldSuccessor != null) oldParent.InsertChildBeforeUnsafe(oldSuccessor, replacement, oldRole); else oldParent.AddChildUnsafe(replacement, oldRole); } return replacement; } /// /// Clones the whole subtree starting at this AST node. /// /// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned. public AstNode Clone() { AstNode copy = (AstNode)MemberwiseClone(); // First, reset the shallow pointer copies copy.parent = null; copy.firstChild = null; copy.lastChild = null; copy.prevSibling = null; copy.nextSibling = null; copy.flags &= ~frozenBit; // unfreeze the copy // Then perform a deep copy: for (AstNode? cur = firstChild; cur != null; cur = cur.nextSibling) { copy.AddChildUnsafe(cur.Clone(), cur.Role); } // Finally, clone the annotation, if necessary copy.CloneAnnotations(); return copy; } object ICloneable.Clone() { return Clone(); } public abstract void AcceptVisitor(IAstVisitor visitor); public abstract T AcceptVisitor(IAstVisitor visitor); public abstract S AcceptVisitor(IAstVisitor visitor, T data); #region Pattern Matching protected static bool MatchString(string? pattern, string? text) { return PatternMatching.Pattern.MatchString(pattern, text); } protected internal abstract bool DoMatch(AstNode? other, PatternMatching.Match match); bool PatternMatching.INode.DoMatch(PatternMatching.INode? other, PatternMatching.Match match) { AstNode? o = other as AstNode; // try matching if other is null, or if other is an AstNode return (other == null || o != null) && DoMatch(o, match); } bool PatternMatching.INode.DoMatchCollection(Role? role, PatternMatching.INode? pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo? backtrackingInfo) { AstNode? o = pos as AstNode; return (pos == null || o != null) && DoMatch(o, match); } PatternMatching.INode? PatternMatching.INode.NextSibling { get { return nextSibling; } } PatternMatching.INode? PatternMatching.INode.FirstChild { get { return firstChild; } } #endregion public AstNode? GetNextNode() { if (NextSibling != null) return NextSibling; if (Parent != null) return Parent.GetNextNode(); return null; } /// /// Gets the next node which fullfills a given predicate /// /// The next node. /// The predicate. public AstNode? GetNextNode(Func pred) { var next = GetNextNode(); while (next != null && !pred(next)) next = next.GetNextNode(); return next; } public AstNode? GetPrevNode() { if (PrevSibling != null) return PrevSibling; if (Parent != null) return Parent.GetPrevNode(); return null; } /// /// Gets the previous node which fullfills a given predicate /// /// The next node. /// The predicate. public AstNode? GetPrevNode(Func pred) { var prev = GetPrevNode(); while (prev != null && !pred(prev)) prev = prev.GetPrevNode(); return prev; } // filters all non c# nodes (comments, white spaces or pre processor directives) public AstNode? GetCSharpNodeBefore(AstNode node) { var n = node.PrevSibling; while (n != null) { if (n.Role != Roles.Comment) return n; n = n.GetPrevNode(); } return null; } /// /// Gets the next sibling which fullfills a given predicate /// /// The next node. /// The predicate. public AstNode? GetNextSibling(Func pred) { var next = NextSibling; while (next != null && !pred(next)) next = next.NextSibling; return next; } /// /// Gets the next sibling which fullfills a given predicate /// /// The next node. /// The predicate. public AstNode? GetPrevSibling(Func pred) { var prev = PrevSibling; while (prev != null && !pred(prev)) prev = prev.PrevSibling; return prev; } #region GetNodeAt /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// public AstNode? GetNodeAt(int line, int column, Predicate? pred = null) { return GetNodeAt(new TextLocation(line, column), pred); } /// /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// public AstNode? GetNodeAt(TextLocation location, Predicate? pred = null) { AstNode? result = null; AstNode node = this; while (node.LastChild != null) { var child = node.LastChild; while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location < child.EndLocation) { if (pred == null || pred(child)) result = child; node = child; } else { // found no better child node - therefore the parent is the right one. break; } } return result; } /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// public T? GetNodeAt(int line, int column) where T : AstNode { return GetNodeAt(new TextLocation(line, column)); } /// /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// public T? GetNodeAt(TextLocation location) where T : AstNode { T? result = null; AstNode node = this; while (node.LastChild != null) { var child = node.LastChild; while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location < child.EndLocation) { if (child is T) result = (T)child; node = child; } else { // found no better child node - therefore the parent is the right one. break; } } return result; } #endregion #region GetAdjacentNodeAt /// /// Gets the node specified by pred at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// public AstNode? GetAdjacentNodeAt(int line, int column, Predicate? pred = null) { return GetAdjacentNodeAt(new TextLocation(line, column), pred); } /// /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// public AstNode? GetAdjacentNodeAt(TextLocation location, Predicate? pred = null) { AstNode? result = null; AstNode node = this; while (node.LastChild != null) { var child = node.LastChild; while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location <= child.EndLocation) { if (pred == null || pred(child)) result = child; node = child; } else { // found no better child node - therefore the parent is the right one. break; } } return result; } /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// public T? GetAdjacentNodeAt(int line, int column) where T : AstNode { return GetAdjacentNodeAt(new TextLocation(line, column)); } /// /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// public T? GetAdjacentNodeAt(TextLocation location) where T : AstNode { T? result = null; AstNode node = this; while (node.LastChild != null) { var child = node.LastChild; while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location <= child.EndLocation) { if (child is T t) result = t; node = child; } else { // found no better child node - therefore the parent is the right one. break; } } return result; } #endregion /// /// Gets the node that fully contains the range from startLocation to endLocation. /// public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation) { for (AstNode? child = firstChild; child != null; child = child.nextSibling) { if (child.StartLocation <= startLocation && endLocation <= child.EndLocation) return child.GetNodeContaining(startLocation, endLocation); } return this; } /// /// Returns the root nodes of all subtrees that are fully contained in the specified region. /// public IEnumerable GetNodesBetween(int startLine, int startColumn, int endLine, int endColumn) { return GetNodesBetween(new TextLocation(startLine, startColumn), new TextLocation(endLine, endColumn)); } /// /// Returns the root nodes of all subtrees that are fully contained between and (inclusive). /// public IEnumerable GetNodesBetween(TextLocation start, TextLocation end) { AstNode? node = this; while (node != null) { AstNode? next; if (start <= node.StartLocation && node.EndLocation <= end) { // Remember next before yielding node. // This allows iteration to continue when the caller removes/replaces the node. next = node.GetNextNode(); yield return node; } else { if (node.EndLocation <= start) { next = node.GetNextNode(); } else { next = node.FirstChild; } } if (next != null && next.StartLocation > end) yield break; node = next; } } /// /// Gets the node as formatted C# output. /// /// /// Formatting options. /// public virtual string ToString(CSharpFormattingOptions? formattingOptions) { if (IsNull) return ""; var w = new StringWriter(); AcceptVisitor(new CSharpOutputVisitor(w, formattingOptions ?? FormattingOptionsFactory.CreateMono())); return w.ToString(); } public sealed override string ToString() { return ToString(null); } /// /// Returns true, if the given coordinates (line, column) are in the node. /// /// /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false. /// public bool Contains(int line, int column) { return Contains(new TextLocation(line, column)); } /// /// Returns true, if the given coordinates are in the node. /// /// /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false. /// public bool Contains(TextLocation location) { return this.StartLocation <= location && location < this.EndLocation; } /// /// Returns true, if the given coordinates (line, column) are in the node. /// /// /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false. /// public bool IsInside(int line, int column) { return IsInside(new TextLocation(line, column)); } /// /// Returns true, if the given coordinates are in the node. /// /// /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false. /// public bool IsInside(TextLocation location) { return this.StartLocation <= location && location <= this.EndLocation; } public override void AddAnnotation(object annotation) { if (this.IsNull) throw new InvalidOperationException("Cannot add annotations to the null node"); base.AddAnnotation(annotation); } internal string DebugToString() { if (IsNull) return "Null"; string text = ToString(); text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); if (text.Length > 100) return text.Substring(0, 97) + "..."; else return text; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents the children of an AstNode that have a specific role. /// public class AstNodeCollection : ICollection, IReadOnlyCollection where T : AstNode { readonly AstNode node; readonly Role role; public AstNodeCollection(AstNode node, Role role) { if (node == null) throw new ArgumentNullException(nameof(node)); if (role == null) throw new ArgumentNullException(nameof(role)); this.node = node; this.role = role; } public int Count { get { int count = 0; uint roleIndex = role.Index; for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) { if (cur.RoleIndex == roleIndex) count++; } return count; } } public void Add(T element) { node.AddChild(element, role); } public void AddRange(IEnumerable nodes) { // Evaluate 'nodes' first, since it might change when we add the new children // Example: collection.AddRange(collection); if (nodes != null) { foreach (T node in nodes.ToList()) Add(node); } } public void AddRange(T[] nodes) { // Fast overload for arrays - we don't need to create a copy if (nodes != null) { foreach (T node in nodes) Add(node); } } public void ReplaceWith(IEnumerable nodes) { // Evaluate 'nodes' first, since it might change when we call Clear() // Example: collection.ReplaceWith(collection); if (nodes != null) nodes = nodes.ToList(); Clear(); if (nodes != null) { foreach (T node in nodes) Add(node); } } public void MoveTo(ICollection targetCollection) { if (targetCollection == null) throw new ArgumentNullException(nameof(targetCollection)); foreach (T node in this) { node.Remove(); targetCollection.Add(node); } } public bool Contains(T element) { return element != null && element.Parent == node && element.RoleIndex == role.Index; } public bool Remove(T element) { if (Contains(element)) { element.Remove(); return true; } else { return false; } } public void CopyTo(T[] array, int arrayIndex) { foreach (T item in this) array[arrayIndex++] = item; } public void Clear() { foreach (T item in this) item.Remove(); } public IEnumerable Detach() { foreach (T item in this) yield return item.Detach(); } /// /// Returns the first element for which the predicate returns true, /// or the null node (AstNode with IsNull=true) if no such object is found. /// public T FirstOrNullObject(Func predicate = null) { foreach (T item in this) if (predicate == null || predicate(item)) return item; return role.NullObject; } /// /// Returns the last element for which the predicate returns true, /// or the null node (AstNode with IsNull=true) if no such object is found. /// public T LastOrNullObject(Func predicate = null) { T result = role.NullObject; foreach (T item in this) if (predicate == null || predicate(item)) result = item; return result; } bool ICollection.IsReadOnly { get { return false; } } public IEnumerator GetEnumerator() { uint roleIndex = role.Index; AstNode next; for (AstNode cur = node.FirstChild; cur != null; cur = next) { Debug.Assert(cur.Parent == node); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.NextSibling; if (cur.RoleIndex == roleIndex) yield return (T)cur; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #region Equals and GetHashCode implementation public override int GetHashCode() { return node.GetHashCode() ^ role.GetHashCode(); } public override bool Equals(object obj) { AstNodeCollection other = obj as AstNodeCollection; if (other == null) return false; return this.node == other.node && this.role == other.role; } #endregion internal bool DoMatch(AstNodeCollection other, Match match) { return Pattern.DoMatchCollection(role, node.FirstChild, other.node.FirstChild, match); } public void InsertAfter(T existingItem, T newItem) { node.InsertChildAfter(existingItem, newItem, role); } public void InsertBefore(T existingItem, T newItem) { node.InsertChildBefore(existingItem, newItem, role); } /// /// Applies the to all nodes in this collection. /// public void AcceptVisitor(IAstVisitor visitor) { uint roleIndex = role.Index; AstNode next; for (AstNode cur = node.FirstChild; cur != null; cur = next) { Debug.Assert(cur.Parent == node); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.NextSibling; if (cur.RoleIndex == roleIndex) cur.AcceptVisitor(visitor); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// A type reference in the C# AST. /// public abstract class AstType : AstNode { #region Null public new static readonly AstType Null = new NullAstType(); sealed class NullAstType : AstType { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator AstType(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : AstType, INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType { get { return NodeType.TypeReference; } } public new AstType Clone() { return (AstType)base.Clone(); } /// /// Gets whether this type is a SimpleType "var". /// public bool IsVar() { SimpleType st = this as SimpleType; return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; } /// /// Gets the name lookup mode from the context (looking at the ancestors of this ). /// public NameLookupMode GetNameLookupMode() { AstType outermostType = this; while (outermostType.Parent is AstType) outermostType = (AstType)outermostType.Parent; if (outermostType.Parent is UsingDeclaration || outermostType.Parent is UsingAliasDeclaration) { return NameLookupMode.TypeInUsingDeclaration; } else if (outermostType.Role == Roles.BaseType) { // Use BaseTypeReference for a type's base type, and for a constraint on a type. // Do not use it for a constraint on a method. if (outermostType.Parent is TypeDeclaration || (outermostType.Parent is Constraint && outermostType.Parent.Parent is TypeDeclaration)) return NameLookupMode.BaseTypeReference; } return NameLookupMode.Type; } /// /// Creates a pointer type from this type by nesting it in a . /// If this type already is a pointer type, this method just increases the PointerRank of the existing pointer type. /// public virtual AstType MakePointerType() { return new ComposedType { BaseType = this }.MakePointerType(); } /// /// Creates an array type from this type by nesting it in a . /// If this type already is an array type, the additional rank is prepended to the existing array specifier list. /// Thus, new SimpleType("T").MakeArrayType(1).MakeArrayType(2) will result in "T[,][]". /// public virtual AstType MakeArrayType(int rank = 1) { return new ComposedType { BaseType = this }.MakeArrayType(rank); } /// /// Creates a nullable type from this type by nesting it in a . /// public AstType MakeNullableType() { return new ComposedType { BaseType = this, HasNullableSpecifier = true }; } /// /// Creates a C# 7 ref type from this type by nesting it in a . /// public virtual AstType MakeRefType() { return new ComposedType { BaseType = this, HasRefSpecifier = true }; } /// /// Builds an expression that can be used to access a static member on this type. /// public MemberType MemberType(string memberName, params AstType[] typeArguments) { var memberType = new MemberType(this, memberName); memberType.TypeArguments.AddRange(typeArguments); return memberType; } /// /// Builds an expression that can be used to access a static member on this type. /// public MemberType MemberType(string memberName, IEnumerable typeArguments) { var memberType = new MemberType(this, memberName); memberType.TypeArguments.AddRange(typeArguments); return memberType; } /// /// Creates a simple AstType from a dotted name. /// Does not support generics, arrays, etc. - just simple dotted names, /// e.g. namespace names. /// public static AstType Create(string dottedName) { string[] parts = dottedName.Split('.'); AstType type = new SimpleType(parts[0]); for (int i = 1; i < parts.Length; i++) { type = new MemberType(type, parts[i]); } return type; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs ================================================ // // CSharpModifierToken.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Immutable; using ICSharpCode.Decompiler.CSharp.OutputVisitor; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class CSharpModifierToken : CSharpTokenNode { Modifiers modifier; public Modifiers Modifier { get { return modifier; } set { ThrowIfFrozen(); this.modifier = value; } } public override TextLocation EndLocation { get { return new TextLocation(StartLocation.Line, StartLocation.Column + GetModifierLength(Modifier)); } } public override string ToString(CSharpFormattingOptions formattingOptions) { return GetModifierName(Modifier); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CSharpModifierToken o = other as CSharpModifierToken; return o != null && this.modifier == o.modifier; } // Not worth using a dictionary for such few elements. // This table is sorted in the order that modifiers should be output when generating code. public static ImmutableArray AllModifiers { get; } = ImmutableArray.Create( Modifiers.Public, Modifiers.Private, Modifiers.Protected, Modifiers.Internal, Modifiers.New, Modifiers.Unsafe, Modifiers.Static, Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Override, Modifiers.Required, Modifiers.Readonly, Modifiers.Volatile, Modifiers.Ref, Modifiers.Extern, Modifiers.Partial, Modifiers.Const, Modifiers.Async, Modifiers.Any ); public CSharpModifierToken(TextLocation location, Modifiers modifier) : base(location, null) { this.Modifier = modifier; } public static string GetModifierName(Modifiers modifier) { switch (modifier) { case Modifiers.Private: return "private"; case Modifiers.Internal: return "internal"; case Modifiers.Protected: return "protected"; case Modifiers.Public: return "public"; case Modifiers.Abstract: return "abstract"; case Modifiers.Virtual: return "virtual"; case Modifiers.Sealed: return "sealed"; case Modifiers.Static: return "static"; case Modifiers.Override: return "override"; case Modifiers.Readonly: return "readonly"; case Modifiers.Const: return "const"; case Modifiers.New: return "new"; case Modifiers.Partial: return "partial"; case Modifiers.Extern: return "extern"; case Modifiers.Volatile: return "volatile"; case Modifiers.Unsafe: return "unsafe"; case Modifiers.Async: return "async"; case Modifiers.Ref: return "ref"; case Modifiers.Required: return "required"; case Modifiers.Any: // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST return "any"; default: throw new NotSupportedException("Invalid value for Modifiers"); } } public static int GetModifierLength(Modifiers modifier) { return GetModifierName(modifier).Length; } public static Modifiers GetModifierValue(string modifier) { switch (modifier) { case "private": return Modifiers.Private; case "internal": return Modifiers.Internal; case "protected": return Modifiers.Protected; case "public": return Modifiers.Public; case "abstract": return Modifiers.Abstract; case "virtual": return Modifiers.Virtual; case "sealed": return Modifiers.Sealed; case "static": return Modifiers.Static; case "override": return Modifiers.Override; case "readonly": return Modifiers.Readonly; case "const": return Modifiers.Const; case "new": return Modifiers.New; case "partial": return Modifiers.Partial; case "extern": return Modifiers.Extern; case "volatile": return Modifiers.Volatile; case "unsafe": return Modifiers.Unsafe; case "async": return Modifiers.Async; case "ref": return Modifiers.Ref; case "required": return Modifiers.Required; case "any": // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST return Modifiers.Any; default: throw new NotSupportedException("Invalid value for Modifiers"); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/CSharpTokenNode.cs ================================================ // // TokenNode.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.CSharp.OutputVisitor; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents a token in C#. Note that the type of the token is defined through the TokenRole. /// /// /// In all non null c# token nodes the Role of a CSharpToken must be a TokenRole. /// public class CSharpTokenNode : AstNode { public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode(); class NullCSharpTokenNode : CSharpTokenNode { public override bool IsNull { get { return true; } } public NullCSharpTokenNode() : base(TextLocation.Empty, null) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } public override NodeType NodeType { get { return NodeType.Token; } } TextLocation startLocation; public override TextLocation StartLocation { get { return startLocation; } } int TokenLength { get { uint tokenRoleIndex = (this.flags >> AstNodeFlagsUsedBits); if (Role.GetByIndex(tokenRoleIndex) is TokenRole r) { return r.Length; } return 0; } } public override TextLocation EndLocation { get { return new TextLocation(StartLocation.Line, StartLocation.Column + TokenLength); } } public CSharpTokenNode(TextLocation location, TokenRole role) { this.startLocation = location; if (role != null) this.flags |= role.Index << AstNodeFlagsUsedBits; } public override string ToString(CSharpFormattingOptions formattingOptions) { uint tokenRoleIndex = (this.flags >> AstNodeFlagsUsedBits); if (Role.GetByIndex(tokenRoleIndex) is TokenRole r) { return r.Token; } return string.Empty; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCSharpTokenNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCSharpTokenNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCSharpTokenNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CSharpTokenNode o = other as CSharpTokenNode; return o != null && !o.IsNull && !(o is CSharpModifierToken); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs ================================================ // // ComposedType.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.OutputVisitor; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ComposedType : AstType { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole RefRole = new TokenRole("ref"); public static readonly TokenRole ReadonlyRole = new TokenRole("readonly"); public static readonly TokenRole NullableRole = new TokenRole("?"); public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier", null); public AstNodeCollection Attributes { get { return base.GetChildrenByRole(AttributeRole); } } /// /// Gets/sets whether this type has a 'ref' specifier. /// This is used for C# 7 ref locals/ref return. /// Parameters use ParameterDeclaration.ParameterModifier instead. /// public bool HasRefSpecifier { get { return !GetChildByRole(RefRole).IsNull; } set { SetChildByRole(RefRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } } /// /// Gets/sets whether this type has a 'readonly' specifier. /// This is used for C# 7.2 'ref readonly' locals/ref return. /// Parameters use ParameterDeclaration.ParameterModifier instead. /// public bool HasReadOnlySpecifier { get { return !GetChildByRole(ReadonlyRole).IsNull; } set { SetChildByRole(ReadonlyRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } } public AstType BaseType { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public bool HasNullableSpecifier { get { return !GetChildByRole(NullableRole).IsNull; } set { SetChildByRole(NullableRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } } public bool HasOnlyNullableSpecifier { get { return HasNullableSpecifier && !HasRefSpecifier && !HasReadOnlySpecifier && PointerRank == 0 && ArraySpecifiers.Count == 0; } } public CSharpTokenNode NullableSpecifierToken { get { return GetChildByRole(NullableRole); } } public int PointerRank { get { return GetChildrenByRole(PointerRole).Count; } set { if (value < 0) throw new ArgumentOutOfRangeException(); int d = this.PointerRank; while (d > value) { GetChildByRole(PointerRole).Remove(); d--; } while (d < value) { InsertChildBefore(GetChildByRole(PointerRole), new CSharpTokenNode(TextLocation.Empty, PointerRole), PointerRole); d++; } } } public AstNodeCollection ArraySpecifiers { get { return GetChildrenByRole(ArraySpecifierRole); } } public AstNodeCollection PointerTokens { get { return GetChildrenByRole(PointerRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitComposedType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitComposedType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitComposedType(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ComposedType o = other as ComposedType; return o != null && this.HasNullableSpecifier == o.HasNullableSpecifier && this.PointerRank == o.PointerRank && this.HasRefSpecifier == o.HasRefSpecifier && this.HasReadOnlySpecifier == o.HasReadOnlySpecifier && this.BaseType.DoMatch(o.BaseType, match) && this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match); } public override string ToString(CSharpFormattingOptions formattingOptions) { StringBuilder b = new StringBuilder(); if (this.HasRefSpecifier) b.Append("ref "); if (this.HasReadOnlySpecifier) b.Append("readonly "); b.Append(this.BaseType.ToString()); if (this.HasNullableSpecifier) b.Append('?'); b.Append('*', this.PointerRank); foreach (var arraySpecifier in this.ArraySpecifiers) { b.Append('['); b.Append(',', arraySpecifier.Dimensions - 1); b.Append(']'); } return b.ToString(); } public override AstType MakePointerType() { if (ArraySpecifiers.Any()) { return base.MakePointerType(); } else { this.PointerRank++; return this; } } public override AstType MakeArrayType(int dimensions) { InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), ArraySpecifierRole); return this; } public override AstType MakeRefType() { this.HasRefSpecifier = true; return this; } } /// /// [,,,] /// public class ArraySpecifier : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public ArraySpecifier() { } public ArraySpecifier(int dimensions) { this.Dimensions = dimensions; } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public int Dimensions { get { return 1 + GetChildrenByRole(Roles.Comma).Count; } set { int d = this.Dimensions; while (d > value) { GetChildByRole(Roles.Comma).Remove(); d--; } while (d < value) { InsertChildBefore(GetChildByRole(Roles.Comma), new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); d++; } } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitArraySpecifier(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitArraySpecifier(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitArraySpecifier(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ArraySpecifier o = other as ArraySpecifier; return o != null && this.Dimensions == o.Dimensions; } public override string ToString(CSharpFormattingOptions formattingOptions) { return "[" + new string(',', this.Dimensions - 1) + "]"; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs ================================================ // // IAstVisitor.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// AST visitor with a default implementation that visits all node depth-first. /// public abstract class DepthFirstAstVisitor : IAstVisitor { protected virtual void VisitChildren(AstNode node) { AstNode next; for (var child = node.FirstChild; child != null; child = next) { // Store next to allow the loop to continue // if the visitor removes/replaces child. next = child.NextSibling; child.AcceptVisitor(this); } } public virtual void VisitNullNode(AstNode nullNode) { // Should we call VisitChildren here? // We usually want to ignore null nodes. // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. } public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) { VisitChildren(syntaxTree); } public virtual void VisitComment(Comment comment) { VisitChildren(comment); } public virtual void VisitDocumentationReference(DocumentationReference documentationReference) { VisitChildren(documentationReference); } public virtual void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) { VisitChildren(preProcessorDirective); } public virtual void VisitIdentifier(Identifier identifier) { VisitChildren(identifier); } public virtual void VisitCSharpTokenNode(CSharpTokenNode token) { VisitChildren(token); } public virtual void VisitPrimitiveType(PrimitiveType primitiveType) { VisitChildren(primitiveType); } public virtual void VisitComposedType(ComposedType composedType) { VisitChildren(composedType); } public virtual void VisitSimpleType(SimpleType simpleType) { VisitChildren(simpleType); } public virtual void VisitMemberType(MemberType memberType) { VisitChildren(memberType); } public virtual void VisitTupleType(TupleAstType tupleType) { VisitChildren(tupleType); } public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement) { VisitChildren(tupleTypeElement); } public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType) { VisitChildren(functionPointerType); } public virtual void VisitInvocationType(InvocationAstType invocationType) { VisitChildren(invocationType); } public virtual void VisitAttribute(Attribute attribute) { VisitChildren(attribute); } public virtual void VisitAttributeSection(AttributeSection attributeSection) { VisitChildren(attributeSection); } public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { VisitChildren(delegateDeclaration); } public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { VisitChildren(namespaceDeclaration); } public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { VisitChildren(typeDeclaration); } public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) { VisitChildren(typeParameterDeclaration); } public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { VisitChildren(enumMemberDeclaration); } public virtual void VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration) { VisitChildren(extensionDeclaration); } public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { VisitChildren(usingDeclaration); } public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) { VisitChildren(usingDeclaration); } public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { VisitChildren(externAliasDeclaration); } public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { VisitChildren(constructorDeclaration); } public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) { VisitChildren(constructorInitializer); } public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { VisitChildren(destructorDeclaration); } public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration) { VisitChildren(eventDeclaration); } public virtual void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { VisitChildren(eventDeclaration); } public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { VisitChildren(fieldDeclaration); } public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { VisitChildren(fixedFieldDeclaration); } public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { VisitChildren(fixedVariableInitializer); } public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { VisitChildren(indexerDeclaration); } public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { VisitChildren(methodDeclaration); } public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { VisitChildren(operatorDeclaration); } public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { VisitChildren(propertyDeclaration); } public virtual void VisitAccessor(Accessor accessor) { VisitChildren(accessor); } public virtual void VisitVariableInitializer(VariableInitializer variableInitializer) { VisitChildren(variableInitializer); } public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { VisitChildren(parameterDeclaration); } public virtual void VisitConstraint(Constraint constraint) { VisitChildren(constraint); } public virtual void VisitBlockStatement(BlockStatement blockStatement) { VisitChildren(blockStatement); } public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) { VisitChildren(expressionStatement); } public virtual void VisitBreakStatement(BreakStatement breakStatement) { VisitChildren(breakStatement); } public virtual void VisitCheckedStatement(CheckedStatement checkedStatement) { VisitChildren(checkedStatement); } public virtual void VisitContinueStatement(ContinueStatement continueStatement) { VisitChildren(continueStatement); } public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { VisitChildren(doWhileStatement); } public virtual void VisitEmptyStatement(EmptyStatement emptyStatement) { VisitChildren(emptyStatement); } public virtual void VisitFixedStatement(FixedStatement fixedStatement) { VisitChildren(fixedStatement); } public virtual void VisitForeachStatement(ForeachStatement foreachStatement) { VisitChildren(foreachStatement); } public virtual void VisitForStatement(ForStatement forStatement) { VisitChildren(forStatement); } public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) { VisitChildren(gotoCaseStatement); } public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) { VisitChildren(gotoDefaultStatement); } public virtual void VisitGotoStatement(GotoStatement gotoStatement) { VisitChildren(gotoStatement); } public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement) { VisitChildren(ifElseStatement); } public virtual void VisitLabelStatement(LabelStatement labelStatement) { VisitChildren(labelStatement); } public virtual void VisitLockStatement(LockStatement lockStatement) { VisitChildren(lockStatement); } public virtual void VisitReturnStatement(ReturnStatement returnStatement) { VisitChildren(returnStatement); } public virtual void VisitSwitchStatement(SwitchStatement switchStatement) { VisitChildren(switchStatement); } public virtual void VisitSwitchSection(SwitchSection switchSection) { VisitChildren(switchSection); } public virtual void VisitCaseLabel(CaseLabel caseLabel) { VisitChildren(caseLabel); } public virtual void VisitSwitchExpression(SwitchExpression switchExpression) { VisitChildren(switchExpression); } public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection) { VisitChildren(switchExpressionSection); } public virtual void VisitThrowStatement(ThrowStatement throwStatement) { VisitChildren(throwStatement); } public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { VisitChildren(tryCatchStatement); } public virtual void VisitCatchClause(CatchClause catchClause) { VisitChildren(catchClause); } public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { VisitChildren(uncheckedStatement); } public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement) { VisitChildren(unsafeStatement); } public virtual void VisitUsingStatement(UsingStatement usingStatement) { VisitChildren(usingStatement); } public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { VisitChildren(variableDeclarationStatement); } public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) { VisitChildren(localFunctionDeclarationStatement); } public virtual void VisitWhileStatement(WhileStatement whileStatement) { VisitChildren(whileStatement); } public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { VisitChildren(yieldBreakStatement); } public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) { VisitChildren(yieldReturnStatement); } public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { VisitChildren(anonymousMethodExpression); } public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression) { VisitChildren(lambdaExpression); } public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { VisitChildren(assignmentExpression); } public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) { VisitChildren(baseReferenceExpression); } public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { VisitChildren(binaryOperatorExpression); } public virtual void VisitCastExpression(CastExpression castExpression) { VisitChildren(castExpression); } public virtual void VisitCheckedExpression(CheckedExpression checkedExpression) { VisitChildren(checkedExpression); } public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) { VisitChildren(conditionalExpression); } public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression) { VisitChildren(identifierExpression); } public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) { VisitChildren(indexerExpression); } public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression) { VisitChildren(interpolatedStringExpression); } public virtual void VisitInterpolation(Interpolation interpolation) { VisitChildren(interpolation); } public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText) { VisitChildren(interpolatedStringText); } public virtual void VisitInvocationExpression(InvocationExpression invocationExpression) { VisitChildren(invocationExpression); } public virtual void VisitDirectionExpression(DirectionExpression directionExpression) { VisitChildren(directionExpression); } public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { VisitChildren(memberReferenceExpression); } public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { VisitChildren(nullReferenceExpression); } public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { VisitChildren(objectCreateExpression); } public virtual void VisitDeclarationExpression(DeclarationExpression declarationExpression) { VisitChildren(declarationExpression); } public virtual void VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression) { VisitChildren(recursivePatternExpression); } public virtual void VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression) { VisitChildren(outVarDeclarationExpression); } public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) { VisitChildren(anonymousTypeCreateExpression); } public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { VisitChildren(arrayCreateExpression); } public virtual void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { VisitChildren(parenthesizedExpression); } public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { VisitChildren(pointerReferenceExpression); } public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { VisitChildren(primitiveExpression); } public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { VisitChildren(sizeOfExpression); } public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { VisitChildren(stackAllocExpression); } public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) { VisitChildren(thisReferenceExpression); } public virtual void VisitThrowExpression(ThrowExpression throwExpression) { VisitChildren(throwExpression); } public virtual void VisitTupleExpression(TupleExpression tupleExpression) { VisitChildren(tupleExpression); } public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression) { VisitChildren(typeOfExpression); } public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { VisitChildren(typeReferenceExpression); } public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { VisitChildren(unaryOperatorExpression); } public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { VisitChildren(uncheckedExpression); } public virtual void VisitQueryExpression(QueryExpression queryExpression) { VisitChildren(queryExpression); } public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { VisitChildren(queryContinuationClause); } public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) { VisitChildren(queryFromClause); } public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) { VisitChildren(queryLetClause); } public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { VisitChildren(queryWhereClause); } public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { VisitChildren(queryJoinClause); } public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) { VisitChildren(queryOrderClause); } public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) { VisitChildren(queryOrdering); } public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) { VisitChildren(querySelectClause); } public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { VisitChildren(queryGroupClause); } public virtual void VisitAsExpression(AsExpression asExpression) { VisitChildren(asExpression); } public virtual void VisitIsExpression(IsExpression isExpression) { VisitChildren(isExpression); } public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { VisitChildren(defaultValueExpression); } public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) { VisitChildren(undocumentedExpression); } public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { VisitChildren(arrayInitializerExpression); } public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier) { VisitChildren(arraySpecifier); } public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { VisitChildren(namedArgumentExpression); } public virtual void VisitNamedExpression(NamedExpression namedExpression) { VisitChildren(namedExpression); } public virtual void VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation) { VisitChildren(singleVariableDesignation); } public virtual void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation) { VisitChildren(parenthesizedVariableDesignation); } public virtual void VisitErrorNode(AstNode errorNode) { VisitChildren(errorNode); } public virtual void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) { VisitChildren(placeholder); } public virtual void VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression) { VisitChildren(withInitializerExpression); } } /// /// AST visitor with a default implementation that visits all node depth-first. /// public abstract class DepthFirstAstVisitor : IAstVisitor { protected virtual T VisitChildren(AstNode node) { AstNode next; for (var child = node.FirstChild; child != null; child = next) { // Store next to allow the loop to continue // if the visitor removes/replaces child. next = child.NextSibling; child.AcceptVisitor(this); } return default(T); } public virtual T VisitNullNode(AstNode nullNode) { // Should we call VisitChildren here? // We usually want to ignore null nodes. // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. return default(T); } public virtual T VisitSyntaxTree(SyntaxTree unit) { return VisitChildren(unit); } public virtual T VisitComment(Comment comment) { return VisitChildren(comment); } public virtual T VisitDocumentationReference(DocumentationReference documentationReference) { return VisitChildren(documentationReference); } public virtual T VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) { return VisitChildren(preProcessorDirective); } public virtual T VisitIdentifier(Identifier identifier) { return VisitChildren(identifier); } public virtual T VisitCSharpTokenNode(CSharpTokenNode token) { return VisitChildren(token); } public virtual T VisitPrimitiveType(PrimitiveType primitiveType) { return VisitChildren(primitiveType); } public virtual T VisitComposedType(ComposedType composedType) { return VisitChildren(composedType); } public virtual T VisitSimpleType(SimpleType simpleType) { return VisitChildren(simpleType); } public virtual T VisitMemberType(MemberType memberType) { return VisitChildren(memberType); } public virtual T VisitTupleType(TupleAstType tupleType) { return VisitChildren(tupleType); } public virtual T VisitTupleTypeElement(TupleTypeElement tupleTypeElement) { return VisitChildren(tupleTypeElement); } public virtual T VisitFunctionPointerType(FunctionPointerAstType functionPointerType) { return VisitChildren(functionPointerType); } public virtual T VisitInvocationType(InvocationAstType invocationType) { return VisitChildren(invocationType); } public virtual T VisitAttribute(Attribute attribute) { return VisitChildren(attribute); } public virtual T VisitAttributeSection(AttributeSection attributeSection) { return VisitChildren(attributeSection); } public virtual T VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { return VisitChildren(delegateDeclaration); } public virtual T VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { return VisitChildren(namespaceDeclaration); } public virtual T VisitTypeDeclaration(TypeDeclaration typeDeclaration) { return VisitChildren(typeDeclaration); } public virtual T VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) { return VisitChildren(typeParameterDeclaration); } public virtual T VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { return VisitChildren(enumMemberDeclaration); } public virtual T VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration) { return VisitChildren(extensionDeclaration); } public virtual T VisitUsingDeclaration(UsingDeclaration usingDeclaration) { return VisitChildren(usingDeclaration); } public virtual T VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) { return VisitChildren(usingDeclaration); } public virtual T VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { return VisitChildren(externAliasDeclaration); } public virtual T VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { return VisitChildren(constructorDeclaration); } public virtual T VisitConstructorInitializer(ConstructorInitializer constructorInitializer) { return VisitChildren(constructorInitializer); } public virtual T VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { return VisitChildren(destructorDeclaration); } public virtual T VisitEventDeclaration(EventDeclaration eventDeclaration) { return VisitChildren(eventDeclaration); } public virtual T VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { return VisitChildren(eventDeclaration); } public virtual T VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { return VisitChildren(fieldDeclaration); } public virtual T VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { return VisitChildren(fixedFieldDeclaration); } public virtual T VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { return VisitChildren(fixedVariableInitializer); } public virtual T VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { return VisitChildren(indexerDeclaration); } public virtual T VisitMethodDeclaration(MethodDeclaration methodDeclaration) { return VisitChildren(methodDeclaration); } public virtual T VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { return VisitChildren(operatorDeclaration); } public virtual T VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { return VisitChildren(propertyDeclaration); } public virtual T VisitAccessor(Accessor accessor) { return VisitChildren(accessor); } public virtual T VisitVariableInitializer(VariableInitializer variableInitializer) { return VisitChildren(variableInitializer); } public virtual T VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { return VisitChildren(parameterDeclaration); } public virtual T VisitConstraint(Constraint constraint) { return VisitChildren(constraint); } public virtual T VisitBlockStatement(BlockStatement blockStatement) { return VisitChildren(blockStatement); } public virtual T VisitExpressionStatement(ExpressionStatement expressionStatement) { return VisitChildren(expressionStatement); } public virtual T VisitBreakStatement(BreakStatement breakStatement) { return VisitChildren(breakStatement); } public virtual T VisitCheckedStatement(CheckedStatement checkedStatement) { return VisitChildren(checkedStatement); } public virtual T VisitContinueStatement(ContinueStatement continueStatement) { return VisitChildren(continueStatement); } public virtual T VisitDoWhileStatement(DoWhileStatement doWhileStatement) { return VisitChildren(doWhileStatement); } public virtual T VisitEmptyStatement(EmptyStatement emptyStatement) { return VisitChildren(emptyStatement); } public virtual T VisitFixedStatement(FixedStatement fixedStatement) { return VisitChildren(fixedStatement); } public virtual T VisitForeachStatement(ForeachStatement foreachStatement) { return VisitChildren(foreachStatement); } public virtual T VisitForStatement(ForStatement forStatement) { return VisitChildren(forStatement); } public virtual T VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) { return VisitChildren(gotoCaseStatement); } public virtual T VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) { return VisitChildren(gotoDefaultStatement); } public virtual T VisitGotoStatement(GotoStatement gotoStatement) { return VisitChildren(gotoStatement); } public virtual T VisitIfElseStatement(IfElseStatement ifElseStatement) { return VisitChildren(ifElseStatement); } public virtual T VisitLabelStatement(LabelStatement labelStatement) { return VisitChildren(labelStatement); } public virtual T VisitLockStatement(LockStatement lockStatement) { return VisitChildren(lockStatement); } public virtual T VisitReturnStatement(ReturnStatement returnStatement) { return VisitChildren(returnStatement); } public virtual T VisitSwitchStatement(SwitchStatement switchStatement) { return VisitChildren(switchStatement); } public virtual T VisitSwitchSection(SwitchSection switchSection) { return VisitChildren(switchSection); } public virtual T VisitCaseLabel(CaseLabel caseLabel) { return VisitChildren(caseLabel); } public virtual T VisitSwitchExpression(SwitchExpression switchExpression) { return VisitChildren(switchExpression); } public virtual T VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection) { return VisitChildren(switchExpressionSection); } public virtual T VisitThrowStatement(ThrowStatement throwStatement) { return VisitChildren(throwStatement); } public virtual T VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { return VisitChildren(tryCatchStatement); } public virtual T VisitCatchClause(CatchClause catchClause) { return VisitChildren(catchClause); } public virtual T VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { return VisitChildren(uncheckedStatement); } public virtual T VisitUnsafeStatement(UnsafeStatement unsafeStatement) { return VisitChildren(unsafeStatement); } public virtual T VisitUsingStatement(UsingStatement usingStatement) { return VisitChildren(usingStatement); } public virtual T VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { return VisitChildren(variableDeclarationStatement); } public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) { return VisitChildren(localFunctionDeclarationStatement); } public virtual T VisitWhileStatement(WhileStatement whileStatement) { return VisitChildren(whileStatement); } public virtual T VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { return VisitChildren(yieldBreakStatement); } public virtual T VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) { return VisitChildren(yieldReturnStatement); } public virtual T VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { return VisitChildren(anonymousMethodExpression); } public virtual T VisitLambdaExpression(LambdaExpression lambdaExpression) { return VisitChildren(lambdaExpression); } public virtual T VisitAssignmentExpression(AssignmentExpression assignmentExpression) { return VisitChildren(assignmentExpression); } public virtual T VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) { return VisitChildren(baseReferenceExpression); } public virtual T VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { return VisitChildren(binaryOperatorExpression); } public virtual T VisitCastExpression(CastExpression castExpression) { return VisitChildren(castExpression); } public virtual T VisitCheckedExpression(CheckedExpression checkedExpression) { return VisitChildren(checkedExpression); } public virtual T VisitConditionalExpression(ConditionalExpression conditionalExpression) { return VisitChildren(conditionalExpression); } public virtual T VisitIdentifierExpression(IdentifierExpression identifierExpression) { return VisitChildren(identifierExpression); } public virtual T VisitIndexerExpression(IndexerExpression indexerExpression) { return VisitChildren(indexerExpression); } public virtual T VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression) { return VisitChildren(interpolatedStringExpression); } public virtual T VisitInterpolation(Interpolation interpolation) { return VisitChildren(interpolation); } public virtual T VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText) { return VisitChildren(interpolatedStringText); } public virtual T VisitInvocationExpression(InvocationExpression invocationExpression) { return VisitChildren(invocationExpression); } public virtual T VisitDirectionExpression(DirectionExpression directionExpression) { return VisitChildren(directionExpression); } public virtual T VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { return VisitChildren(memberReferenceExpression); } public virtual T VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { return VisitChildren(nullReferenceExpression); } public virtual T VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { return VisitChildren(objectCreateExpression); } public virtual T VisitDeclarationExpression(DeclarationExpression declarationExpression) { return VisitChildren(declarationExpression); } public virtual T VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression) { return VisitChildren(recursivePatternExpression); } public virtual T VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression) { return VisitChildren(outVarDeclarationExpression); } public virtual T VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) { return VisitChildren(anonymousTypeCreateExpression); } public virtual T VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { return VisitChildren(arrayCreateExpression); } public virtual T VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { return VisitChildren(parenthesizedExpression); } public virtual T VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { return VisitChildren(pointerReferenceExpression); } public virtual T VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { return VisitChildren(primitiveExpression); } public virtual T VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { return VisitChildren(sizeOfExpression); } public virtual T VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { return VisitChildren(stackAllocExpression); } public virtual T VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) { return VisitChildren(thisReferenceExpression); } public virtual T VisitThrowExpression(ThrowExpression throwExpression) { return VisitChildren(throwExpression); } public virtual T VisitTupleExpression(TupleExpression tupleExpression) { return VisitChildren(tupleExpression); } public virtual T VisitTypeOfExpression(TypeOfExpression typeOfExpression) { return VisitChildren(typeOfExpression); } public virtual T VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { return VisitChildren(typeReferenceExpression); } public virtual T VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { return VisitChildren(unaryOperatorExpression); } public virtual T VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { return VisitChildren(uncheckedExpression); } public virtual T VisitQueryExpression(QueryExpression queryExpression) { return VisitChildren(queryExpression); } public virtual T VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { return VisitChildren(queryContinuationClause); } public virtual T VisitQueryFromClause(QueryFromClause queryFromClause) { return VisitChildren(queryFromClause); } public virtual T VisitQueryLetClause(QueryLetClause queryLetClause) { return VisitChildren(queryLetClause); } public virtual T VisitQueryWhereClause(QueryWhereClause queryWhereClause) { return VisitChildren(queryWhereClause); } public virtual T VisitQueryJoinClause(QueryJoinClause queryJoinClause) { return VisitChildren(queryJoinClause); } public virtual T VisitQueryOrderClause(QueryOrderClause queryOrderClause) { return VisitChildren(queryOrderClause); } public virtual T VisitQueryOrdering(QueryOrdering queryOrdering) { return VisitChildren(queryOrdering); } public virtual T VisitQuerySelectClause(QuerySelectClause querySelectClause) { return VisitChildren(querySelectClause); } public virtual T VisitQueryGroupClause(QueryGroupClause queryGroupClause) { return VisitChildren(queryGroupClause); } public virtual T VisitAsExpression(AsExpression asExpression) { return VisitChildren(asExpression); } public virtual T VisitIsExpression(IsExpression isExpression) { return VisitChildren(isExpression); } public virtual T VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { return VisitChildren(defaultValueExpression); } public virtual T VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) { return VisitChildren(undocumentedExpression); } public virtual T VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { return VisitChildren(arrayInitializerExpression); } public virtual T VisitArraySpecifier(ArraySpecifier arraySpecifier) { return VisitChildren(arraySpecifier); } public virtual T VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { return VisitChildren(namedArgumentExpression); } public virtual T VisitNamedExpression(NamedExpression namedExpression) { return VisitChildren(namedExpression); } public virtual T VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation) { return VisitChildren(singleVariableDesignation); } public virtual T VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation) { return VisitChildren(parenthesizedVariableDesignation); } public virtual T VisitErrorNode(AstNode errorNode) { return VisitChildren(errorNode); } public virtual T VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) { return VisitChildren(placeholder); } public virtual T VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression) { return VisitChildren(withInitializerExpression); } } /// /// AST visitor with a default implementation that visits all node depth-first. /// public abstract class DepthFirstAstVisitor : IAstVisitor { protected virtual S VisitChildren(AstNode node, T data) { AstNode next; for (var child = node.FirstChild; child != null; child = next) { // Store next to allow the loop to continue // if the visitor removes/replaces child. next = child.NextSibling; child.AcceptVisitor(this, data); } return default(S); } public virtual S VisitNullNode(AstNode nullNode, T data) { // Should we call VisitChildren here? // We usually want to ignore null nodes. // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. return default(S); } public virtual S VisitSyntaxTree(SyntaxTree unit, T data) { return VisitChildren(unit, data); } public virtual S VisitComment(Comment comment, T data) { return VisitChildren(comment, data); } public virtual S VisitDocumentationReference(DocumentationReference documentationReference, T data) { return VisitChildren(documentationReference, data); } public virtual S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective, T data) { return VisitChildren(preProcessorDirective, data); } public virtual S VisitIdentifier(Identifier identifier, T data) { return VisitChildren(identifier, data); } public virtual S VisitCSharpTokenNode(CSharpTokenNode token, T data) { return VisitChildren(token, data); } public virtual S VisitPrimitiveType(PrimitiveType primitiveType, T data) { return VisitChildren(primitiveType, data); } public virtual S VisitComposedType(ComposedType composedType, T data) { return VisitChildren(composedType, data); } public virtual S VisitSimpleType(SimpleType simpleType, T data) { return VisitChildren(simpleType, data); } public virtual S VisitMemberType(MemberType memberType, T data) { return VisitChildren(memberType, data); } public virtual S VisitTupleType(TupleAstType tupleType, T data) { return VisitChildren(tupleType, data); } public virtual S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data) { return VisitChildren(tupleTypeElement, data); } public virtual S VisitFunctionPointerType(FunctionPointerAstType functionPointerType, T data) { return VisitChildren(functionPointerType, data); } public virtual S VisitInvocationType(InvocationAstType invocationType, T data) { return VisitChildren(invocationType, data); } public virtual S VisitAttribute(Attribute attribute, T data) { return VisitChildren(attribute, data); } public virtual S VisitAttributeSection(AttributeSection attributeSection, T data) { return VisitChildren(attributeSection, data); } public virtual S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data) { return VisitChildren(delegateDeclaration, data); } public virtual S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data) { return VisitChildren(namespaceDeclaration, data); } public virtual S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data) { return VisitChildren(typeDeclaration, data); } public virtual S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data) { return VisitChildren(typeParameterDeclaration, data); } public virtual S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data) { return VisitChildren(enumMemberDeclaration, data); } public virtual S VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration, T data) { return VisitChildren(extensionDeclaration, data); } public virtual S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data) { return VisitChildren(usingDeclaration, data); } public virtual S VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration, T data) { return VisitChildren(usingDeclaration, data); } public virtual S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data) { return VisitChildren(externAliasDeclaration, data); } public virtual S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data) { return VisitChildren(constructorDeclaration, data); } public virtual S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data) { return VisitChildren(constructorInitializer, data); } public virtual S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data) { return VisitChildren(destructorDeclaration, data); } public virtual S VisitEventDeclaration(EventDeclaration eventDeclaration, T data) { return VisitChildren(eventDeclaration, data); } public virtual S VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, T data) { return VisitChildren(eventDeclaration, data); } public virtual S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data) { return VisitChildren(fieldDeclaration, data); } public virtual S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data) { return VisitChildren(fixedFieldDeclaration, data); } public virtual S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data) { return VisitChildren(fixedVariableInitializer, data); } public virtual S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data) { return VisitChildren(indexerDeclaration, data); } public virtual S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data) { return VisitChildren(methodDeclaration, data); } public virtual S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data) { return VisitChildren(operatorDeclaration, data); } public virtual S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data) { return VisitChildren(propertyDeclaration, data); } public virtual S VisitAccessor(Accessor accessor, T data) { return VisitChildren(accessor, data); } public virtual S VisitVariableInitializer(VariableInitializer variableInitializer, T data) { return VisitChildren(variableInitializer, data); } public virtual S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data) { return VisitChildren(parameterDeclaration, data); } public virtual S VisitConstraint(Constraint constraint, T data) { return VisitChildren(constraint, data); } public virtual S VisitBlockStatement(BlockStatement blockStatement, T data) { return VisitChildren(blockStatement, data); } public virtual S VisitExpressionStatement(ExpressionStatement expressionStatement, T data) { return VisitChildren(expressionStatement, data); } public virtual S VisitBreakStatement(BreakStatement breakStatement, T data) { return VisitChildren(breakStatement, data); } public virtual S VisitCheckedStatement(CheckedStatement checkedStatement, T data) { return VisitChildren(checkedStatement, data); } public virtual S VisitContinueStatement(ContinueStatement continueStatement, T data) { return VisitChildren(continueStatement, data); } public virtual S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data) { return VisitChildren(doWhileStatement, data); } public virtual S VisitEmptyStatement(EmptyStatement emptyStatement, T data) { return VisitChildren(emptyStatement, data); } public virtual S VisitFixedStatement(FixedStatement fixedStatement, T data) { return VisitChildren(fixedStatement, data); } public virtual S VisitForeachStatement(ForeachStatement foreachStatement, T data) { return VisitChildren(foreachStatement, data); } public virtual S VisitForStatement(ForStatement forStatement, T data) { return VisitChildren(forStatement, data); } public virtual S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data) { return VisitChildren(gotoCaseStatement, data); } public virtual S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data) { return VisitChildren(gotoDefaultStatement, data); } public virtual S VisitGotoStatement(GotoStatement gotoStatement, T data) { return VisitChildren(gotoStatement, data); } public virtual S VisitIfElseStatement(IfElseStatement ifElseStatement, T data) { return VisitChildren(ifElseStatement, data); } public virtual S VisitLabelStatement(LabelStatement labelStatement, T data) { return VisitChildren(labelStatement, data); } public virtual S VisitLockStatement(LockStatement lockStatement, T data) { return VisitChildren(lockStatement, data); } public virtual S VisitReturnStatement(ReturnStatement returnStatement, T data) { return VisitChildren(returnStatement, data); } public virtual S VisitSwitchStatement(SwitchStatement switchStatement, T data) { return VisitChildren(switchStatement, data); } public virtual S VisitSwitchSection(SwitchSection switchSection, T data) { return VisitChildren(switchSection, data); } public virtual S VisitCaseLabel(CaseLabel caseLabel, T data) { return VisitChildren(caseLabel, data); } public virtual S VisitSwitchExpression(SwitchExpression switchExpression, T data) { return VisitChildren(switchExpression, data); } public virtual S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection, T data) { return VisitChildren(switchExpressionSection, data); } public virtual S VisitThrowStatement(ThrowStatement throwStatement, T data) { return VisitChildren(throwStatement, data); } public virtual S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data) { return VisitChildren(tryCatchStatement, data); } public virtual S VisitCatchClause(CatchClause catchClause, T data) { return VisitChildren(catchClause, data); } public virtual S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data) { return VisitChildren(uncheckedStatement, data); } public virtual S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data) { return VisitChildren(unsafeStatement, data); } public virtual S VisitUsingStatement(UsingStatement usingStatement, T data) { return VisitChildren(usingStatement, data); } public virtual S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data) { return VisitChildren(variableDeclarationStatement, data); } public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data) { return VisitChildren(localFunctionDeclarationStatement, data); } public virtual S VisitWhileStatement(WhileStatement whileStatement, T data) { return VisitChildren(whileStatement, data); } public virtual S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data) { return VisitChildren(yieldBreakStatement, data); } public virtual S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data) { return VisitChildren(yieldReturnStatement, data); } public virtual S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data) { return VisitChildren(anonymousMethodExpression, data); } public virtual S VisitLambdaExpression(LambdaExpression lambdaExpression, T data) { return VisitChildren(lambdaExpression, data); } public virtual S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data) { return VisitChildren(assignmentExpression, data); } public virtual S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data) { return VisitChildren(baseReferenceExpression, data); } public virtual S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data) { return VisitChildren(binaryOperatorExpression, data); } public virtual S VisitCastExpression(CastExpression castExpression, T data) { return VisitChildren(castExpression, data); } public virtual S VisitCheckedExpression(CheckedExpression checkedExpression, T data) { return VisitChildren(checkedExpression, data); } public virtual S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data) { return VisitChildren(conditionalExpression, data); } public virtual S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data) { return VisitChildren(identifierExpression, data); } public virtual S VisitIndexerExpression(IndexerExpression indexerExpression, T data) { return VisitChildren(indexerExpression, data); } public virtual S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression, T data) { return VisitChildren(interpolatedStringExpression, data); } public virtual S VisitInterpolation(Interpolation interpolation, T data) { return VisitChildren(interpolation, data); } public virtual S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText, T data) { return VisitChildren(interpolatedStringText, data); } public virtual S VisitInvocationExpression(InvocationExpression invocationExpression, T data) { return VisitChildren(invocationExpression, data); } public virtual S VisitDirectionExpression(DirectionExpression directionExpression, T data) { return VisitChildren(directionExpression, data); } public virtual S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data) { return VisitChildren(memberReferenceExpression, data); } public virtual S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data) { return VisitChildren(nullReferenceExpression, data); } public virtual S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data) { return VisitChildren(objectCreateExpression, data); } public virtual S VisitDeclarationExpression(DeclarationExpression declarationExpression, T data) { return VisitChildren(declarationExpression, data); } public virtual S VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression, T data) { return VisitChildren(recursivePatternExpression, data); } public virtual S VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression, T data) { return VisitChildren(outVarDeclarationExpression, data); } public virtual S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data) { return VisitChildren(anonymousTypeCreateExpression, data); } public virtual S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data) { return VisitChildren(arrayCreateExpression, data); } public virtual S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data) { return VisitChildren(parenthesizedExpression, data); } public virtual S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data) { return VisitChildren(pointerReferenceExpression, data); } public virtual S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data) { return VisitChildren(primitiveExpression, data); } public virtual S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data) { return VisitChildren(sizeOfExpression, data); } public virtual S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data) { return VisitChildren(stackAllocExpression, data); } public virtual S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data) { return VisitChildren(thisReferenceExpression, data); } public virtual S VisitThrowExpression(ThrowExpression throwExpression, T data) { return VisitChildren(throwExpression, data); } public virtual S VisitTupleExpression(TupleExpression tupleExpression, T data) { return VisitChildren(tupleExpression, data); } public virtual S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data) { return VisitChildren(typeOfExpression, data); } public virtual S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data) { return VisitChildren(typeReferenceExpression, data); } public virtual S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data) { return VisitChildren(unaryOperatorExpression, data); } public virtual S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data) { return VisitChildren(uncheckedExpression, data); } public virtual S VisitQueryExpression(QueryExpression queryExpression, T data) { return VisitChildren(queryExpression, data); } public virtual S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data) { return VisitChildren(queryContinuationClause, data); } public virtual S VisitQueryFromClause(QueryFromClause queryFromClause, T data) { return VisitChildren(queryFromClause, data); } public virtual S VisitQueryLetClause(QueryLetClause queryLetClause, T data) { return VisitChildren(queryLetClause, data); } public virtual S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data) { return VisitChildren(queryWhereClause, data); } public virtual S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data) { return VisitChildren(queryJoinClause, data); } public virtual S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data) { return VisitChildren(queryOrderClause, data); } public virtual S VisitQueryOrdering(QueryOrdering queryOrdering, T data) { return VisitChildren(queryOrdering, data); } public virtual S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data) { return VisitChildren(querySelectClause, data); } public virtual S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data) { return VisitChildren(queryGroupClause, data); } public virtual S VisitAsExpression(AsExpression asExpression, T data) { return VisitChildren(asExpression, data); } public virtual S VisitIsExpression(IsExpression isExpression, T data) { return VisitChildren(isExpression, data); } public virtual S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data) { return VisitChildren(defaultValueExpression, data); } public virtual S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data) { return VisitChildren(undocumentedExpression, data); } public virtual S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data) { return VisitChildren(arrayInitializerExpression, data); } public virtual S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data) { return VisitChildren(arraySpecifier, data); } public virtual S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data) { return VisitChildren(namedArgumentExpression, data); } public virtual S VisitNamedExpression(NamedExpression namedExpression, T data) { return VisitChildren(namedExpression, data); } public virtual S VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation, T data) { return VisitChildren(singleVariableDesignation, data); } public virtual S VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation, T data) { return VisitChildren(parenthesizedVariableDesignation, data); } public virtual S VisitErrorNode(AstNode errorNode, T data) { return VisitChildren(errorNode, data); } public virtual S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data) { return VisitChildren(placeholder, data); } public virtual S VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression, T data) { return VisitChildren(withInitializerExpression, data); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/DocumentationReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents a 'cref' reference in XML documentation. /// public class DocumentationReference : AstNode { public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); SymbolKind symbolKind; OperatorType operatorType; bool hasParameterList; /// /// Gets/Sets the entity type. /// Possible values are: /// SymbolKind.Operator for operators, /// SymbolKind.Indexer for indexers, /// SymbolKind.TypeDefinition for references to primitive types, /// and SymbolKind.None for everything else. /// public SymbolKind SymbolKind { get { return symbolKind; } set { ThrowIfFrozen(); symbolKind = value; } } /// /// Gets/Sets the operator type. /// This property is only used when SymbolKind==Operator. /// public OperatorType OperatorType { get { return operatorType; } set { ThrowIfFrozen(); operatorType = value; } } /// /// Gets/Sets whether a parameter list was provided. /// public bool HasParameterList { get { return hasParameterList; } set { ThrowIfFrozen(); hasParameterList = value; } } public override NodeType NodeType { get { return NodeType.Unknown; } } /// /// Gets/Sets the declaring type. /// public AstType DeclaringType { get { return GetChildByRole(DeclaringTypeRole); } set { SetChildByRole(DeclaringTypeRole, value); } } /// /// Gets/sets the member name. /// This property is only used when SymbolKind==None. /// public string MemberName { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } /// /// Gets/Sets the return type of conversion operators. /// This property is only used when SymbolKind==Operator and OperatorType is explicit or implicit. /// public AstType ConversionOperatorReturnType { get { return GetChildByRole(ConversionOperatorReturnTypeRole); } set { SetChildByRole(ConversionOperatorReturnTypeRole, value); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DocumentationReference o = other as DocumentationReference; if (!(o != null && this.SymbolKind == o.SymbolKind && this.HasParameterList == o.HasParameterList)) return false; if (this.SymbolKind == SymbolKind.Operator) { if (this.OperatorType != o.OperatorType) return false; if (this.OperatorType == OperatorType.Implicit || this.OperatorType == OperatorType.Explicit) { if (!this.ConversionOperatorReturnType.DoMatch(o.ConversionOperatorReturnType, match)) return false; } } else if (this.SymbolKind == SymbolKind.None) { if (!MatchString(this.MemberName, o.MemberName)) return false; if (!this.TypeArguments.DoMatch(o.TypeArguments, match)) return false; } return this.Parameters.DoMatch(o.Parameters, match); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDocumentationReference(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDocumentationReference(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDocumentationReference(this, data); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AnonymousMethodExpression.cs ================================================ // // AnonymousMethodExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// [async] delegate(Parameters) {Body} /// public class AnonymousMethodExpression : Expression { public readonly static TokenRole DelegateKeywordRole = new TokenRole("delegate"); public readonly static TokenRole AsyncModifierRole = LambdaExpression.AsyncModifierRole; bool isAsync; public bool IsAsync { get { return isAsync; } set { ThrowIfFrozen(); isAsync = value; } } // used to tell the difference between delegate {} and delegate () {} bool hasParameterList; public bool HasParameterList { get { return hasParameterList || Parameters.Any(); } set { ThrowIfFrozen(); hasParameterList = value; } } public CSharpTokenNode DelegateToken { get { return GetChildByRole(DelegateKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public AnonymousMethodExpression() { } public AnonymousMethodExpression(BlockStatement body, IEnumerable parameters = null) { if (parameters != null) { hasParameterList = true; foreach (var parameter in parameters) { AddChild(parameter, Roles.Parameter); } } AddChild(body, Roles.Body); } public AnonymousMethodExpression(BlockStatement body, params ParameterDeclaration[] parameters) : this(body, (IEnumerable)parameters) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAnonymousMethodExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAnonymousMethodExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAnonymousMethodExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { AnonymousMethodExpression o = other as AnonymousMethodExpression; return o != null && this.IsAsync == o.IsAsync && this.HasParameterList == o.HasParameterList && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AnonymousTypeCreateExpression.cs ================================================ // // AnonymousTypeCreateExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// new { [ExpressionList] } /// public class AnonymousTypeCreateExpression : Expression { public readonly static TokenRole NewKeywordRole = new TokenRole("new"); public CSharpTokenNode NewToken { get { return GetChildByRole(NewKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Initializers { get { return GetChildrenByRole(Roles.Expression); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public AnonymousTypeCreateExpression() { } public AnonymousTypeCreateExpression(IEnumerable initializers) { foreach (var ini in initializers) { AddChild(ini, Roles.Expression); } } public AnonymousTypeCreateExpression(params Expression[] initializer) : this((IEnumerable)initializer) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAnonymousTypeCreateExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAnonymousTypeCreateExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAnonymousTypeCreateExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as AnonymousTypeCreateExpression; return o != null && this.Initializers.DoMatch(o.Initializers, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// new Type[Dimensions] /// public class ArrayCreateExpression : Expression { public readonly static TokenRole NewKeywordRole = new TokenRole("new"); public readonly static Role AdditionalArraySpecifierRole = new Role("AdditionalArraySpecifier", null); public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); public CSharpTokenNode NewToken { get { return GetChildByRole(NewKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } /// /// Gets additional array ranks (those without size info). /// Empty for "new int[5,1]"; will contain a single element for "new int[5][]". /// public AstNodeCollection AdditionalArraySpecifiers { get { return GetChildrenByRole(AdditionalArraySpecifierRole); } } public ArrayInitializerExpression Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitArrayCreateExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitArrayCreateExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitArrayCreateExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ArrayCreateExpression o = other as ArrayCreateExpression; return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayInitializerExpression.cs ================================================ // // ArrayInitializerExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// { Elements } /// public class ArrayInitializerExpression : Expression { /// /// For ease of use purposes in the resolver the ast representation /// of { a, b, c } is { {a}, {b}, {c} }. /// If IsSingleElement is true then this array initializer expression is a generated one. /// That has no meaning in the source code (and contains no brace tokens). /// public virtual bool IsSingleElement { get { return false; } } public ArrayInitializerExpression() { } public ArrayInitializerExpression(IEnumerable elements) { this.Elements.AddRange(elements); } public ArrayInitializerExpression(params Expression[] elements) { this.Elements.AddRange(elements); } #region Null public new static readonly ArrayInitializerExpression Null = new NullArrayInitializerExpression(); sealed class NullArrayInitializerExpression : ArrayInitializerExpression { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection Elements { get { return GetChildrenByRole(Roles.Expression); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitArrayInitializerExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitArrayInitializerExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitArrayInitializerExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ArrayInitializerExpression o = other as ArrayInitializerExpression; return o != null && this.Elements.DoMatch(o.Elements, match); } public static ArrayInitializerExpression CreateSingleElementInitializer() { return new SingleArrayInitializerExpression(); } /// /// Single elements in array initializers are represented with this special class. /// class SingleArrayInitializerExpression : ArrayInitializerExpression { public override bool IsSingleElement { get { return true; } } } #region PatternPlaceholder public static implicit operator ArrayInitializerExpression(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : ArrayInitializerExpression, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AsExpression.cs ================================================ // // AsExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Expression as TypeReference /// public class AsExpression : Expression { public readonly static TokenRole AsKeywordRole = new TokenRole("as"); public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode AsToken { get { return GetChildByRole(AsKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public AsExpression() { } public AsExpression(Expression expression, AstType type) { AddChild(expression, Roles.Expression); AddChild(type, Roles.Type); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAsExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAsExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAsExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { AsExpression o = other as AsExpression; return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs ================================================ // // AssignmentExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Left Operator= Right /// public class AssignmentExpression : Expression { // reuse roles from BinaryOperatorExpression public readonly static Role LeftRole = BinaryOperatorExpression.LeftRole; public readonly static Role RightRole = BinaryOperatorExpression.RightRole; public readonly static TokenRole AssignRole = new TokenRole("="); public readonly static TokenRole AddRole = new TokenRole("+="); public readonly static TokenRole SubtractRole = new TokenRole("-="); public readonly static TokenRole MultiplyRole = new TokenRole("*="); public readonly static TokenRole DivideRole = new TokenRole("/="); public readonly static TokenRole ModulusRole = new TokenRole("%="); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<="); public readonly static TokenRole ShiftRightRole = new TokenRole(">>="); public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>="); public readonly static TokenRole BitwiseAndRole = new TokenRole("&="); public readonly static TokenRole BitwiseOrRole = new TokenRole("|="); public readonly static TokenRole ExclusiveOrRole = new TokenRole("^="); public AssignmentExpression() { } public AssignmentExpression(Expression left, Expression right) { this.Left = left; this.Right = right; } public AssignmentExpression(Expression left, AssignmentOperatorType op, Expression right) { this.Left = left; this.Operator = op; this.Right = right; } public AssignmentOperatorType Operator { get; set; } public Expression Left { get { return GetChildByRole(LeftRole); } set { SetChildByRole(LeftRole, value); } } public CSharpTokenNode OperatorToken { get { return GetChildByRole(GetOperatorRole(Operator)); } } public Expression Right { get { return GetChildByRole(RightRole); } set { SetChildByRole(RightRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAssignmentExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAssignmentExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAssignmentExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { AssignmentExpression o = other as AssignmentExpression; return o != null && (this.Operator == AssignmentOperatorType.Any || this.Operator == o.Operator) && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); } public static TokenRole GetOperatorRole(AssignmentOperatorType op) { switch (op) { case AssignmentOperatorType.Assign: return AssignRole; case AssignmentOperatorType.Add: return AddRole; case AssignmentOperatorType.Subtract: return SubtractRole; case AssignmentOperatorType.Multiply: return MultiplyRole; case AssignmentOperatorType.Divide: return DivideRole; case AssignmentOperatorType.Modulus: return ModulusRole; case AssignmentOperatorType.ShiftLeft: return ShiftLeftRole; case AssignmentOperatorType.ShiftRight: return ShiftRightRole; case AssignmentOperatorType.UnsignedShiftRight: return UnsignedShiftRightRole; case AssignmentOperatorType.BitwiseAnd: return BitwiseAndRole; case AssignmentOperatorType.BitwiseOr: return BitwiseOrRole; case AssignmentOperatorType.ExclusiveOr: return ExclusiveOrRole; default: throw new NotSupportedException("Invalid value for AssignmentOperatorType"); } } /// /// Gets the binary operator for the specified compound assignment operator. /// Returns null if 'op' is not a compound assignment. /// public static BinaryOperatorType? GetCorrespondingBinaryOperator(AssignmentOperatorType op) { switch (op) { case AssignmentOperatorType.Assign: return null; case AssignmentOperatorType.Add: return BinaryOperatorType.Add; case AssignmentOperatorType.Subtract: return BinaryOperatorType.Subtract; case AssignmentOperatorType.Multiply: return BinaryOperatorType.Multiply; case AssignmentOperatorType.Divide: return BinaryOperatorType.Divide; case AssignmentOperatorType.Modulus: return BinaryOperatorType.Modulus; case AssignmentOperatorType.ShiftLeft: return BinaryOperatorType.ShiftLeft; case AssignmentOperatorType.ShiftRight: return BinaryOperatorType.ShiftRight; case AssignmentOperatorType.UnsignedShiftRight: return BinaryOperatorType.UnsignedShiftRight; case AssignmentOperatorType.BitwiseAnd: return BinaryOperatorType.BitwiseAnd; case AssignmentOperatorType.BitwiseOr: return BinaryOperatorType.BitwiseOr; case AssignmentOperatorType.ExclusiveOr: return BinaryOperatorType.ExclusiveOr; default: throw new NotSupportedException("Invalid value for AssignmentOperatorType"); } } public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool checkForOverflow) { switch (op) { case AssignmentOperatorType.Assign: return ExpressionType.Assign; case AssignmentOperatorType.Add: return checkForOverflow ? ExpressionType.AddAssignChecked : ExpressionType.AddAssign; case AssignmentOperatorType.Subtract: return checkForOverflow ? ExpressionType.SubtractAssignChecked : ExpressionType.SubtractAssign; case AssignmentOperatorType.Multiply: return checkForOverflow ? ExpressionType.MultiplyAssignChecked : ExpressionType.MultiplyAssign; case AssignmentOperatorType.Divide: return ExpressionType.DivideAssign; case AssignmentOperatorType.Modulus: return ExpressionType.ModuloAssign; case AssignmentOperatorType.ShiftLeft: return ExpressionType.LeftShiftAssign; case AssignmentOperatorType.ShiftRight: return ExpressionType.RightShiftAssign; case AssignmentOperatorType.UnsignedShiftRight: return ExpressionType.Extension; case AssignmentOperatorType.BitwiseAnd: return ExpressionType.AndAssign; case AssignmentOperatorType.BitwiseOr: return ExpressionType.OrAssign; case AssignmentOperatorType.ExclusiveOr: return ExpressionType.ExclusiveOrAssign; default: throw new NotSupportedException("Invalid value for AssignmentOperatorType"); } } public static AssignmentOperatorType? GetAssignmentOperatorTypeFromExpressionType(ExpressionType expressionType) { switch (expressionType) { case ExpressionType.AddAssign: case ExpressionType.AddAssignChecked: return AssignmentOperatorType.Add; case ExpressionType.AndAssign: return AssignmentOperatorType.BitwiseAnd; case ExpressionType.DivideAssign: return AssignmentOperatorType.Divide; case ExpressionType.ExclusiveOrAssign: return AssignmentOperatorType.ExclusiveOr; case ExpressionType.LeftShiftAssign: return AssignmentOperatorType.ShiftLeft; case ExpressionType.ModuloAssign: return AssignmentOperatorType.Modulus; case ExpressionType.MultiplyAssign: case ExpressionType.MultiplyAssignChecked: return AssignmentOperatorType.Multiply; case ExpressionType.OrAssign: return AssignmentOperatorType.BitwiseOr; case ExpressionType.RightShiftAssign: return AssignmentOperatorType.ShiftRight; case ExpressionType.SubtractAssign: case ExpressionType.SubtractAssignChecked: return AssignmentOperatorType.Subtract; default: return null; } } } public enum AssignmentOperatorType { /// left = right Assign, /// left += right Add, /// left -= right Subtract, /// left *= right Multiply, /// left /= right Divide, /// left %= right Modulus, /// left <<= right ShiftLeft, /// left >>= right ShiftRight, /// left >>>= right UnsignedShiftRight, /// left &= right BitwiseAnd, /// left |= right BitwiseOr, /// left ^= right ExclusiveOr, /// Any operator (for pattern matching) Any } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BaseReferenceExpression.cs ================================================ // // BaseReferenceExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// base /// public class BaseReferenceExpression : Expression { public TextLocation Location { get; set; } public override TextLocation StartLocation { get { return Location; } } public override TextLocation EndLocation { get { return new TextLocation(Location.Line, Location.Column + "base".Length); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitBaseReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitBaseReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitBaseReferenceExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { BaseReferenceExpression o = other as BaseReferenceExpression; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs ================================================ // // BinaryOperatorExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Linq.Expressions; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Left Operator Right /// public class BinaryOperatorExpression : Expression { public readonly static TokenRole BitwiseAndRole = new TokenRole("&"); public readonly static TokenRole BitwiseOrRole = new TokenRole("|"); public readonly static TokenRole ConditionalAndRole = new TokenRole("&&"); public readonly static TokenRole ConditionalOrRole = new TokenRole("||"); public readonly static TokenRole ExclusiveOrRole = new TokenRole("^"); public readonly static TokenRole GreaterThanRole = new TokenRole(">"); public readonly static TokenRole GreaterThanOrEqualRole = new TokenRole(">="); public readonly static TokenRole EqualityRole = new TokenRole("=="); public readonly static TokenRole InEqualityRole = new TokenRole("!="); public readonly static TokenRole LessThanRole = new TokenRole("<"); public readonly static TokenRole LessThanOrEqualRole = new TokenRole("<="); public readonly static TokenRole AddRole = new TokenRole("+"); public readonly static TokenRole SubtractRole = new TokenRole("-"); public readonly static TokenRole MultiplyRole = new TokenRole("*"); public readonly static TokenRole DivideRole = new TokenRole("/"); public readonly static TokenRole ModulusRole = new TokenRole("%"); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<"); public readonly static TokenRole ShiftRightRole = new TokenRole(">>"); public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>"); public readonly static TokenRole NullCoalescingRole = new TokenRole("??"); public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; public readonly static Role LeftRole = new Role("Left", Expression.Null); public readonly static Role RightRole = new Role("Right", Expression.Null); public BinaryOperatorExpression() { } public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) { this.Left = left; this.Operator = op; this.Right = right; } public BinaryOperatorType Operator { get; set; } public Expression Left { get { return GetChildByRole(LeftRole); } set { SetChildByRole(LeftRole, value); } } public CSharpTokenNode OperatorToken { get { return GetChildByRole(GetOperatorRole(Operator)); } } public Expression Right { get { return GetChildByRole(RightRole); } set { SetChildByRole(RightRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitBinaryOperatorExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitBinaryOperatorExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitBinaryOperatorExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { BinaryOperatorExpression o = other as BinaryOperatorExpression; return o != null && (this.Operator == BinaryOperatorType.Any || this.Operator == o.Operator) && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); } public static TokenRole GetOperatorRole(BinaryOperatorType op) { switch (op) { case BinaryOperatorType.BitwiseAnd: return BitwiseAndRole; case BinaryOperatorType.BitwiseOr: return BitwiseOrRole; case BinaryOperatorType.ConditionalAnd: return ConditionalAndRole; case BinaryOperatorType.ConditionalOr: return ConditionalOrRole; case BinaryOperatorType.ExclusiveOr: return ExclusiveOrRole; case BinaryOperatorType.GreaterThan: return GreaterThanRole; case BinaryOperatorType.GreaterThanOrEqual: return GreaterThanOrEqualRole; case BinaryOperatorType.Equality: return EqualityRole; case BinaryOperatorType.InEquality: return InEqualityRole; case BinaryOperatorType.LessThan: return LessThanRole; case BinaryOperatorType.LessThanOrEqual: return LessThanOrEqualRole; case BinaryOperatorType.Add: return AddRole; case BinaryOperatorType.Subtract: return SubtractRole; case BinaryOperatorType.Multiply: return MultiplyRole; case BinaryOperatorType.Divide: return DivideRole; case BinaryOperatorType.Modulus: return ModulusRole; case BinaryOperatorType.ShiftLeft: return ShiftLeftRole; case BinaryOperatorType.ShiftRight: return ShiftRightRole; case BinaryOperatorType.UnsignedShiftRight: return UnsignedShiftRightRole; case BinaryOperatorType.NullCoalescing: return NullCoalescingRole; case BinaryOperatorType.Range: return RangeRole; case BinaryOperatorType.IsPattern: return IsKeywordRole; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); } } public static ExpressionType GetLinqNodeType(BinaryOperatorType op, bool checkForOverflow) { switch (op) { case BinaryOperatorType.BitwiseAnd: return ExpressionType.And; case BinaryOperatorType.BitwiseOr: return ExpressionType.Or; case BinaryOperatorType.ConditionalAnd: return ExpressionType.AndAlso; case BinaryOperatorType.ConditionalOr: return ExpressionType.OrElse; case BinaryOperatorType.ExclusiveOr: return ExpressionType.ExclusiveOr; case BinaryOperatorType.GreaterThan: return ExpressionType.GreaterThan; case BinaryOperatorType.GreaterThanOrEqual: return ExpressionType.GreaterThanOrEqual; case BinaryOperatorType.Equality: return ExpressionType.Equal; case BinaryOperatorType.InEquality: return ExpressionType.NotEqual; case BinaryOperatorType.LessThan: return ExpressionType.LessThan; case BinaryOperatorType.LessThanOrEqual: return ExpressionType.LessThanOrEqual; case BinaryOperatorType.Add: return checkForOverflow ? ExpressionType.AddChecked : ExpressionType.Add; case BinaryOperatorType.Subtract: return checkForOverflow ? ExpressionType.SubtractChecked : ExpressionType.Subtract; case BinaryOperatorType.Multiply: return checkForOverflow ? ExpressionType.MultiplyChecked : ExpressionType.Multiply; case BinaryOperatorType.Divide: return ExpressionType.Divide; case BinaryOperatorType.Modulus: return ExpressionType.Modulo; case BinaryOperatorType.ShiftLeft: return ExpressionType.LeftShift; case BinaryOperatorType.ShiftRight: return ExpressionType.RightShift; case BinaryOperatorType.NullCoalescing: return ExpressionType.Coalesce; case BinaryOperatorType.Range: case BinaryOperatorType.UnsignedShiftRight: return ExpressionType.Extension; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); } } } public enum BinaryOperatorType { /// /// Any binary operator (used in pattern matching) /// Any, // We avoid 'logical or' on purpose, because it's not clear if that refers to the bitwise // or to the short-circuiting (conditional) operator: // MCS and old NRefactory used bitwise='|', logical='||' // but the C# spec uses logical='|', conditional='||' /// left & right BitwiseAnd, /// left | right BitwiseOr, /// left && right ConditionalAnd, /// left || right ConditionalOr, /// left ^ right ExclusiveOr, /// left > right GreaterThan, /// left >= right GreaterThanOrEqual, /// left == right Equality, /// left != right InEquality, /// left < right LessThan, /// left <= right LessThanOrEqual, /// left + right Add, /// left - right Subtract, /// left * right Multiply, /// left / right Divide, /// left % right Modulus, /// left << right ShiftLeft, /// left >> right ShiftRight, /// left >>> right UnsignedShiftRight, /// left ?? right NullCoalescing, /// left .. right /// left and right are optional = may be Expression.Null Range, /// left is right /// right must be a pattern IsPattern, } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/CastExpression.cs ================================================ // // CastExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// (CastTo)Expression /// public class CastExpression : Expression { public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CastExpression() { } public CastExpression(AstType castToType, Expression expression) { AddChild(castToType, Roles.Type); AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCastExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCastExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCastExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CastExpression o = other as CastExpression; return o != null && this.Type.DoMatch(o.Type, match) && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/CheckedExpression.cs ================================================ // // CheckedExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// checked(Expression) /// public class CheckedExpression : Expression { public readonly static TokenRole CheckedKeywordRole = new TokenRole("checked"); public CSharpTokenNode CheckedToken { get { return GetChildByRole(CheckedKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CheckedExpression() { } public CheckedExpression(Expression expression) { AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCheckedExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCheckedExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCheckedExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CheckedExpression o = other as CheckedExpression; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ConditionalExpression.cs ================================================ // // ConditionalExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Condition ? TrueExpression : FalseExpression /// public class ConditionalExpression : Expression { public readonly static Role ConditionRole = Roles.Condition; public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); public readonly static Role TrueRole = new Role("True", Expression.Null); public readonly static TokenRole ColonRole = Roles.Colon; public readonly static Role FalseRole = new Role("False", Expression.Null); public Expression Condition { get { return GetChildByRole(ConditionRole); } set { SetChildByRole(ConditionRole, value); } } public CSharpTokenNode QuestionMarkToken { get { return GetChildByRole(QuestionMarkRole); } } public Expression TrueExpression { get { return GetChildByRole(TrueRole); } set { SetChildByRole(TrueRole, value); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(ColonRole); } } public Expression FalseExpression { get { return GetChildByRole(FalseRole); } set { SetChildByRole(FalseRole, value); } } public ConditionalExpression() { } public ConditionalExpression(Expression condition, Expression trueExpression, Expression falseExpression) { AddChild(condition, ConditionRole); AddChild(trueExpression, TrueRole); AddChild(falseExpression, FalseRole); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitConditionalExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitConditionalExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitConditionalExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ConditionalExpression o = other as ConditionalExpression; return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueExpression.DoMatch(o.TrueExpression, match) && this.FalseExpression.DoMatch(o.FalseExpression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DeclarationExpression.cs ================================================ // Copyright (c) 2020 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// TypeName VariableDesignation /// public class DeclarationExpression : Expression { public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public VariableDesignation Designation { get { return GetChildByRole(Roles.VariableDesignationRole); } set { SetChildByRole(Roles.VariableDesignationRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDeclarationExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDeclarationExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDeclarationExpression(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is DeclarationExpression o && Type.DoMatch(o.Type, match) && Designation.DoMatch(o.Designation, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DefaultValueExpression.cs ================================================ // // DefaultValueExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// default(Type) /// public class DefaultValueExpression : Expression { public readonly static TokenRole DefaultKeywordRole = new TokenRole("default"); public CSharpTokenNode DefaultToken { get { return GetChildByRole(DefaultKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public DefaultValueExpression() { } public DefaultValueExpression(AstType type) { AddChild(type, Roles.Type); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDefaultValueExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDefaultValueExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDefaultValueExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DefaultValueExpression o = other as DefaultValueExpression; return o != null && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/DirectionExpression.cs ================================================ // // DirectionExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum FieldDirection { None, Out, Ref, In } /// /// ref Expression /// public class DirectionExpression : Expression { public readonly static TokenRole RefKeywordRole = new TokenRole("ref"); public readonly static TokenRole OutKeywordRole = new TokenRole("out"); public readonly static TokenRole InKeywordRole = new TokenRole("in"); public FieldDirection FieldDirection { get; set; } public CSharpTokenNode FieldDirectionToken { get { switch (FieldDirection) { case FieldDirection.Ref: return GetChildByRole(RefKeywordRole); case FieldDirection.In: return GetChildByRole(InKeywordRole); default: return GetChildByRole(OutKeywordRole); } } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public DirectionExpression() { } public DirectionExpression(FieldDirection direction, Expression expression) { this.FieldDirection = direction; AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDirectionExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDirectionExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDirectionExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DirectionExpression o = other as DirectionExpression; return o != null && this.FieldDirection == o.FieldDirection && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ErrorExpression.cs ================================================ // // ErrorExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Xamarin Inc. // // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ErrorExpression : Expression { public TextLocation Location { get; set; } public override TextLocation StartLocation { get { return Location; } } public override TextLocation EndLocation { get { return Location; } } public string Error { get; private set; } public ErrorExpression() { } public ErrorExpression(string error) { AddChild(new Comment(error, CommentType.MultiLine), Roles.Comment); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitErrorNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitErrorNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitErrorNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as ErrorExpression; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Base class for expressions. /// /// /// This class is useful even though it doesn't provide any additional functionality: /// It can be used to communicate more information in APIs, e.g. "this subnode will always be an expression" /// public abstract class Expression : AstNode { #region Null public new static readonly Expression Null = new NullExpression(); sealed class NullExpression : Expression { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator Expression(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : Expression, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType { get { return NodeType.Expression; } } public new Expression Clone() { return (Expression)base.Clone(); } public Expression ReplaceWith(Func replaceFunction) { if (replaceFunction == null) throw new ArgumentNullException(nameof(replaceFunction)); return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IdentifierExpression.cs ================================================ // // IdentifierExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public class IdentifierExpression : Expression { public IdentifierExpression() { } public IdentifierExpression(string identifier) { this.Identifier = identifier; } public IdentifierExpression(string identifier, TextLocation location) { SetChildByRole(Roles.Identifier, Decompiler.CSharp.Syntax.Identifier.Create(identifier, location)); } public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Decompiler.CSharp.Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIdentifierExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIdentifierExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIdentifierExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IdentifierExpression o = other as IdentifierExpression; return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IndexerExpression.cs ================================================ // // IndexerExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Target[Arguments] /// public class IndexerExpression : Expression { public Expression Target { get { return GetChildByRole(Roles.TargetExpression); } set { SetChildByRole(Roles.TargetExpression, value); } } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public IndexerExpression() { } public IndexerExpression(Expression target, IEnumerable arguments) { AddChild(target, Roles.TargetExpression); if (arguments != null) { foreach (var arg in arguments) { AddChild(arg, Roles.Argument); } } } public IndexerExpression(Expression target, params Expression[] arguments) : this(target, (IEnumerable)arguments) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIndexerExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIndexerExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIndexerExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IndexerExpression o = other as IndexerExpression; return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs ================================================ using System; using System.Collections.Generic; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class InterpolatedStringExpression : Expression { public static readonly TokenRole OpenQuote = new TokenRole("$\""); public static readonly TokenRole CloseQuote = new TokenRole("\""); public AstNodeCollection Content { get { return GetChildrenByRole(InterpolatedStringContent.Role); } } public InterpolatedStringExpression() { } public InterpolatedStringExpression(IList content) { Content.AddRange(content); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInterpolatedStringExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitInterpolatedStringExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitInterpolatedStringExpression(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { InterpolatedStringExpression o = other as InterpolatedStringExpression; return o != null && !o.IsNull && this.Content.DoMatch(o.Content, match); } } public abstract class InterpolatedStringContent : AstNode { #region Null public new static readonly InterpolatedStringContent Null = new NullInterpolatedStringContent(); sealed class NullInterpolatedStringContent : InterpolatedStringContent { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion public new static readonly Role Role = new Role("InterpolatedStringContent", Syntax.InterpolatedStringContent.Null); public override NodeType NodeType => NodeType.Unknown; } /// /// { Expression , Alignment : Suffix } /// public class Interpolation : InterpolatedStringContent { public static readonly TokenRole LBrace = new TokenRole("{"); public static readonly TokenRole RBrace = new TokenRole("}"); public CSharpTokenNode LBraceToken { get { return GetChildByRole(LBrace); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public int Alignment { get; } public string Suffix { get; } public CSharpTokenNode RBraceToken { get { return GetChildByRole(RBrace); } } public Interpolation() { } public Interpolation(Expression expression, int alignment = 0, string suffix = null) { Expression = expression; Alignment = alignment; Suffix = suffix; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInterpolation(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitInterpolation(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitInterpolation(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { Interpolation o = other as Interpolation; return o != null && this.Expression.DoMatch(o.Expression, match); } } public class InterpolatedStringText : InterpolatedStringContent { public string Text { get; set; } public InterpolatedStringText() { } public InterpolatedStringText(string text) { Text = text; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInterpolatedStringText(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitInterpolatedStringText(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitInterpolatedStringText(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { InterpolatedStringText o = other as InterpolatedStringText; return o != null && o.Text == this.Text; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InvocationExpression.cs ================================================ // // InvocationExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Target(Arguments) /// public class InvocationExpression : Expression { public Expression Target { get { return GetChildByRole(Roles.TargetExpression); } set { SetChildByRole(Roles.TargetExpression, value); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInvocationExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitInvocationExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitInvocationExpression(this, data); } public InvocationExpression() { } public InvocationExpression(Expression target, IEnumerable arguments) { AddChild(target, Roles.TargetExpression); if (arguments != null) { foreach (var arg in arguments) { AddChild(arg, Roles.Argument); } } } public InvocationExpression(Expression target, params Expression[] arguments) : this(target, (IEnumerable)arguments) { } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { InvocationExpression o = other as InvocationExpression; return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/IsExpression.cs ================================================ // // TypeOfIsExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Expression is Type /// public class IsExpression : Expression { public readonly static TokenRole IsKeywordRole = new TokenRole("is"); public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode IsToken { get { return GetChildByRole(IsKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public IsExpression() { } public IsExpression(Expression expression, AstType type) { AddChild(expression, Roles.Expression); AddChild(type, Roles.Type); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIsExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIsExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIsExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IsExpression o = other as IsExpression; return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs ================================================ // // LambdaExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// [async] Parameters => Body /// public class LambdaExpression : Expression { public static readonly Role AttributeRole = new Role("Attribute", null); public readonly static TokenRole AsyncModifierRole = new TokenRole("async"); public static readonly Role BodyRole = new Role("Body", AstNode.Null); bool isAsync; public AstNodeCollection Attributes { get { return base.GetChildrenByRole(AttributeRole); } } public bool IsAsync { get { return isAsync; } set { ThrowIfFrozen(); isAsync = value; } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CSharpTokenNode ArrowToken { get { return GetChildByRole(Roles.Arrow); } } public AstNode Body { get { return GetChildByRole(BodyRole); } set { SetChildByRole(BodyRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitLambdaExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitLambdaExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitLambdaExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { LambdaExpression o = other as LambdaExpression; return o != null && this.IsAsync == o.IsAsync && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/MemberReferenceExpression.cs ================================================ // // MemberReferenceExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Target.MemberName /// public class MemberReferenceExpression : Expression { public Expression Target { get { return GetChildByRole(Roles.TargetExpression); } set { SetChildByRole(Roles.TargetExpression, value); } } public CSharpTokenNode DotToken { get { return GetChildByRole(Roles.Dot); } } public string MemberName { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier MemberNameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode LChevronToken { get { return GetChildByRole(Roles.LChevron); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public CSharpTokenNode RChevronToken { get { return GetChildByRole(Roles.RChevron); } } public MemberReferenceExpression() { } public MemberReferenceExpression(Expression target, string memberName, IEnumerable arguments = null) { AddChild(target, Roles.TargetExpression); MemberName = memberName; if (arguments != null) { foreach (var arg in arguments) { AddChild(arg, Roles.TypeArgument); } } } public MemberReferenceExpression(Expression target, string memberName, params AstType[] arguments) : this(target, memberName, (IEnumerable)arguments) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitMemberReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitMemberReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitMemberReferenceExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { MemberReferenceExpression o = other as MemberReferenceExpression; return o != null && this.Target.DoMatch(o.Target, match) && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/NamedArgumentExpression.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents a named argument passed to a method or attribute. /// name: expression /// public class NamedArgumentExpression : Expression { public NamedArgumentExpression() { } public NamedArgumentExpression(string name, Expression expression) { this.Name = name; this.Expression = expression; } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(Roles.Colon); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNamedArgumentExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNamedArgumentExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNamedArgumentExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { NamedArgumentExpression o = other as NamedArgumentExpression; return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/NamedExpression.cs ================================================ // // NamedExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Xamarin // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// name = expression /// This isn't the same as 'assign' even though it has the same syntax. /// This expression is used in object initializers and for named attribute arguments [Attr(FieldName = value)]. /// public class NamedExpression : Expression { public NamedExpression() { } public NamedExpression(string name, Expression expression) { this.Name = name; this.Expression = expression; } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNamedExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNamedExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNamedExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as NamedExpression; return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/NullReferenceExpression.cs ================================================ // // NullReferenceExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// null /// public class NullReferenceExpression : Expression { TextLocation location; public override TextLocation StartLocation { get { return location; } } internal void SetStartLocation(TextLocation value) { ThrowIfFrozen(); this.location = value; } public override TextLocation EndLocation { get { return new TextLocation(location.Line, location.Column + "null".Length); } } public NullReferenceExpression() { } public NullReferenceExpression(TextLocation location) { this.location = location; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullReferenceExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { NullReferenceExpression o = other as NullReferenceExpression; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ObjectCreateExpression.cs ================================================ // // ObjectCreateExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// new Type(Arguments) { Initializer } /// public class ObjectCreateExpression : Expression { public readonly static TokenRole NewKeywordRole = new TokenRole("new"); public readonly static Role InitializerRole = ArrayCreateExpression.InitializerRole; public CSharpTokenNode NewToken { get { return GetChildByRole(NewKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public ArrayInitializerExpression Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public ObjectCreateExpression() { } public ObjectCreateExpression(AstType type, IEnumerable arguments = null) { AddChild(type, Roles.Type); if (arguments != null) { foreach (var arg in arguments) { AddChild(arg, Roles.Argument); } } } public ObjectCreateExpression(AstType type, params Expression[] arguments) : this(type, (IEnumerable)arguments) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitObjectCreateExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitObjectCreateExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitObjectCreateExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ObjectCreateExpression o = other as ObjectCreateExpression; return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/OutVarDeclarationExpression.cs ================================================ // Copyright (c) 2017 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// out type expression /// public class OutVarDeclarationExpression : Expression { public readonly static TokenRole OutKeywordRole = DirectionExpression.OutKeywordRole; public CSharpTokenNode OutKeywordToken { get { return GetChildByRole(OutKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public VariableInitializer Variable { get { return GetChildByRole(Roles.Variable); } set { SetChildByRole(Roles.Variable, value); } } public OutVarDeclarationExpression() { } public OutVarDeclarationExpression(AstType type, string name) { this.Type = type; this.Variable = new VariableInitializer(name); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitOutVarDeclarationExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitOutVarDeclarationExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitOutVarDeclarationExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as OutVarDeclarationExpression; return o != null && this.Type.DoMatch(o.Type, match) && this.Variable.DoMatch(o.Variable, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ParenthesizedExpression.cs ================================================ // // ParenthesizedExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// ( Expression ) /// public class ParenthesizedExpression : Expression { public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public ParenthesizedExpression() { } public ParenthesizedExpression(Expression expr) { Expression = expr; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitParenthesizedExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitParenthesizedExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitParenthesizedExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ParenthesizedExpression o = other as ParenthesizedExpression; return o != null && this.Expression.DoMatch(o.Expression, match); } /// /// Gets whether the expression acts like a parenthesized expression, /// i.e. whether information about the expected type (for lambda type inference) flows /// into the inner expression. /// /// Returns true for ParenthesizedExpression, CheckedExpression or UncheckedExpression; false otherwise. public static bool ActsAsParenthesizedExpression(AstNode expression) { return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; } /// /// Unpacks the given expression if it is a ParenthesizedExpression, CheckedExpression or UncheckedExpression. /// public static Expression UnpackParenthesizedExpression(Expression expr) { while (ActsAsParenthesizedExpression(expr)) expr = expr.GetChildByRole(Roles.Expression); return expr; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PointerReferenceExpression.cs ================================================ // // PointerReferenceExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Target->MemberName /// public class PointerReferenceExpression : Expression { public readonly static TokenRole ArrowRole = new TokenRole("->"); public Expression Target { get { return GetChildByRole(Roles.TargetExpression); } set { SetChildByRole(Roles.TargetExpression, value); } } public CSharpTokenNode ArrowToken { get { return GetChildByRole(ArrowRole); } } public string MemberName { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier MemberNameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPointerReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPointerReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPointerReferenceExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PointerReferenceExpression o = other as PointerReferenceExpression; return o != null && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs ================================================ // // PrimitiveExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Form of a C# literal. /// public enum LiteralFormat : byte { None, DecimalNumber, HexadecimalNumber, BinaryNumber, StringLiteral, VerbatimStringLiteral, CharLiteral, Utf8Literal, } /// /// Represents a literal value. /// public class PrimitiveExpression : Expression { public static readonly object AnyValue = new object(); TextLocation startLocation; TextLocation endLocation; public override TextLocation StartLocation => startLocation; public override TextLocation EndLocation => endLocation; internal void SetLocation(TextLocation startLocation, TextLocation endLocation) { ThrowIfFrozen(); this.startLocation = startLocation; this.endLocation = endLocation; } object value; LiteralFormat format; public object Value { get { return this.value; } set { ThrowIfFrozen(); this.value = value; } } public LiteralFormat Format { get { return format; } set { ThrowIfFrozen(); format = value; } } public PrimitiveExpression(object value) { this.Value = value; } public PrimitiveExpression(object value, LiteralFormat format) { this.Value = value; this.format = format; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPrimitiveExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPrimitiveExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPrimitiveExpression(this, data); } unsafe static TextLocation AdvanceLocation(TextLocation startLocation, string str) { int line = startLocation.Line; int col = startLocation.Column; fixed (char* start = str) { char* p = start; char* endPtr = start + str.Length; while (p < endPtr) { var nl = NewLine.GetDelimiterLength(*p, () => { char* nextp = p + 1; if (nextp < endPtr) return *nextp; return '\0'; }); if (nl > 0) { line++; col = 1; if (nl == 2) p++; } else { col++; } p++; } } return new TextLocation(line, col); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PrimitiveExpression o = other as PrimitiveExpression; return o != null && (this.Value == AnyValue || object.Equals(this.Value, o.Value)); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/QueryExpression.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public class QueryExpression : Expression { public static readonly Role ClauseRole = new Role("Clause", null); #region Null public new static readonly QueryExpression Null = new NullQueryExpression(); sealed class NullQueryExpression : QueryExpression { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion public AstNodeCollection Clauses { get { return GetChildrenByRole(ClauseRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryExpression o = other as QueryExpression; return o != null && !o.IsNull && this.Clauses.DoMatch(o.Clauses, match); } } public abstract class QueryClause : AstNode { public override NodeType NodeType { get { return NodeType.QueryClause; } } } /// /// Represents a query continuation. /// "(from .. select ..) into Identifier" or "(from .. group .. by ..) into Identifier" /// Note that "join .. into .." is not a query continuation! /// /// This is always the first(!!) clause in a query expression. /// The tree for "from a in b select c into d select e" looks like this: /// new QueryExpression { /// new QueryContinuationClause { /// PrecedingQuery = new QueryExpression { /// new QueryFromClause(a in b), /// new QuerySelectClause(c) /// }, /// Identifier = d /// }, /// new QuerySelectClause(e) /// } /// public class QueryContinuationClause : QueryClause { public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", QueryExpression.Null); public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); public QueryExpression PrecedingQuery { get { return GetChildByRole(PrecedingQueryRole); } set { SetChildByRole(PrecedingQueryRole, value); } } public CSharpTokenNode IntoKeyword { get { return GetChildByRole(IntoKeywordRole); } } public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Decompiler.CSharp.Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryContinuationClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryContinuationClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryContinuationClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryContinuationClause o = other as QueryContinuationClause; return o != null && MatchString(this.Identifier, o.Identifier) && this.PrecedingQuery.DoMatch(o.PrecedingQuery, match); } } public class QueryFromClause : QueryClause { public static readonly TokenRole FromKeywordRole = new TokenRole("from"); public static readonly TokenRole InKeywordRole = new TokenRole("in"); public CSharpTokenNode FromKeyword { get { return GetChildByRole(FromKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Decompiler.CSharp.Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } } public CSharpTokenNode InKeyword { get { return GetChildByRole(InKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryFromClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryFromClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryFromClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryFromClause o = other as QueryFromClause; return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.Identifier, o.Identifier) && this.Expression.DoMatch(o.Expression, match); } } public class QueryLetClause : QueryClause { public readonly static TokenRole LetKeywordRole = new TokenRole("let"); public CSharpTokenNode LetKeyword { get { return GetChildByRole(LetKeywordRole); } } public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Decompiler.CSharp.Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryLetClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryLetClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryLetClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryLetClause o = other as QueryLetClause; return o != null && MatchString(this.Identifier, o.Identifier) && this.Expression.DoMatch(o.Expression, match); } } public class QueryWhereClause : QueryClause { public readonly static TokenRole WhereKeywordRole = new TokenRole("where"); public CSharpTokenNode WhereKeyword { get { return GetChildByRole(WhereKeywordRole); } } public Expression Condition { get { return GetChildByRole(Roles.Condition); } set { SetChildByRole(Roles.Condition, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryWhereClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryWhereClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryWhereClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryWhereClause o = other as QueryWhereClause; return o != null && this.Condition.DoMatch(o.Condition, match); } } /// /// Represents a join or group join clause. /// public class QueryJoinClause : QueryClause { public static readonly TokenRole JoinKeywordRole = new TokenRole("join"); public static readonly Role TypeRole = Roles.Type; public static readonly Role JoinIdentifierRole = Roles.Identifier; public static readonly TokenRole InKeywordRole = new TokenRole("in"); public static readonly Role InExpressionRole = Roles.Expression; public static readonly TokenRole OnKeywordRole = new TokenRole("on"); public static readonly Role OnExpressionRole = new Role("OnExpression", Expression.Null); public static readonly TokenRole EqualsKeywordRole = new TokenRole("equals"); public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", Expression.Null); public static readonly TokenRole IntoKeywordRole = new TokenRole("into"); public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", Identifier.Null); public bool IsGroupJoin { get { return !string.IsNullOrEmpty(this.IntoIdentifier); } } public CSharpTokenNode JoinKeyword { get { return GetChildByRole(JoinKeywordRole); } } public AstType Type { get { return GetChildByRole(TypeRole); } set { SetChildByRole(TypeRole, value); } } public string JoinIdentifier { get { return GetChildByRole(JoinIdentifierRole).Name; } set { SetChildByRole(JoinIdentifierRole, Identifier.Create(value)); } } public Identifier JoinIdentifierToken { get { return GetChildByRole(JoinIdentifierRole); } } public CSharpTokenNode InKeyword { get { return GetChildByRole(InKeywordRole); } } public Expression InExpression { get { return GetChildByRole(InExpressionRole); } set { SetChildByRole(InExpressionRole, value); } } public CSharpTokenNode OnKeyword { get { return GetChildByRole(OnKeywordRole); } } public Expression OnExpression { get { return GetChildByRole(OnExpressionRole); } set { SetChildByRole(OnExpressionRole, value); } } public CSharpTokenNode EqualsKeyword { get { return GetChildByRole(EqualsKeywordRole); } } public Expression EqualsExpression { get { return GetChildByRole(EqualsExpressionRole); } set { SetChildByRole(EqualsExpressionRole, value); } } public CSharpTokenNode IntoKeyword { get { return GetChildByRole(IntoKeywordRole); } } public string IntoIdentifier { get { return GetChildByRole(IntoIdentifierRole).Name; } set { SetChildByRole(IntoIdentifierRole, Identifier.Create(value)); } } public Identifier IntoIdentifierToken { get { return GetChildByRole(IntoIdentifierRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryJoinClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryJoinClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryJoinClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryJoinClause o = other as QueryJoinClause; return o != null && this.IsGroupJoin == o.IsGroupJoin && this.Type.DoMatch(o.Type, match) && MatchString(this.JoinIdentifier, o.JoinIdentifier) && this.InExpression.DoMatch(o.InExpression, match) && this.OnExpression.DoMatch(o.OnExpression, match) && this.EqualsExpression.DoMatch(o.EqualsExpression, match) && MatchString(this.IntoIdentifier, o.IntoIdentifier); } } public class QueryOrderClause : QueryClause { public static readonly TokenRole OrderbyKeywordRole = new TokenRole("orderby"); public static readonly Role OrderingRole = new Role("Ordering", null); public CSharpTokenNode OrderbyToken { get { return GetChildByRole(OrderbyKeywordRole); } } public AstNodeCollection Orderings { get { return GetChildrenByRole(OrderingRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryOrderClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryOrderClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryOrderClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryOrderClause o = other as QueryOrderClause; return o != null && this.Orderings.DoMatch(o.Orderings, match); } } public class QueryOrdering : AstNode { public readonly static TokenRole AscendingKeywordRole = new TokenRole("ascending"); public readonly static TokenRole DescendingKeywordRole = new TokenRole("descending"); public override NodeType NodeType { get { return NodeType.Unknown; } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public QueryOrderingDirection Direction { get; set; } public CSharpTokenNode DirectionToken { get { return Direction == QueryOrderingDirection.Ascending ? GetChildByRole(AscendingKeywordRole) : GetChildByRole(DescendingKeywordRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryOrdering(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryOrdering(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryOrdering(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryOrdering o = other as QueryOrdering; return o != null && this.Direction == o.Direction && this.Expression.DoMatch(o.Expression, match); } } public enum QueryOrderingDirection { None, Ascending, Descending } public class QuerySelectClause : QueryClause { public readonly static TokenRole SelectKeywordRole = new TokenRole("select"); public CSharpTokenNode SelectKeyword { get { return GetChildByRole(SelectKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQuerySelectClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQuerySelectClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQuerySelectClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QuerySelectClause o = other as QuerySelectClause; return o != null && this.Expression.DoMatch(o.Expression, match); } } public class QueryGroupClause : QueryClause { public static readonly TokenRole GroupKeywordRole = new TokenRole("group"); public static readonly Role ProjectionRole = new Role("Projection", Expression.Null); public static readonly TokenRole ByKeywordRole = new TokenRole("by"); public static readonly Role KeyRole = new Role("Key", Expression.Null); public CSharpTokenNode GroupKeyword { get { return GetChildByRole(GroupKeywordRole); } } public Expression Projection { get { return GetChildByRole(ProjectionRole); } set { SetChildByRole(ProjectionRole, value); } } public CSharpTokenNode ByKeyword { get { return GetChildByRole(ByKeywordRole); } } public Expression Key { get { return GetChildByRole(KeyRole); } set { SetChildByRole(KeyRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitQueryGroupClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitQueryGroupClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitQueryGroupClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { QueryGroupClause o = other as QueryGroupClause; return o != null && this.Projection.DoMatch(o.Projection, match) && this.Key.DoMatch(o.Key, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/RecursivePatternExpression.cs ================================================ // Copyright (c) 2023 Daniel Grunwald // // 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class RecursivePatternExpression : Expression { public static readonly Role SubPatternRole = new Role("SubPattern", Syntax.Expression.Null); public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public AstNodeCollection SubPatterns { get { return GetChildrenByRole(SubPatternRole); } } public VariableDesignation Designation { get { return GetChildByRole(Roles.VariableDesignationRole); } set { SetChildByRole(Roles.VariableDesignationRole, value); } } public bool IsPositional { get; set; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitRecursivePatternExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitRecursivePatternExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitRecursivePatternExpression(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is RecursivePatternExpression o && Type.DoMatch(o.Type, match) && IsPositional == o.IsPositional && SubPatterns.DoMatch(o.SubPatterns, match) && Designation.DoMatch(o.Designation, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SizeOfExpression.cs ================================================ // // SizeOfExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// sizeof(Type) /// public class SizeOfExpression : Expression { public readonly static TokenRole SizeofKeywordRole = new TokenRole("sizeof"); public CSharpTokenNode SizeOfToken { get { return GetChildByRole(SizeofKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public SizeOfExpression() { } public SizeOfExpression(AstType type) { AddChild(type, Roles.Type); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSizeOfExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSizeOfExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSizeOfExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SizeOfExpression o = other as SizeOfExpression; return o != null && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs ================================================ // // StackAllocExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// stackalloc Type[Count] /// public class StackAllocExpression : Expression { public readonly static TokenRole StackallocKeywordRole = new TokenRole("stackalloc"); public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); public CSharpTokenNode StackAllocToken { get { return GetChildByRole(StackallocKeywordRole); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public Expression CountExpression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public ArrayInitializerExpression Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitStackAllocExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitStackAllocExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitStackAllocExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { StackAllocExpression o = other as StackAllocExpression; return o != null && this.Type.DoMatch(o.Type, match) && this.CountExpression.DoMatch(o.CountExpression, match) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Expression switch { SwitchSections } /// public class SwitchExpression : Expression { public static readonly TokenRole SwitchKeywordRole = new TokenRole("switch"); public static readonly Role SwitchSectionRole = new Role("SwitchSection", null); public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SwitchToken { get { return GetChildByRole(SwitchKeywordRole); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection SwitchSections { get { return GetChildrenByRole(SwitchSectionRole); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSwitchExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSwitchExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSwitchExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SwitchExpression o = other as SwitchExpression; return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); } } /// /// Pattern => Expression /// public class SwitchExpressionSection : AstNode { public static readonly Role PatternRole = new Role("Pattern", Expression.Null); public static readonly Role BodyRole = new Role("Body", Expression.Null); public Expression Pattern { get { return GetChildByRole(PatternRole); } set { SetChildByRole(PatternRole, value); } } public CSharpTokenNode ArrowToken { get { return GetChildByRole(Roles.Arrow); } } public Expression Body { get { return GetChildByRole(BodyRole); } set { SetChildByRole(BodyRole, value); } } public override NodeType NodeType => NodeType.Unknown; public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSwitchExpressionSection(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSwitchExpressionSection(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSwitchExpressionSection(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SwitchExpressionSection o = other as SwitchExpressionSection; return o != null && this.Pattern.DoMatch(o.Pattern, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ThisReferenceExpression.cs ================================================ // // ThisReferenceExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// this /// public class ThisReferenceExpression : Expression { public TextLocation Location { get; set; } public override TextLocation StartLocation { get { return Location; } } public override TextLocation EndLocation { get { return new TextLocation(Location.Line, Location.Column + "this".Length); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitThisReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitThisReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitThisReferenceExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ThisReferenceExpression o = other as ThisReferenceExpression; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ThrowExpression.cs ================================================ // Copyright (c) 2017 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// throw Expression /// public class ThrowExpression : Expression { public static readonly TokenRole ThrowKeywordRole = ThrowStatement.ThrowKeywordRole; public CSharpTokenNode ThrowToken { get { return GetChildByRole(ThrowKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public ThrowExpression() { } public ThrowExpression(Expression expression) { AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitThrowExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitThrowExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitThrowExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ThrowExpression o = other as ThrowExpression; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/TupleExpression.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class TupleExpression : Expression { public AstNodeCollection Elements { get { return GetChildrenByRole(Roles.Expression); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTupleExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTupleExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTupleExpression(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is TupleExpression tuple && Elements.DoMatch(tuple.Elements, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/TypeOfExpression.cs ================================================ // // TypeOfExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// typeof(Type) /// public class TypeOfExpression : Expression { public readonly static TokenRole TypeofKeywordRole = new TokenRole("typeof"); public CSharpTokenNode TypeOfToken { get { return GetChildByRole(TypeofKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public TypeOfExpression() { } public TypeOfExpression(AstType type) { AddChild(type, Roles.Type); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTypeOfExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTypeOfExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTypeOfExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { TypeOfExpression o = other as TypeOfExpression; return o != null && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/TypeReferenceExpression.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents an AstType as an expression. /// This is used when calling a method on a primitive type: "int.Parse()" /// public class TypeReferenceExpression : Expression { public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTypeReferenceExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTypeReferenceExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTypeReferenceExpression(this, data); } public TypeReferenceExpression() { } public TypeReferenceExpression(AstType type) { AddChild(type, Roles.Type); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { TypeReferenceExpression o = other as TypeReferenceExpression; return o != null && this.Type.DoMatch(o.Type, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs ================================================ // // UnaryOperatorExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Linq.Expressions; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Operator Expression /// public class UnaryOperatorExpression : Expression { public readonly static TokenRole NotRole = new TokenRole("!"); public readonly static TokenRole BitNotRole = new TokenRole("~"); public readonly static TokenRole MinusRole = new TokenRole("-"); public readonly static TokenRole PlusRole = new TokenRole("+"); public readonly static TokenRole IncrementRole = new TokenRole("++"); public readonly static TokenRole DecrementRole = new TokenRole("--"); public readonly static TokenRole DereferenceRole = new TokenRole("*"); public readonly static TokenRole AddressOfRole = new TokenRole("&"); public readonly static TokenRole AwaitRole = new TokenRole("await"); public readonly static TokenRole NullConditionalRole = new TokenRole("?"); public readonly static TokenRole SuppressNullableWarningRole = new TokenRole("!"); public readonly static TokenRole IndexFromEndRole = new TokenRole("^"); public readonly static TokenRole PatternNotRole = new TokenRole("not"); public UnaryOperatorExpression() { } public UnaryOperatorExpression(UnaryOperatorType op, Expression expression) { this.Operator = op; this.Expression = expression; } public UnaryOperatorType Operator { get; set; } public CSharpTokenNode OperatorToken { get { return GetChildByRole(GetOperatorRole(Operator)); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUnaryOperatorExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUnaryOperatorExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUnaryOperatorExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UnaryOperatorExpression o = other as UnaryOperatorExpression; return o != null && (this.Operator == UnaryOperatorType.Any || this.Operator == o.Operator) && this.Expression.DoMatch(o.Expression, match); } public static TokenRole GetOperatorRole(UnaryOperatorType op) { switch (op) { case UnaryOperatorType.Not: return NotRole; case UnaryOperatorType.BitNot: return BitNotRole; case UnaryOperatorType.Minus: return MinusRole; case UnaryOperatorType.Plus: return PlusRole; case UnaryOperatorType.Increment: case UnaryOperatorType.PostIncrement: return IncrementRole; case UnaryOperatorType.PostDecrement: case UnaryOperatorType.Decrement: return DecrementRole; case UnaryOperatorType.Dereference: return DereferenceRole; case UnaryOperatorType.AddressOf: return AddressOfRole; case UnaryOperatorType.Await: return AwaitRole; case UnaryOperatorType.NullConditional: return NullConditionalRole; case UnaryOperatorType.NullConditionalRewrap: case UnaryOperatorType.IsTrue: return null; // no syntax case UnaryOperatorType.SuppressNullableWarning: return SuppressNullableWarningRole; case UnaryOperatorType.IndexFromEnd: return IndexFromEndRole; case UnaryOperatorType.PatternNot: return PatternNotRole; case UnaryOperatorType.PatternRelationalLessThan: return BinaryOperatorExpression.LessThanRole; case UnaryOperatorType.PatternRelationalLessThanOrEqual: return BinaryOperatorExpression.LessThanOrEqualRole; case UnaryOperatorType.PatternRelationalGreaterThan: return BinaryOperatorExpression.GreaterThanRole; case UnaryOperatorType.PatternRelationalGreaterThanOrEqual: return BinaryOperatorExpression.GreaterThanOrEqualRole; default: throw new NotSupportedException("Invalid value for UnaryOperatorType"); } } public static ExpressionType GetLinqNodeType(UnaryOperatorType op, bool checkForOverflow) { switch (op) { case UnaryOperatorType.Not: return ExpressionType.Not; case UnaryOperatorType.BitNot: return ExpressionType.OnesComplement; case UnaryOperatorType.Minus: return checkForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate; case UnaryOperatorType.Plus: return ExpressionType.UnaryPlus; case UnaryOperatorType.Increment: return ExpressionType.PreIncrementAssign; case UnaryOperatorType.Decrement: return ExpressionType.PreDecrementAssign; case UnaryOperatorType.PostIncrement: return ExpressionType.PostIncrementAssign; case UnaryOperatorType.PostDecrement: return ExpressionType.PostDecrementAssign; case UnaryOperatorType.Dereference: case UnaryOperatorType.AddressOf: case UnaryOperatorType.Await: case UnaryOperatorType.SuppressNullableWarning: case UnaryOperatorType.IndexFromEnd: case UnaryOperatorType.PatternNot: case UnaryOperatorType.PatternRelationalLessThan: case UnaryOperatorType.PatternRelationalLessThanOrEqual: case UnaryOperatorType.PatternRelationalGreaterThan: case UnaryOperatorType.PatternRelationalGreaterThanOrEqual: return ExpressionType.Extension; default: throw new NotSupportedException("Invalid value for UnaryOperatorType"); } } } public enum UnaryOperatorType { /// /// Any unary operator (used in pattern matching) /// Any, /// Logical not (!a) Not, /// Bitwise not (~a) BitNot, /// Unary minus (-a) Minus, /// Unary plus (+a) Plus, /// Pre increment (++a) Increment, /// Pre decrement (--a) Decrement, /// Post increment (a++) PostIncrement, /// Post decrement (a--) PostDecrement, /// Dereferencing (*a) Dereference, /// Get address (&a) AddressOf, /// C# 5.0 await Await, /// C# 6 null-conditional operator. /// Occurs as target of member reference or indexer expressions /// to indicate ?. or ?[]. /// Corresponds to nullable.unwrap in ILAst. /// NullConditional, /// /// Wrapper around a primary expression containing a null conditional operator. /// Corresponds to nullable.rewrap in ILAst. /// This has no syntax in C#, but the node is used to ensure parentheses are inserted where necessary. /// NullConditionalRewrap, /// /// Implicit call of "operator true". /// IsTrue, /// /// C# 8 postfix ! operator (dammit operator) /// SuppressNullableWarning, /// /// C# 8 prefix ^ operator /// IndexFromEnd, /// /// C# 9 not pattern /// PatternNot, /// /// C# 9 relational pattern /// PatternRelationalLessThan, /// /// C# 9 relational pattern /// PatternRelationalLessThanOrEqual, /// /// C# 9 relational pattern /// PatternRelationalGreaterThan, /// /// C# 9 relational pattern /// PatternRelationalGreaterThanOrEqual, } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UncheckedExpression.cs ================================================ // // UncheckedExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// unchecked(Expression) /// public class UncheckedExpression : Expression { public readonly static TokenRole UncheckedKeywordRole = new TokenRole("unchecked"); public CSharpTokenNode UncheckedToken { get { return GetChildByRole(UncheckedKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public UncheckedExpression() { } public UncheckedExpression(Expression expression) { AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUncheckedExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUncheckedExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUncheckedExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UncheckedExpression o = other as UncheckedExpression; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UndocumentedExpression.cs ================================================ // // UndocumentedExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum UndocumentedExpressionType { ArgListAccess, // __arglist ArgList, // __arglist (a1, a2, ..., an) RefValue, // __refvalue (expr , type) RefType, // __reftype (expr) MakeRef // __makeref (expr) } /// /// Represents undocumented expressions. /// public class UndocumentedExpression : Expression { public readonly static TokenRole ArglistKeywordRole = new TokenRole("__arglist"); public readonly static TokenRole RefvalueKeywordRole = new TokenRole("__refvalue"); public readonly static TokenRole ReftypeKeywordRole = new TokenRole("__reftype"); public readonly static TokenRole MakerefKeywordRole = new TokenRole("__makeref"); public UndocumentedExpressionType UndocumentedExpressionType { get; set; } public CSharpTokenNode UndocumentedToken { get { switch (UndocumentedExpressionType) { case UndocumentedExpressionType.ArgListAccess: case UndocumentedExpressionType.ArgList: return GetChildByRole(ArglistKeywordRole); case UndocumentedExpressionType.RefValue: return GetChildByRole(RefvalueKeywordRole); case UndocumentedExpressionType.RefType: return GetChildByRole(ReftypeKeywordRole); case UndocumentedExpressionType.MakeRef: return GetChildByRole(MakerefKeywordRole); } return CSharpTokenNode.Null; } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUndocumentedExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUndocumentedExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUndocumentedExpression(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UndocumentedExpression o = other as UndocumentedExpression; return o != null && this.UndocumentedExpressionType == o.UndocumentedExpressionType && this.Arguments.DoMatch(o.Arguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Expressions/WithInitializerExpression.cs ================================================ // Copyright (c) 2010-2021 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Expression with Initializer /// public class WithInitializerExpression : Expression { public readonly static TokenRole WithKeywordRole = new TokenRole("with"); public readonly static Role InitializerRole = ArrayCreateExpression.InitializerRole; public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode WithToken { get { return GetChildByRole(WithKeywordRole); } } public ArrayInitializerExpression Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitWithInitializerExpression(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitWithInitializerExpression(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitWithInitializerExpression(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is WithInitializerExpression o && this.Expression.DoMatch(o.Expression, match) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs ================================================ // // FullTypeName.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public class FunctionPointerAstType : AstType { public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly Role CallingConventionRole = new Role("CallConv", AstType.Null); public bool HasUnmanagedCallingConvention { get; set; } public AstNodeCollection CallingConventions { get { return GetChildrenByRole(CallingConventionRole); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public AstType ReturnType { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitFunctionPointerType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitFunctionPointerType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitFunctionPointerType(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other is FunctionPointerAstType o && this.CallingConventions.DoMatch(o.CallingConventions, match) && this.Parameters.DoMatch(o.Parameters, match) && this.ReturnType.DoMatch(o.ReturnType, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Attribute.cs ================================================ // // Attribute.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.CSharp.OutputVisitor; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Attribute(Arguments) /// public class Attribute : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Arguments { get { return base.GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } // HasArgumentList == false: [Empty] public bool HasArgumentList { get; set; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAttribute(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAttribute(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAttribute(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { Attribute o = other as Attribute; return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match); } public override string ToString(CSharpFormattingOptions formattingOptions) { if (IsNull) return "Null"; return base.ToString(formattingOptions); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/AttributeSection.cs ================================================ // // AttributeSection.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// [AttributeTarget: Attributes] /// public class AttributeSection : AstNode { #region PatternPlaceholder public static implicit operator AttributeSection(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : AttributeSection, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public string AttributeTarget { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier AttributeTargetToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public AstNodeCollection Attributes { get { return base.GetChildrenByRole(Roles.Attribute); } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAttributeSection(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAttributeSection(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAttributeSection(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { AttributeSection o = other as AttributeSection; return o != null && MatchString(this.AttributeTarget, o.AttributeTarget) && this.Attributes.DoMatch(o.Attributes, match); } public AttributeSection() { } public AttributeSection(Attribute attr) { this.Attributes.Add(attr); } // public static string GetAttributeTargetName(AttributeTarget attributeTarget) // { // switch (attributeTarget) { // case AttributeTarget.None: // return null; // case AttributeTarget.Assembly: // return "assembly"; // case AttributeTarget.Module: // return "module"; // case AttributeTarget.Type: // return "type"; // case AttributeTarget.Param: // return "param"; // case AttributeTarget.Field: // return "field"; // case AttributeTarget.Return: // return "return"; // case AttributeTarget.Method: // return "method"; // default: // throw new NotSupportedException("Invalid value for AttributeTarget"); // } // } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Comment.cs ================================================ // // Comment.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum CommentType { /// /// "//" comment /// SingleLine, /// /// "/* */" comment /// MultiLine, /// /// "///" comment /// Documentation, /// /// Inactive code (code in non-taken "#if") /// InactiveCode, /// /// "/** */" comment /// MultiLineDocumentation } public class Comment : AstNode { public override NodeType NodeType { get { return NodeType.Whitespace; } } CommentType commentType; public CommentType CommentType { get { return commentType; } set { ThrowIfFrozen(); commentType = value; } } /// /// Returns true if the is Documentation or MultiLineDocumentation. /// public bool IsDocumentation { get { return commentType == CommentType.Documentation || commentType == CommentType.MultiLineDocumentation; } } bool startsLine; public bool StartsLine { get { return startsLine; } set { ThrowIfFrozen(); startsLine = value; } } string content; public string Content { get { return content; } set { ThrowIfFrozen(); content = value; } } TextLocation startLocation; public override TextLocation StartLocation { get { return startLocation; } } TextLocation endLocation; public override TextLocation EndLocation { get { return endLocation; } } internal void SetStartLocation(TextLocation value) { ThrowIfFrozen(); this.startLocation = value; } internal void SetEndLocation(TextLocation value) { ThrowIfFrozen(); this.endLocation = value; } public Comment(string content, CommentType type = CommentType.SingleLine) { this.CommentType = type; this.Content = content; } public Comment(CommentType commentType, TextLocation startLocation, TextLocation endLocation) { this.CommentType = commentType; this.startLocation = startLocation; this.endLocation = endLocation; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitComment(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitComment(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitComment(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { Comment o = other as Comment; return o != null && this.CommentType == o.CommentType && MatchString(this.Content, o.Content); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs ================================================ // // Constraint.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// where TypeParameter : BaseTypes /// /// /// new(), struct and class constraints are represented using a PrimitiveType "new", "struct" or "class" /// public class Constraint : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode WhereKeyword { get { return GetChildByRole(Roles.WhereKeyword); } } public SimpleType TypeParameter { get { return GetChildByRole(Roles.ConstraintTypeParameter); } set { SetChildByRole(Roles.ConstraintTypeParameter, value); } } public AstNodeCollection BaseTypes { get { return GetChildrenByRole(Roles.BaseType); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitConstraint(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitConstraint(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitConstraint(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { Constraint o = other as Constraint; return o != null && this.TypeParameter.DoMatch(o.TypeParameter, match) && this.BaseTypes.DoMatch(o.BaseTypes, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/DelegateDeclaration.cs ================================================ // // DelegateDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// delegate ReturnType Name<TypeParameters>(Parameters) where Constraints; /// public class DelegateDeclaration : EntityDeclaration { public override NodeType NodeType { get { return NodeType.TypeDeclaration; } } public override SymbolKind SymbolKind { get { return SymbolKind.TypeDefinition; } } public CSharpTokenNode DelegateToken { get { return GetChildByRole(Roles.DelegateKeyword); } } public AstNodeCollection TypeParameters { get { return GetChildrenByRole(Roles.TypeParameter); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public AstNodeCollection Constraints { get { return GetChildrenByRole(Roles.Constraint); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDelegateDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDelegateDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDelegateDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DelegateDeclaration o = other as DelegateDeclaration; return o != null && MatchString(this.Name, o.Name) && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs ================================================ // // ExternAliasDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// extern alias IDENTIFIER; /// public class ExternAliasDeclaration : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode ExternToken { get { return GetChildByRole(Roles.ExternKeyword); } } public CSharpTokenNode AliasToken { get { return GetChildByRole(Roles.AliasKeyword); } } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitExternAliasDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitExternAliasDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitExternAliasDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as ExternAliasDeclaration; return o != null && MatchString(this.Name, o.Name); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs ================================================ // // NamespaceDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// namespace Name { Members } /// public class NamespaceDeclaration : AstNode { public static readonly Role MemberRole = SyntaxTree.MemberRole; public static readonly Role NamespaceNameRole = new Role("NamespaceName", AstType.Null); public override NodeType NodeType { get { return NodeType.Unknown; } } public bool IsFileScoped { get; set; } public CSharpTokenNode NamespaceToken { get { return GetChildByRole(Roles.NamespaceKeyword); } } public AstType NamespaceName { get { return GetChildByRole(NamespaceNameRole) ?? AstType.Null; } set { SetChildByRole(NamespaceNameRole, value); } } public string Name { get { return UsingDeclaration.ConstructNamespace(NamespaceName); } set { var arr = value.Split('.'); NamespaceName = ConstructType(arr, arr.Length - 1); } } static AstType ConstructType(string[] arr, int i) { if (i < 0 || i >= arr.Length) throw new ArgumentOutOfRangeException(nameof(i)); if (i == 0) return new SimpleType(arr[i]); return new MemberType(ConstructType(arr, i - 1), arr[i]); } /// /// Gets the full namespace name (including any parent namespaces) /// public string FullName { get { NamespaceDeclaration parentNamespace = Parent as NamespaceDeclaration; if (parentNamespace != null) return BuildQualifiedName(parentNamespace.FullName, Name); return Name; } } public IEnumerable Identifiers { get { var result = new Stack(); AstType type = NamespaceName; while (type is MemberType) { var mt = (MemberType)type; result.Push(mt.MemberName); type = mt.Target; } if (type is SimpleType) result.Push(((SimpleType)type).Identifier); return result; } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection Members { get { return GetChildrenByRole(MemberRole); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public NamespaceDeclaration() { } public NamespaceDeclaration(string name) { this.Name = name; } public static string BuildQualifiedName(string name1, string name2) { if (string.IsNullOrEmpty(name1)) return name2; if (string.IsNullOrEmpty(name2)) return name1; return name1 + "." + name2; } public void AddMember(AstNode child) { AddChild(child, MemberRole); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNamespaceDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNamespaceDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNamespaceDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { NamespaceDeclaration o = other as NamespaceDeclaration; return o != null && MatchString(this.Name, o.Name) && this.Members.DoMatch(o.Members, match); } } }; ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/PreProcessorDirective.cs ================================================ // // PreProcessorDirective.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Xamarin Inc. // // 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.Linq; namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum PreProcessorDirectiveType : byte { Invalid = 0, Region = 1, Endregion = 2, If = 3, Endif = 4, Elif = 5, Else = 6, Define = 7, Undef = 8, Error = 9, Warning = 10, Pragma = 11, Line = 12 } public class LinePreprocessorDirective : PreProcessorDirective { public int LineNumber { get; set; } public string FileName { get; set; } public LinePreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base(PreProcessorDirectiveType.Line, startLocation, endLocation) { } public LinePreprocessorDirective(string argument = null) : base(PreProcessorDirectiveType.Line, argument) { } } public class PragmaWarningPreprocessorDirective : PreProcessorDirective { public static readonly Role WarningRole = new Role("Warning", null); public static readonly TokenRole PragmaKeywordRole = new TokenRole("#pragma"); public static readonly TokenRole WarningKeywordRole = new TokenRole("warning"); public static readonly TokenRole DisableKeywordRole = new TokenRole("disable"); public static readonly TokenRole RestoreKeywordRole = new TokenRole("restore"); public bool Disable { get { return !DisableToken.IsNull; } } public CSharpTokenNode PragmaToken { get { return GetChildByRole(PragmaKeywordRole); } } public CSharpTokenNode WarningToken { get { return GetChildByRole(WarningKeywordRole); } } public CSharpTokenNode DisableToken { get { return GetChildByRole(DisableKeywordRole); } } public CSharpTokenNode RestoreToken { get { return GetChildByRole(RestoreKeywordRole); } } public AstNodeCollection Warnings { get { return GetChildrenByRole(WarningRole); } } public override TextLocation EndLocation { get { var child = LastChild; if (child == null) return base.EndLocation; return child.EndLocation; } } public PragmaWarningPreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base(PreProcessorDirectiveType.Pragma, startLocation, endLocation) { } public PragmaWarningPreprocessorDirective(string argument = null) : base(PreProcessorDirectiveType.Pragma, argument) { } public bool IsDefined(int pragmaWarning) { return Warnings.Select(w => (int)w.Value).Any(n => n == pragmaWarning); } } public class PreProcessorDirective : AstNode { public override NodeType NodeType { get { return NodeType.Whitespace; } } public PreProcessorDirectiveType Type { get; set; } public string Argument { get; set; } /// /// For an '#if' directive, specifies whether the condition evaluated to true. /// public bool Take { get; set; } TextLocation startLocation; public override TextLocation StartLocation { get { return startLocation; } } TextLocation endLocation; public override TextLocation EndLocation { get { return endLocation; } } public PreProcessorDirective(PreProcessorDirectiveType type, TextLocation startLocation, TextLocation endLocation) { this.Type = type; this.startLocation = startLocation; this.endLocation = endLocation; } public PreProcessorDirective(PreProcessorDirectiveType type, string argument = null) { this.Type = type; this.Argument = argument; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPreProcessorDirective(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPreProcessorDirective(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPreProcessorDirective(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PreProcessorDirective o = other as PreProcessorDirective; return o != null && Type == o.Type && MatchString(Argument, o.Argument); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/TypeDeclaration.cs ================================================ // // TypeDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum ClassType { Class, Struct, Interface, Enum, /// /// C# 9 'record class' /// RecordClass, /// /// C# 10 'record struct' /// RecordStruct, } /// /// class Name<TypeParameters> : BaseTypes where Constraints; /// public class TypeDeclaration : EntityDeclaration { public override NodeType NodeType { get { return NodeType.TypeDeclaration; } } public override SymbolKind SymbolKind { get { return SymbolKind.TypeDefinition; } } ClassType classType; public CSharpTokenNode TypeKeyword { get { switch (classType) { case ClassType.Class: return GetChildByRole(Roles.ClassKeyword); case ClassType.Struct: case ClassType.RecordStruct: return GetChildByRole(Roles.StructKeyword); case ClassType.Interface: return GetChildByRole(Roles.InterfaceKeyword); case ClassType.Enum: return GetChildByRole(Roles.EnumKeyword); case ClassType.RecordClass: return GetChildByRole(Roles.RecordKeyword); default: return CSharpTokenNode.Null; } } } public ClassType ClassType { get { return classType; } set { ThrowIfFrozen(); classType = value; } } public CSharpTokenNode LChevronToken { get { return GetChildByRole(Roles.LChevron); } } public AstNodeCollection TypeParameters { get { return GetChildrenByRole(Roles.TypeParameter); } } public CSharpTokenNode RChevronToken { get { return GetChildByRole(Roles.RChevron); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(Roles.Colon); } } public bool HasPrimaryConstructor { get; set; } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection PrimaryConstructorParameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public AstNodeCollection BaseTypes { get { return GetChildrenByRole(Roles.BaseType); } } public AstNodeCollection Constraints { get { return GetChildrenByRole(Roles.Constraint); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection Members { get { return GetChildrenByRole(Roles.TypeMemberRole); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTypeDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTypeDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTypeDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { TypeDeclaration o = other as TypeDeclaration; return o != null && this.ClassType == o.ClassType && MatchString(this.Name, o.Name) && this.MatchAttributesAndModifiers(o, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.BaseTypes.DoMatch(o.BaseTypes, match) && this.Constraints.DoMatch(o.Constraints, match) && this.HasPrimaryConstructor == o.HasPrimaryConstructor && this.PrimaryConstructorParameters.DoMatch(o.PrimaryConstructorParameters, match) && this.Members.DoMatch(o.Members, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/TypeParameterDeclaration.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// [in|out] Name /// /// Represents a type parameter. /// Note: mirroring the C# syntax, constraints are not part of the type parameter declaration, but belong /// to the parent type or method. /// public class TypeParameterDeclaration : AstNode { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole OutVarianceKeywordRole = new TokenRole("out"); public static readonly TokenRole InVarianceKeywordRole = new TokenRole("in"); public override NodeType NodeType { get { return NodeType.Unknown; } } public AstNodeCollection Attributes { get { return GetChildrenByRole(AttributeRole); } } VarianceModifier variance; public VarianceModifier Variance { get { return variance; } set { ThrowIfFrozen(); variance = value; } } public CSharpTokenNode VarianceToken { get { switch (Variance) { case VarianceModifier.Covariant: return GetChildByRole(OutVarianceKeywordRole); case VarianceModifier.Contravariant: return GetChildByRole(InVarianceKeywordRole); default: return CSharpTokenNode.Null; } } } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public TypeParameterDeclaration() { } public TypeParameterDeclaration(string name) { Name = name; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTypeParameterDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTypeParameterDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTypeParameterDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { TypeParameterDeclaration o = other as TypeParameterDeclaration; return o != null && this.Variance == o.Variance && MatchString(this.Name, o.Name) && this.Attributes.DoMatch(o.Attributes, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs ================================================ // // UsingAliasDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// using Alias = Import; /// public class UsingAliasDeclaration : AstNode { public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly Role AliasRole = new Role("Alias", Identifier.Null); public static readonly Role ImportRole = UsingDeclaration.ImportRole; public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode UsingToken { get { return GetChildByRole(UsingKeywordRole); } } public string Alias { get { return GetChildByRole(AliasRole).Name; } set { SetChildByRole(AliasRole, Identifier.Create(value)); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public AstType Import { get { return GetChildByRole(ImportRole); } set { SetChildByRole(ImportRole, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public UsingAliasDeclaration() { } public UsingAliasDeclaration(string alias, string nameSpace) { AddChild(Identifier.Create(alias), AliasRole); AddChild(new SimpleType(nameSpace), ImportRole); } public UsingAliasDeclaration(string alias, AstType import) { AddChild(Identifier.Create(alias), AliasRole); AddChild(import, ImportRole); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUsingAliasDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUsingAliasDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUsingAliasDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UsingAliasDeclaration o = other as UsingAliasDeclaration; return o != null && MatchString(this.Alias, o.Alias) && this.Import.DoMatch(o.Import, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs ================================================ // // UsingDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; using System.Text; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// using Import; /// public class UsingDeclaration : AstNode { public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly Role ImportRole = new Role("Import", AstType.Null); public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode UsingToken { get { return GetChildByRole(UsingKeywordRole); } } public AstType Import { get { return GetChildByRole(ImportRole); } set { SetChildByRole(ImportRole, value); } } public string Namespace { get { return ConstructNamespace(Import); } } internal static string ConstructNamespace(AstType type) { var stack = new Stack(); while (type is MemberType) { var mt = (MemberType)type; stack.Push(mt.MemberName); type = mt.Target; if (mt.IsDoubleColon) { stack.Push("::"); } else { stack.Push("."); } } if (type is SimpleType) stack.Push(((SimpleType)type).Identifier); var result = new StringBuilder(); while (stack.Count > 0) result.Append(stack.Pop()); return result.ToString(); } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public UsingDeclaration() { } public UsingDeclaration(string nameSpace) { AddChild(AstType.Create(nameSpace), ImportRole); } public UsingDeclaration(AstType import) { AddChild(import, ImportRole); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUsingDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUsingDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUsingDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UsingDeclaration o = other as UsingDeclaration; return o != null && this.Import.DoMatch(o.Import, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Threading; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Provides an interface to handle annotations in an object. /// public interface IAnnotatable { /// /// Gets all annotations stored on this IAnnotatable. /// IEnumerable Annotations { get; } /// /// Gets the first annotation of the specified type. /// Returns null if no matching annotation exists. /// /// /// The type of the annotation. /// T Annotation() where T : class; /// /// Gets the first annotation of the specified type. /// Returns null if no matching annotation exists. /// /// /// The type of the annotation. /// object Annotation(Type type); /// /// Adds an annotation to this instance. /// /// /// The annotation to add. /// void AddAnnotation(object annotation); /// /// Removes all annotations of the specified type. /// /// /// The type of the annotations to remove. /// void RemoveAnnotations() where T : class; /// /// Removes all annotations of the specified type. /// /// /// The type of the annotations to remove. /// void RemoveAnnotations(Type type); } /// /// Base class used to implement the IAnnotatable interface. /// This implementation is thread-safe. /// [Serializable] public abstract class AbstractAnnotatable : IAnnotatable { // Annotations: points either null (no annotations), to the single annotation, // or to an AnnotationList. // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list) object annotations; /// /// Clones all annotations. /// This method is intended to be called by Clone() implementations in derived classes. /// /// AstNode copy = (AstNode)MemberwiseClone(); /// copy.CloneAnnotations(); /// /// protected void CloneAnnotations() { ICloneable cloneable = annotations as ICloneable; if (cloneable != null) annotations = cloneable.Clone(); } sealed class AnnotationList : List, ICloneable { // There are two uses for this custom list type: // 1) it's private, and thus (unlike List) cannot be confused with real annotations // 2) It allows us to simplify the cloning logic by making the list behave the same as a clonable annotation. public AnnotationList(int initialCapacity) : base(initialCapacity) { } public object Clone() { lock (this) { AnnotationList copy = new AnnotationList(this.Count); for (int i = 0; i < this.Count; i++) { object obj = this[i]; ICloneable c = obj as ICloneable; copy.Add(c != null ? c.Clone() : obj); } return copy; } } } public virtual void AddAnnotation(object annotation) { if (annotation == null) throw new ArgumentNullException(nameof(annotation)); retry: // Retry until successful object oldAnnotation = Interlocked.CompareExchange(ref this.annotations, annotation, null); if (oldAnnotation == null) { return; // we successfully added a single annotation } AnnotationList list = oldAnnotation as AnnotationList; if (list == null) { // we need to transform the old annotation into a list list = new AnnotationList(4); list.Add(oldAnnotation); list.Add(annotation); if (Interlocked.CompareExchange(ref this.annotations, list, oldAnnotation) != oldAnnotation) { // the transformation failed (some other thread wrote to this.annotations first) goto retry; } } else { // once there's a list, use simple locking lock (list) { list.Add(annotation); } } } public virtual void RemoveAnnotations() where T : class { retry: // Retry until successful object oldAnnotations = this.annotations; AnnotationList list = oldAnnotations as AnnotationList; if (list != null) { lock (list) list.RemoveAll(obj => obj is T); } else if (oldAnnotations is T) { if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) { // Operation failed (some other thread wrote to this.annotations first) goto retry; } } } public virtual void RemoveAnnotations(Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); retry: // Retry until successful object oldAnnotations = this.annotations; AnnotationList list = oldAnnotations as AnnotationList; if (list != null) { lock (list) list.RemoveAll(type.IsInstanceOfType); } else if (type.IsInstanceOfType(oldAnnotations)) { if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) { // Operation failed (some other thread wrote to this.annotations first) goto retry; } } } public T Annotation() where T : class { object annotations = this.annotations; AnnotationList list = annotations as AnnotationList; if (list != null) { lock (list) { foreach (object obj in list) { T t = obj as T; if (t != null) return t; } return null; } } else { return annotations as T; } } public object Annotation(Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); object annotations = this.annotations; AnnotationList list = annotations as AnnotationList; if (list != null) { lock (list) { foreach (object obj in list) { if (type.IsInstanceOfType(obj)) return obj; } } } else { if (type.IsInstanceOfType(annotations)) return annotations; } return null; } /// /// Gets all annotations stored on this AstNode. /// public IEnumerable Annotations { get { object annotations = this.annotations; AnnotationList list = annotations as AnnotationList; if (list != null) { lock (list) { return list.ToArray(); } } else { if (annotations != null) return new object[] { annotations }; else return Enumerable.Empty(); } } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// AST visitor. /// public interface IAstVisitor { void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); void VisitAsExpression(AsExpression asExpression); void VisitAssignmentExpression(AssignmentExpression assignmentExpression); void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); void VisitCastExpression(CastExpression castExpression); void VisitCheckedExpression(CheckedExpression checkedExpression); void VisitConditionalExpression(ConditionalExpression conditionalExpression); void VisitDeclarationExpression(DeclarationExpression declarationExpression); void VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression); void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); void VisitDirectionExpression(DirectionExpression directionExpression); void VisitIdentifierExpression(IdentifierExpression identifierExpression); void VisitIndexerExpression(IndexerExpression indexerExpression); void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression); void VisitInvocationExpression(InvocationExpression invocationExpression); void VisitIsExpression(IsExpression isExpression); void VisitLambdaExpression(LambdaExpression lambdaExpression); void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); void VisitNamedExpression(NamedExpression namedExpression); void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); void VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression); void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); void VisitSizeOfExpression(SizeOfExpression sizeOfExpression); void VisitStackAllocExpression(StackAllocExpression stackAllocExpression); void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); void VisitThrowExpression(ThrowExpression throwExpression); void VisitTupleExpression(TupleExpression tupleExpression); void VisitTypeOfExpression(TypeOfExpression typeOfExpression); void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); void VisitUncheckedExpression(UncheckedExpression uncheckedExpression); void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); void VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression); void VisitQueryExpression(QueryExpression queryExpression); void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); void VisitQueryFromClause(QueryFromClause queryFromClause); void VisitQueryLetClause(QueryLetClause queryLetClause); void VisitQueryWhereClause(QueryWhereClause queryWhereClause); void VisitQueryJoinClause(QueryJoinClause queryJoinClause); void VisitQueryOrderClause(QueryOrderClause queryOrderClause); void VisitQueryOrdering(QueryOrdering queryOrdering); void VisitQuerySelectClause(QuerySelectClause querySelectClause); void VisitQueryGroupClause(QueryGroupClause queryGroupClause); void VisitAttribute(Attribute attribute); void VisitAttributeSection(AttributeSection attributeSection); void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); void VisitTypeDeclaration(TypeDeclaration typeDeclaration); void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); void VisitUsingDeclaration(UsingDeclaration usingDeclaration); void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); void VisitBlockStatement(BlockStatement blockStatement); void VisitBreakStatement(BreakStatement breakStatement); void VisitCheckedStatement(CheckedStatement checkedStatement); void VisitContinueStatement(ContinueStatement continueStatement); void VisitDoWhileStatement(DoWhileStatement doWhileStatement); void VisitEmptyStatement(EmptyStatement emptyStatement); void VisitExpressionStatement(ExpressionStatement expressionStatement); void VisitFixedStatement(FixedStatement fixedStatement); void VisitForeachStatement(ForeachStatement foreachStatement); void VisitForStatement(ForStatement forStatement); void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); void VisitGotoStatement(GotoStatement gotoStatement); void VisitIfElseStatement(IfElseStatement ifElseStatement); void VisitLabelStatement(LabelStatement labelStatement); void VisitLockStatement(LockStatement lockStatement); void VisitReturnStatement(ReturnStatement returnStatement); void VisitSwitchStatement(SwitchStatement switchStatement); void VisitSwitchSection(SwitchSection switchSection); void VisitCaseLabel(CaseLabel caseLabel); void VisitSwitchExpression(SwitchExpression switchExpression); void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection); void VisitThrowStatement(ThrowStatement throwStatement); void VisitTryCatchStatement(TryCatchStatement tryCatchStatement); void VisitCatchClause(CatchClause catchClause); void VisitUncheckedStatement(UncheckedStatement uncheckedStatement); void VisitUnsafeStatement(UnsafeStatement unsafeStatement); void VisitUsingStatement(UsingStatement usingStatement); void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); void VisitWhileStatement(WhileStatement whileStatement); void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); void VisitAccessor(Accessor accessor); void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); void VisitConstructorInitializer(ConstructorInitializer constructorInitializer); void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); void VisitEventDeclaration(EventDeclaration eventDeclaration); void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); void VisitFieldDeclaration(FieldDeclaration fieldDeclaration); void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); void VisitMethodDeclaration(MethodDeclaration methodDeclaration); void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); void VisitVariableInitializer(VariableInitializer variableInitializer); void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); void VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration); void VisitSyntaxTree(SyntaxTree syntaxTree); void VisitSimpleType(SimpleType simpleType); void VisitMemberType(MemberType memberType); void VisitTupleType(TupleAstType tupleType); void VisitTupleTypeElement(TupleTypeElement tupleTypeElement); void VisitFunctionPointerType(FunctionPointerAstType functionPointerType); void VisitInvocationType(InvocationAstType invocationType); void VisitComposedType(ComposedType composedType); void VisitArraySpecifier(ArraySpecifier arraySpecifier); void VisitPrimitiveType(PrimitiveType primitiveType); void VisitComment(Comment comment); void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); void VisitDocumentationReference(DocumentationReference documentationReference); void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); void VisitConstraint(Constraint constraint); void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); void VisitIdentifier(Identifier identifier); void VisitInterpolation(Interpolation interpolation); void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText); void VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation); void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation); void VisitNullNode(AstNode nullNode); void VisitErrorNode(AstNode errorNode); void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); } /// /// AST visitor. /// public interface IAstVisitor { S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); S VisitAsExpression(AsExpression asExpression); S VisitAssignmentExpression(AssignmentExpression assignmentExpression); S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); S VisitCastExpression(CastExpression castExpression); S VisitCheckedExpression(CheckedExpression checkedExpression); S VisitConditionalExpression(ConditionalExpression conditionalExpression); S VisitDeclarationExpression(DeclarationExpression declarationExpression); S VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression); S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); S VisitDirectionExpression(DirectionExpression directionExpression); S VisitIdentifierExpression(IdentifierExpression identifierExpression); S VisitIndexerExpression(IndexerExpression indexerExpression); S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression); S VisitInvocationExpression(InvocationExpression invocationExpression); S VisitIsExpression(IsExpression isExpression); S VisitLambdaExpression(LambdaExpression lambdaExpression); S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); S VisitNamedExpression(NamedExpression namedExpression); S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); S VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression); S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); S VisitSizeOfExpression(SizeOfExpression sizeOfExpression); S VisitStackAllocExpression(StackAllocExpression stackAllocExpression); S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); S VisitThrowExpression(ThrowExpression throwExpression); S VisitTupleExpression(TupleExpression tupleExpression); S VisitTypeOfExpression(TypeOfExpression typeOfExpression); S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); S VisitUncheckedExpression(UncheckedExpression uncheckedExpression); S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); S VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression); S VisitQueryExpression(QueryExpression queryExpression); S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); S VisitQueryFromClause(QueryFromClause queryFromClause); S VisitQueryLetClause(QueryLetClause queryLetClause); S VisitQueryWhereClause(QueryWhereClause queryWhereClause); S VisitQueryJoinClause(QueryJoinClause queryJoinClause); S VisitQueryOrderClause(QueryOrderClause queryOrderClause); S VisitQueryOrdering(QueryOrdering queryOrdering); S VisitQuerySelectClause(QuerySelectClause querySelectClause); S VisitQueryGroupClause(QueryGroupClause queryGroupClause); S VisitAttribute(Attribute attribute); S VisitAttributeSection(AttributeSection attributeSection); S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); S VisitTypeDeclaration(TypeDeclaration typeDeclaration); S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); S VisitUsingDeclaration(UsingDeclaration usingDeclaration); S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); S VisitBlockStatement(BlockStatement blockStatement); S VisitBreakStatement(BreakStatement breakStatement); S VisitCheckedStatement(CheckedStatement checkedStatement); S VisitContinueStatement(ContinueStatement continueStatement); S VisitDoWhileStatement(DoWhileStatement doWhileStatement); S VisitEmptyStatement(EmptyStatement emptyStatement); S VisitExpressionStatement(ExpressionStatement expressionStatement); S VisitFixedStatement(FixedStatement fixedStatement); S VisitForeachStatement(ForeachStatement foreachStatement); S VisitForStatement(ForStatement forStatement); S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); S VisitGotoStatement(GotoStatement gotoStatement); S VisitIfElseStatement(IfElseStatement ifElseStatement); S VisitLabelStatement(LabelStatement labelStatement); S VisitLockStatement(LockStatement lockStatement); S VisitReturnStatement(ReturnStatement returnStatement); S VisitSwitchStatement(SwitchStatement switchStatement); S VisitSwitchSection(SwitchSection switchSection); S VisitCaseLabel(CaseLabel caseLabel); S VisitSwitchExpression(SwitchExpression switchExpression); S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection); S VisitThrowStatement(ThrowStatement throwStatement); S VisitTryCatchStatement(TryCatchStatement tryCatchStatement); S VisitCatchClause(CatchClause catchClause); S VisitUncheckedStatement(UncheckedStatement uncheckedStatement); S VisitUnsafeStatement(UnsafeStatement unsafeStatement); S VisitUsingStatement(UsingStatement usingStatement); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); S VisitWhileStatement(WhileStatement whileStatement); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); S VisitAccessor(Accessor accessor); S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); S VisitConstructorInitializer(ConstructorInitializer constructorInitializer); S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); S VisitEventDeclaration(EventDeclaration eventDeclaration); S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); S VisitFieldDeclaration(FieldDeclaration fieldDeclaration); S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); S VisitMethodDeclaration(MethodDeclaration methodDeclaration); S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); S VisitVariableInitializer(VariableInitializer variableInitializer); S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); S VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration); S VisitSyntaxTree(SyntaxTree syntaxTree); S VisitSimpleType(SimpleType simpleType); S VisitMemberType(MemberType memberType); S VisitTupleType(TupleAstType tupleType); S VisitTupleTypeElement(TupleTypeElement tupleTypeElement); S VisitFunctionPointerType(FunctionPointerAstType functionPointerType); S VisitInvocationType(InvocationAstType invocationType); S VisitComposedType(ComposedType composedType); S VisitArraySpecifier(ArraySpecifier arraySpecifier); S VisitPrimitiveType(PrimitiveType primitiveType); S VisitComment(Comment comment); S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); S VisitDocumentationReference(DocumentationReference documentationReference); S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); S VisitConstraint(Constraint constraint); S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); S VisitIdentifier(Identifier identifier); S VisitInterpolation(Interpolation interpolation); S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText); S VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation); S VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation); S VisitNullNode(AstNode nullNode); S VisitErrorNode(AstNode errorNode); S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); } /// /// AST visitor. /// public interface IAstVisitor { S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data); S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data); S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data); S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data); S VisitAsExpression(AsExpression asExpression, T data); S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data); S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data); S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data); S VisitCastExpression(CastExpression castExpression, T data); S VisitCheckedExpression(CheckedExpression checkedExpression, T data); S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data); S VisitDeclarationExpression(DeclarationExpression declarationExpression, T data); S VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression, T data); S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data); S VisitDirectionExpression(DirectionExpression directionExpression, T data); S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data); S VisitIndexerExpression(IndexerExpression indexerExpression, T data); S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression, T data); S VisitInvocationExpression(InvocationExpression invocationExpression, T data); S VisitIsExpression(IsExpression isExpression, T data); S VisitLambdaExpression(LambdaExpression lambdaExpression, T data); S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data); S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data); S VisitNamedExpression(NamedExpression namedExpression, T data); S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data); S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data); S VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression, T data); S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data); S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data); S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data); S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data); S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data); S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data); S VisitThrowExpression(ThrowExpression throwExpression, T data); S VisitTupleExpression(TupleExpression tupleExpression, T data); S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data); S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data); S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data); S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data); S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data); S VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression, T data); S VisitQueryExpression(QueryExpression queryExpression, T data); S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data); S VisitQueryFromClause(QueryFromClause queryFromClause, T data); S VisitQueryLetClause(QueryLetClause queryLetClause, T data); S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data); S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data); S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data); S VisitQueryOrdering(QueryOrdering queryOrdering, T data); S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data); S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data); S VisitAttribute(Attribute attribute, T data); S VisitAttributeSection(AttributeSection attributeSection, T data); S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data); S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data); S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data); S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration, T data); S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data); S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data); S VisitBlockStatement(BlockStatement blockStatement, T data); S VisitBreakStatement(BreakStatement breakStatement, T data); S VisitCheckedStatement(CheckedStatement checkedStatement, T data); S VisitContinueStatement(ContinueStatement continueStatement, T data); S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data); S VisitEmptyStatement(EmptyStatement emptyStatement, T data); S VisitExpressionStatement(ExpressionStatement expressionStatement, T data); S VisitFixedStatement(FixedStatement fixedStatement, T data); S VisitForeachStatement(ForeachStatement foreachStatement, T data); S VisitForStatement(ForStatement forStatement, T data); S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data); S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data); S VisitGotoStatement(GotoStatement gotoStatement, T data); S VisitIfElseStatement(IfElseStatement ifElseStatement, T data); S VisitLabelStatement(LabelStatement labelStatement, T data); S VisitLockStatement(LockStatement lockStatement, T data); S VisitReturnStatement(ReturnStatement returnStatement, T data); S VisitSwitchStatement(SwitchStatement switchStatement, T data); S VisitSwitchSection(SwitchSection switchSection, T data); S VisitCaseLabel(CaseLabel caseLabel, T data); S VisitSwitchExpression(SwitchExpression switchExpression, T data); S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection, T data); S VisitThrowStatement(ThrowStatement throwStatement, T data); S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data); S VisitCatchClause(CatchClause catchClause, T data); S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data); S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); S VisitUsingStatement(UsingStatement usingStatement, T data); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data); S VisitWhileStatement(WhileStatement whileStatement, T data); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); S VisitAccessor(Accessor accessor, T data); S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data); S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data); S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data); S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data); S VisitEventDeclaration(EventDeclaration eventDeclaration, T data); S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration, T data); S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data); S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data); S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data); S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data); S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data); S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data); S VisitVariableInitializer(VariableInitializer variableInitializer, T data); S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data); S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data); S VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration, T data); S VisitSyntaxTree(SyntaxTree syntaxTree, T data); S VisitSimpleType(SimpleType simpleType, T data); S VisitMemberType(MemberType memberType, T data); S VisitTupleType(TupleAstType tupleType, T data); S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data); S VisitFunctionPointerType(FunctionPointerAstType functionPointerType, T data); S VisitInvocationType(InvocationAstType invocationType, T data); S VisitComposedType(ComposedType composedType, T data); S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); S VisitPrimitiveType(PrimitiveType primitiveType, T data); S VisitComment(Comment comment, T data); S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective, T data); S VisitDocumentationReference(DocumentationReference documentationReference, T data); S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data); S VisitConstraint(Constraint constraint, T data); S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data); S VisitIdentifier(Identifier identifier, T data); S VisitInterpolation(Interpolation interpolation, T data); S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText, T data); S VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation, T data); S VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation, T data); S VisitNullNode(AstNode nullNode, T data); S VisitErrorNode(AstNode errorNode, T data); S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs ================================================ // // Identifier.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class Identifier : AstNode { public new static readonly Identifier Null = new NullIdentifier(); sealed class NullIdentifier : Identifier { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } public override NodeType NodeType { get { return NodeType.Token; } } string name; public string Name { get { return this.name; } set { if (value == null) throw new ArgumentNullException(nameof(value)); ThrowIfFrozen(); this.name = value; } } TextLocation startLocation; public override TextLocation StartLocation { get { return startLocation; } } internal void SetStartLocation(TextLocation value) { ThrowIfFrozen(); this.startLocation = value; } const uint verbatimBit = 1u << AstNodeFlagsUsedBits; public bool IsVerbatim { get { return (flags & verbatimBit) != 0; } set { ThrowIfFrozen(); if (value) flags |= verbatimBit; else flags &= ~verbatimBit; } } public override TextLocation EndLocation { get { return new TextLocation(StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsVerbatim ? 1 : 0)); } } Identifier() { this.name = string.Empty; } protected Identifier(string name, TextLocation location) { if (name == null) throw new ArgumentNullException(nameof(name)); this.Name = name; this.startLocation = location; } public static Identifier Create(string name) { return Create(name, TextLocation.Empty); } public static Identifier Create(string name, TextLocation location) { if (string.IsNullOrEmpty(name)) return Identifier.Null; if (name[0] == '@') return new Identifier(name.Substring(1), new TextLocation(location.Line, location.Column + 1)) { IsVerbatim = true }; else return new Identifier(name, location); } public static Identifier Create(string name, TextLocation location, bool isVerbatim) { if (string.IsNullOrEmpty(name)) return Identifier.Null; if (isVerbatim) return new Identifier(name, location) { IsVerbatim = true }; return new Identifier(name, location); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIdentifier(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIdentifier(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIdentifier(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { Identifier o = other as Identifier; return o != null && !o.IsNull && MatchString(this.Name, o.Name); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/IdentifierExpressionBackreference.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Linq; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. /// public class IdentifierExpressionBackreference : PatternMatching.Pattern { readonly string referencedGroupName; public string ReferencedGroupName { get { return referencedGroupName; } } public IdentifierExpressionBackreference(string referencedGroupName) { if (referencedGroupName == null) throw new ArgumentNullException(nameof(referencedGroupName)); this.referencedGroupName = referencedGroupName; } public override bool DoMatch(PatternMatching.INode other, PatternMatching.Match match) { IdentifierExpression ident = other as IdentifierExpression; if (ident == null || ident.TypeArguments.Any()) return false; AstNode referenced = (AstNode)match.Get(referencedGroupName).Last(); if (referenced == null) return false; return ident.Identifier == referenced.GetChildByRole(Roles.Identifier).Name; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/InvocationAstType.cs ================================================ // Copyright (c) 2021 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// BaseType "(" Argument { "," Argument } ")" /// public class InvocationAstType : AstType { public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Expression); } } public AstType BaseType { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInvocationType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitInvocationType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitInvocationType(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is InvocationAstType o && this.BaseType.DoMatch(o.BaseType, match) && this.Arguments.DoMatch(o.Arguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/MemberType.cs ================================================ // // FullTypeName.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class MemberType : AstType { public static readonly Role TargetRole = new Role("Target", AstType.Null); bool isDoubleColon; public bool IsDoubleColon { get { return isDoubleColon; } set { ThrowIfFrozen(); isDoubleColon = value; } } public AstType Target { get { return GetChildByRole(TargetRole); } set { SetChildByRole(TargetRole, value); } } public string MemberName { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier MemberNameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public MemberType() { } public MemberType(AstType target, string memberName) { this.Target = target; this.MemberName = memberName; } public MemberType(AstType target, string memberName, IEnumerable typeArguments) { this.Target = target; this.MemberName = memberName; foreach (var arg in typeArguments) { AddChild(arg, Roles.TypeArgument); } } public MemberType(AstType target, string memberName, params AstType[] typeArguments) : this(target, memberName, (IEnumerable)typeArguments) { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitMemberType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitMemberType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitMemberType(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { MemberType o = other as MemberType; return o != null && this.IsDoubleColon == o.IsDoubleColon && MatchString(this.MemberName, o.MemberName) && this.Target.DoMatch(o.Target, match) && this.TypeArguments.DoMatch(o.TypeArguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs ================================================ // // Modifiers.cs // // Author: // Mike Krüger // // Copyright (C) 2008 Novell, Inc (http://www.novell.com) // // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { [Flags] public enum Modifiers { None = 0, Private = 0x0001, Internal = 0x0002, Protected = 0x0004, Public = 0x0008, Abstract = 0x0010, Virtual = 0x0020, Sealed = 0x0040, Static = 0x0080, Override = 0x0100, Readonly = 0x0200, Const = 0x0400, New = 0x0800, Partial = 0x1000, Extern = 0x2000, Volatile = 0x4000, Unsafe = 0x8000, Async = 0x10000, Ref = 0x20000, Required = 0x40000, VisibilityMask = Private | Internal | Protected | Public, /// /// Special value used to match any modifiers during pattern matching. /// Any = unchecked((int)0x80000000) } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/NodeType.cs ================================================ // // NodeType.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum NodeType { Unknown, /// /// AstType /// TypeReference, /// /// Type or delegate declaration /// TypeDeclaration, Member, Statement, Expression, Token, QueryClause, /// /// Comment or whitespace or pre-processor directive /// Whitespace, /// /// Placeholder for a pattern /// Pattern } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNode.cs ================================================ // Copyright (c) 2011-2013 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 // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Matches any node. /// /// Does not match null nodes. public class AnyNode : Pattern { readonly string groupName; public string GroupName { get { return groupName; } } public AnyNode(string groupName = null) { this.groupName = groupName; } public override bool DoMatch(INode other, Match match) { match.Add(this.groupName, other); return other != null && !other.IsNull; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/AnyNodeOrNull.cs ================================================ // // AnyNodeOrNull.cs // // Author: // Mike Krüger // // Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Matches any node. /// /// Does not match null nodes. public class AnyNodeOrNull : Pattern { readonly string groupName; public string GroupName { get { return groupName; } } public AnyNodeOrNull(string groupName = null) { this.groupName = groupName; } public override bool DoMatch(INode other, Match match) { if (other == null) { match.AddNull(this.groupName); } else { match.Add(this.groupName, other); } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs ================================================ // Copyright (c) 2011-2013 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 // 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.Linq; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Matches the last entry in the specified named group. /// public class Backreference : Pattern { readonly string referencedGroupName; public string ReferencedGroupName { get { return referencedGroupName; } } public Backreference(string referencedGroupName) { if (referencedGroupName == null) throw new ArgumentNullException(nameof(referencedGroupName)); this.referencedGroupName = referencedGroupName; } public override bool DoMatch(INode other, Match match) { var last = match.Get(referencedGroupName).LastOrDefault(); if (last == null && other == null) return true; return last.IsMatch(other); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/BacktrackingInfo.cs ================================================ // Copyright (c) 2011-2013 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 // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Container for the backtracking info. /// public class BacktrackingInfo { internal Stack backtrackingStack = new Stack(); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs ================================================ // Copyright (c) 2011-2013 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 // 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; using System.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Matches one of several alternatives. /// public class Choice : Pattern, IEnumerable { readonly List alternatives = new List(); public void Add(string name, INode alternative) { if (alternative == null) throw new ArgumentNullException(nameof(alternative)); alternatives.Add(new NamedNode(name, alternative)); } public void Add(INode alternative) { if (alternative == null) throw new ArgumentNullException(nameof(alternative)); alternatives.Add(alternative); } public override bool DoMatch(INode other, Match match) { var checkPoint = match.CheckPoint(); foreach (INode alt in alternatives) { if (alt.DoMatch(other, match)) return true; else match.RestoreCheckPoint(checkPoint); } return false; } IEnumerator IEnumerable.GetEnumerator() { return alternatives.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return alternatives.GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs ================================================ // Copyright (c) 2011-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// AST node that supports pattern matching. /// public interface INode { Role Role { get; } INode FirstChild { get; } INode NextSibling { get; } bool IsNull { get; } bool DoMatch(INode other, Match match); bool DoMatchCollection(Role role, INode pos, Match match, BacktrackingInfo backtrackingInfo); } public static class PatternExtensions { /// /// Performs a pattern matching operation. /// this is the pattern, is the AST that is being matched. /// /// /// A match object. Check to see whether the match was successful. /// /// /// Patterns are ASTs that contain special pattern nodes (from the PatternMatching namespace). /// However, it is also possible to match two ASTs without any pattern nodes - /// doing so will produce a successful match if the two ASTs are structurally identical. /// public static Match Match(this INode pattern, INode other) { if (pattern == null) throw new ArgumentNullException(nameof(pattern)); Match match = PatternMatching.Match.CreateNew(); if (pattern.DoMatch(other, match)) return match; else return default(PatternMatching.Match); } public static bool IsMatch(this INode pattern, INode other) { if (pattern == null) throw new ArgumentNullException(nameof(pattern)); return pattern.DoMatch(other, PatternMatching.Match.CreateNew()); } public static AstType ToType(this Pattern pattern) { return pattern; } public static Expression ToExpression(this Pattern pattern) { return pattern; } public static Statement ToStatement(this Pattern pattern) { return pattern; } public static Expression WithName(this Expression node, string patternGroupName) { return new NamedNode(patternGroupName, node); } public static Statement WithName(this Statement node, string patternGroupName) { return new NamedNode(patternGroupName, node); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Match.cs ================================================ // Copyright (c) 2011-2013 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 // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Represents the result of a pattern matching operation. /// public struct Match { // TODO: maybe we should add an implicit Match->bool conversion? (implicit operator bool(Match m) { return m != null; }) List> results; public bool Success { get { return results != null; } } internal static Match CreateNew() { Match m; m.results = new List>(); return m; } internal int CheckPoint() { return results.Count; } internal void RestoreCheckPoint(int checkPoint) { results.RemoveRange(checkPoint, results.Count - checkPoint); } public IEnumerable Get(string groupName) { if (results == null) yield break; foreach (var pair in results) { if (pair.Key == groupName) yield return pair.Value; } } public IEnumerable Get(string groupName) where T : INode { if (results == null) yield break; foreach (var pair in results) { if (pair.Key == groupName) yield return (T)pair.Value; } } public bool Has(string groupName) { if (results == null) return false; foreach (var pair in results) { if (pair.Key == groupName) return true; } return false; } public void Add(string groupName, INode node) { if (groupName != null && node != null) { results.Add(new KeyValuePair(groupName, node)); } } internal void AddNull(string groupName) { if (groupName != null) { results.Add(new KeyValuePair(groupName, null)); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs ================================================ // Copyright (c) 2011-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Represents a named node within a pattern. /// public class NamedNode : Pattern { readonly string groupName; readonly INode childNode; public string GroupName { get { return groupName; } } public INode ChildNode { get { return childNode; } } public NamedNode(string groupName, INode childNode) { if (childNode == null) throw new ArgumentNullException(nameof(childNode)); this.groupName = groupName; this.childNode = childNode; } public override bool DoMatch(INode other, Match match) { match.Add(this.groupName, other); return childNode.DoMatch(other, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs ================================================ // Copyright (c) 2011-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { public class OptionalNode : Pattern { readonly INode childNode; public INode ChildNode { get { return childNode; } } public OptionalNode(INode childNode) { if (childNode == null) throw new ArgumentNullException(nameof(childNode)); this.childNode = childNode; } public OptionalNode(string groupName, INode childNode) : this(new NamedNode(groupName, childNode)) { } public override bool DoMatchCollection(Role role, INode pos, Match match, BacktrackingInfo backtrackingInfo) { backtrackingInfo.backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); return childNode.DoMatch(pos, match); } public override bool DoMatch(INode other, Match match) { if (other == null || other.IsNull) return true; else return childNode.DoMatch(other, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Pattern.cs ================================================ // Copyright (c) 2011-2013 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 // 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.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Base class for all patterns. /// public abstract class Pattern : INode { /// /// Gets the string that matches any string. /// public static readonly string AnyString = "$any$"; public static bool MatchString(string pattern, string text) { return pattern == AnyString || pattern == text; } internal struct PossibleMatch { public readonly INode NextOther; // next node after the last matched node public readonly int Checkpoint; // checkpoint public PossibleMatch(INode nextOther, int checkpoint) { this.NextOther = nextOther; this.Checkpoint = checkpoint; } } bool INode.IsNull { get { return false; } } Role INode.Role { get { return null; } } INode INode.NextSibling { get { return null; } } INode INode.FirstChild { get { return null; } } public abstract bool DoMatch(INode other, Match match); public virtual bool DoMatchCollection(Role role, INode pos, Match match, BacktrackingInfo backtrackingInfo) { return DoMatch(pos, match); } public static bool DoMatchCollection(Role role, INode firstPatternChild, INode firstOtherChild, Match match) { BacktrackingInfo backtrackingInfo = new BacktrackingInfo(); Stack patternStack = new Stack(); Stack stack = backtrackingInfo.backtrackingStack; patternStack.Push(firstPatternChild); stack.Push(new PossibleMatch(firstOtherChild, match.CheckPoint())); while (stack.Count > 0) { INode cur1 = patternStack.Pop(); INode cur2 = stack.Peek().NextOther; match.RestoreCheckPoint(stack.Pop().Checkpoint); bool success = true; while (cur1 != null && success) { while (cur1 != null && cur1.Role != role) cur1 = cur1.NextSibling; while (cur2 != null && cur2.Role != role) cur2 = cur2.NextSibling; if (cur1 == null) break; Debug.Assert(stack.Count == patternStack.Count); success = cur1.DoMatchCollection(role, cur2, match, backtrackingInfo); Debug.Assert(stack.Count >= patternStack.Count); while (stack.Count > patternStack.Count) patternStack.Push(cur1.NextSibling); cur1 = cur1.NextSibling; if (cur2 != null) cur2 = cur2.NextSibling; } while (cur2 != null && cur2.Role != role) cur2 = cur2.NextSibling; if (success && cur2 == null) return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs ================================================ // Copyright (c) 2011-2013 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 // 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.Diagnostics; namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching { /// /// Represents an optional node. /// public class Repeat : Pattern { readonly INode childNode; public int MinCount { get; set; } public int MaxCount { get; set; } public INode ChildNode { get { return childNode; } } public Repeat(INode childNode) { if (childNode == null) throw new ArgumentNullException(nameof(childNode)); this.childNode = childNode; this.MinCount = 0; this.MaxCount = int.MaxValue; } public override bool DoMatchCollection(Role role, INode pos, Match match, BacktrackingInfo backtrackingInfo) { var backtrackingStack = backtrackingInfo.backtrackingStack; Debug.Assert(pos == null || pos.Role == role); int matchCount = 0; if (this.MinCount <= 0) backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); while (matchCount < this.MaxCount && pos != null && childNode.DoMatch(pos, match)) { matchCount++; do { pos = pos.NextSibling; } while (pos != null && pos.Role != role); if (matchCount >= this.MinCount) backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); } return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack. } public override bool DoMatch(INode other, Match match) { if (other == null || other.IsNull) return this.MinCount <= 0; else return this.MaxCount >= 1 && childNode.DoMatch(other, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/PrimitiveType.cs ================================================ // // FullTypeName.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class PrimitiveType : AstType { TextLocation location; string keyword = string.Empty; public string Keyword { get { return keyword; } set { if (value == null) throw new ArgumentNullException(); ThrowIfFrozen(); keyword = value; } } public KnownTypeCode KnownTypeCode { get { return GetTypeCodeForPrimitiveType(this.Keyword); } } public PrimitiveType() { } public PrimitiveType(string keyword) { this.Keyword = keyword; } public PrimitiveType(string keyword, TextLocation location) { this.Keyword = keyword; this.location = location; } public override TextLocation StartLocation { get { return location; } } internal void SetStartLocation(TextLocation value) { ThrowIfFrozen(); this.location = value; } public override TextLocation EndLocation { get { return new TextLocation(location.Line, location.Column + keyword.Length); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPrimitiveType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPrimitiveType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPrimitiveType(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PrimitiveType o = other as PrimitiveType; return o != null && MatchString(this.Keyword, o.Keyword); } public override string ToString(CSharpFormattingOptions formattingOptions) { return Keyword; } public static KnownTypeCode GetTypeCodeForPrimitiveType(string keyword) { switch (keyword) { case "string": return KnownTypeCode.String; case "int": return KnownTypeCode.Int32; case "uint": return KnownTypeCode.UInt32; case "object": return KnownTypeCode.Object; case "bool": return KnownTypeCode.Boolean; case "sbyte": return KnownTypeCode.SByte; case "byte": return KnownTypeCode.Byte; case "short": return KnownTypeCode.Int16; case "ushort": return KnownTypeCode.UInt16; case "long": return KnownTypeCode.Int64; case "ulong": return KnownTypeCode.UInt64; case "float": return KnownTypeCode.Single; case "double": return KnownTypeCode.Double; case "decimal": return KnownTypeCode.Decimal; case "char": return KnownTypeCode.Char; case "void": return KnownTypeCode.Void; default: return KnownTypeCode.None; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Role.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Threading; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Represents the role a node plays within its parent. /// public abstract class Role { public const int RoleIndexBits = 9; static readonly Role[] roles = new Role[1 << RoleIndexBits]; static int nextRoleIndex = 0; readonly uint index; public uint Index { get { return index; } } // don't allow NRefactory consumers to derive from Role internal Role() { this.index = (uint)Interlocked.Increment(ref nextRoleIndex); if (this.index >= roles.Length) throw new InvalidOperationException("Too many roles"); roles[this.index] = this; } /// /// Gets whether the specified node is valid in this role. /// public abstract bool IsValid(object node); /// /// Gets the role with the specified index. /// public static Role GetByIndex(uint index) { return roles[index]; } } /// /// Represents the role a node plays within its parent. /// All nodes with this role have type T. /// public class Role : Role where T : class? { readonly string name; // helps with debugging the AST readonly T nullObject; /// /// Gets the null object used when there's no node with this role. /// Not every role has a null object; this property returns null for roles without a null object. /// /// /// Roles used for non-collections should always have a null object, so that no AST property returns null. /// However, if a role used for collections only, it may leave out the null object. /// public T NullObject { get { return nullObject; } } public override bool IsValid(object node) { return node is T; } [Obsolete("Use the other overload explicitly specifying the nullObject.")] public Role(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); this.name = name; this.nullObject = null!; } public Role(string name, T nullObject) { this.name = name ?? throw new ArgumentNullException(nameof(name)); this.nullObject = nullObject; } public override string ToString() { return name; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs ================================================ // // Roles.cs // // Author: // Mike Krüger // // Copyright (c) 2012 Xamarin // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public static class Roles { public static readonly Role Root = AstNode.RootRole; // some pre defined constants for common roles public static readonly Role Identifier = new Role("Identifier", Syntax.Identifier.Null); public static readonly Role Body = new Role("Body", BlockStatement.Null); public static readonly Role Parameter = new Role("Parameter", null); public static readonly Role Argument = new Role("Argument", Syntax.Expression.Null); public static readonly Role Type = new Role("Type", AstType.Null); public static readonly Role Expression = new Role("Expression", Syntax.Expression.Null); public static readonly Role TargetExpression = new Role("Target", Syntax.Expression.Null); public readonly static Role Condition = new Role("Condition", Syntax.Expression.Null); public static readonly Role TypeParameter = new Role("TypeParameter", null); public static readonly Role TypeArgument = new Role("TypeArgument", AstType.Null); public readonly static Role Constraint = new Role("Constraint", null); public static readonly Role Variable = new Role("Variable", VariableInitializer.Null); public static readonly Role EmbeddedStatement = new Role("EmbeddedStatement", Statement.Null); public readonly static Role TypeMemberRole = new Role("TypeMember", null); public static readonly Role VariableDesignationRole = new Role("VariableDesignation", VariableDesignation.Null); // public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null); // public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null); // some pre defined constants for most used punctuation public static readonly TokenRole LPar = new TokenRole("("); public static readonly TokenRole RPar = new TokenRole(")"); public static readonly TokenRole LBracket = new TokenRole("["); public static readonly TokenRole RBracket = new TokenRole("]"); public static readonly TokenRole LBrace = new TokenRole("{"); public static readonly TokenRole RBrace = new TokenRole("}"); public static readonly TokenRole LChevron = new TokenRole("<"); public static readonly TokenRole RChevron = new TokenRole(">"); public static readonly TokenRole Comma = new TokenRole(","); public static readonly TokenRole Dot = new TokenRole("."); public static readonly TokenRole Semicolon = new TokenRole(";"); public static readonly TokenRole Assign = new TokenRole("="); public static readonly TokenRole Colon = new TokenRole(":"); public static readonly TokenRole DoubleColon = new TokenRole("::"); public static readonly TokenRole Arrow = new TokenRole("=>"); public static readonly Role Comment = new Role("Comment", null); public static readonly Role PreProcessorDirective = new Role("PreProcessorDirective", null); public readonly static Role BaseType = new Role("BaseType", AstType.Null); public static readonly Role Attribute = new Role("Attribute", null); public static readonly Role AttributeTargetRole = new Role("AttributeTarget", CSharpTokenNode.Null); public readonly static TokenRole WhereKeyword = new TokenRole("where"); public readonly static Role ConstraintTypeParameter = new Role("TypeParameter", SimpleType.Null); public readonly static TokenRole DelegateKeyword = new TokenRole("delegate"); public static readonly TokenRole ExternKeyword = new TokenRole("extern"); public static readonly TokenRole AliasKeyword = new TokenRole("alias"); public static readonly TokenRole NamespaceKeyword = new TokenRole("namespace"); public static readonly TokenRole EnumKeyword = new TokenRole("enum"); public static readonly TokenRole InterfaceKeyword = new TokenRole("interface"); public static readonly TokenRole StructKeyword = new TokenRole("struct"); public static readonly TokenRole ClassKeyword = new TokenRole("class"); public static readonly TokenRole RecordKeyword = new TokenRole("record"); public static readonly TokenRole RecordStructKeyword = new TokenRole("record"); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/SimpleType.cs ================================================ // // FullTypeName.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class SimpleType : AstType { #region Null public new static readonly SimpleType Null = new NullSimpleType(); sealed class NullSimpleType : SimpleType { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion public SimpleType() { } public SimpleType(string identifier) { this.Identifier = identifier; } public SimpleType(Identifier identifier) { this.IdentifierToken = identifier; } public SimpleType(string identifier, TextLocation location) { SetChildByRole(Roles.Identifier, Syntax.Identifier.Create(identifier, location)); } public SimpleType(string identifier, IEnumerable typeArguments) { this.Identifier = identifier; foreach (var arg in typeArguments) { AddChild(arg, Roles.TypeArgument); } } public SimpleType(string identifier, params AstType[] typeArguments) : this(identifier, (IEnumerable)typeArguments) { } public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public AstNodeCollection TypeArguments { get { return GetChildrenByRole(Roles.TypeArgument); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSimpleType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSimpleType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSimpleType(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SimpleType o = other as SimpleType; return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs ================================================ // // BlockStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// { Statements } /// public class BlockStatement : Statement, IEnumerable { public static readonly Role StatementRole = new Role("Statement", Statement.Null); #region Null public static readonly new BlockStatement Null = new NullBlockStatement(); sealed class NullBlockStatement : BlockStatement { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator BlockStatement(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : BlockStatement, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection Statements { get { return GetChildrenByRole(StatementRole); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitBlockStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitBlockStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitBlockStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { BlockStatement o = other as BlockStatement; return o != null && !o.IsNull && this.Statements.DoMatch(o.Statements, match); } public void Add(Statement statement) { AddChild(statement, StatementRole); } public void Add(Expression expression) { AddChild(new ExpressionStatement(expression), StatementRole); } IEnumerator IEnumerable.GetEnumerator() { return this.Statements.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.Statements.GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/BreakStatement.cs ================================================ // // BreakStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// break; /// public class BreakStatement : Statement { public static readonly TokenRole BreakKeywordRole = new TokenRole("break"); public CSharpTokenNode BreakToken { get { return GetChildByRole(BreakKeywordRole); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitBreakStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitBreakStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitBreakStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { BreakStatement o = other as BreakStatement; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/CheckedStatement.cs ================================================ // // CheckedStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// checked BodyBlock /// public class CheckedStatement : Statement { public static readonly TokenRole CheckedKeywordRole = new TokenRole("checked"); public CSharpTokenNode CheckedToken { get { return GetChildByRole(CheckedKeywordRole); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public CheckedStatement() { } public CheckedStatement(BlockStatement body) { AddChild(body, Roles.Body); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCheckedStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCheckedStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCheckedStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CheckedStatement o = other as CheckedStatement; return o != null && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ContinueStatement.cs ================================================ // // ContinueStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// continue; /// public class ContinueStatement : Statement { public static readonly TokenRole ContinueKeywordRole = new TokenRole("continue"); public CSharpTokenNode ContinueToken { get { return GetChildByRole(ContinueKeywordRole); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitContinueStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitContinueStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitContinueStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ContinueStatement o = other as ContinueStatement; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/DoWhileStatement.cs ================================================ // // DoWhileStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// "do EmbeddedStatement while(Condition);" /// public class DoWhileStatement : Statement { public static readonly TokenRole DoKeywordRole = new TokenRole("do"); public static readonly TokenRole WhileKeywordRole = new TokenRole("while"); public CSharpTokenNode DoToken { get { return GetChildByRole(DoKeywordRole); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public CSharpTokenNode WhileToken { get { return GetChildByRole(WhileKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Condition { get { return GetChildByRole(Roles.Condition); } set { SetChildByRole(Roles.Condition, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDoWhileStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDoWhileStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDoWhileStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DoWhileStatement o = other as DoWhileStatement; return o != null && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match) && this.Condition.DoMatch(o.Condition, match); } public DoWhileStatement() { } public DoWhileStatement(Expression condition, Statement embeddedStatement) { this.Condition = condition; this.EmbeddedStatement = embeddedStatement; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/EmptyStatement.cs ================================================ // // EmptyStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// ; /// public class EmptyStatement : Statement { public TextLocation Location { get; set; } public override TextLocation StartLocation { get { return Location; } } public override TextLocation EndLocation { get { return new TextLocation(Location.Line, Location.Column + 1); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitEmptyStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitEmptyStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitEmptyStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { EmptyStatement o = other as EmptyStatement; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ExpressionStatement.cs ================================================ // // ExpressionStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Expression; /// public class ExpressionStatement : Statement { public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitExpressionStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitExpressionStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitExpressionStatement(this, data); } public ExpressionStatement() { } public ExpressionStatement(Expression expression) { this.Expression = expression; } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ExpressionStatement o = other as ExpressionStatement; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/FixedStatement.cs ================================================ // // FixedStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// fixed (Type Variables) EmbeddedStatement /// public class FixedStatement : Statement { public static readonly TokenRole FixedKeywordRole = new TokenRole("fixed"); public CSharpTokenNode FixedToken { get { return GetChildByRole(FixedKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public AstNodeCollection Variables { get { return GetChildrenByRole(Roles.Variable); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitFixedStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitFixedStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitFixedStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { FixedStatement o = other as FixedStatement; return o != null && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForStatement.cs ================================================ // // ForStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// for (Initializers; Condition; Iterators) EmbeddedStatement /// public class ForStatement : Statement { public static readonly TokenRole ForKeywordRole = new TokenRole("for"); public readonly static Role InitializerRole = new Role("Initializer", Statement.Null); public readonly static Role IteratorRole = new Role("Iterator", Statement.Null); public CSharpTokenNode ForToken { get { return GetChildByRole(ForKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } /// /// Gets the list of initializer statements. /// Note: this contains multiple statements for "for (a = 2, b = 1; a > b; a--)", but contains /// only a single statement for "for (int a = 2, b = 1; a > b; a--)" (a single VariableDeclarationStatement with two variables) /// public AstNodeCollection Initializers { get { return GetChildrenByRole(InitializerRole); } } public Expression Condition { get { return GetChildByRole(Roles.Condition); } set { SetChildByRole(Roles.Condition, value); } } public AstNodeCollection Iterators { get { return GetChildrenByRole(IteratorRole); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitForStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitForStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitForStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ForStatement o = other as ForStatement; return o != null && this.Initializers.DoMatch(o.Initializers, match) && this.Condition.DoMatch(o.Condition, match) && this.Iterators.DoMatch(o.Iterators, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ForeachStatement.cs ================================================ // // ForeachStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// foreach (Type VariableName in InExpression) EmbeddedStatement /// public class ForeachStatement : Statement { public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole; public static readonly TokenRole ForeachKeywordRole = new TokenRole("foreach"); public static readonly TokenRole InKeywordRole = new TokenRole("in"); public CSharpTokenNode AwaitToken { get { return GetChildByRole(AwaitRole); } } public bool IsAsync { get { return !GetChildByRole(AwaitRole).IsNull; } set { SetChildByRole(AwaitRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } } public CSharpTokenNode ForeachToken { get { return GetChildByRole(ForeachKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType VariableType { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public VariableDesignation VariableDesignation { get { return GetChildByRole(Roles.VariableDesignationRole); } set { SetChildByRole(Roles.VariableDesignationRole, value); } } public CSharpTokenNode InToken { get { return GetChildByRole(InKeywordRole); } } public Expression InExpression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitForeachStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitForeachStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitForeachStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ForeachStatement o = other as ForeachStatement; return o != null && this.VariableType.DoMatch(o.VariableType, match) && this.VariableDesignation.DoMatch(o.VariableDesignation, match) && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/GotoStatement.cs ================================================ // // GotoStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// "goto Label;" /// public class GotoStatement : Statement { public static readonly TokenRole GotoKeywordRole = new TokenRole("goto"); public GotoStatement() { } public GotoStatement(string label) { this.Label = label; } public CSharpTokenNode GotoToken { get { return GetChildByRole(GotoKeywordRole); } } public string Label { get { return GetChildByRole(Roles.Identifier).Name; } set { if (string.IsNullOrEmpty(value)) SetChildByRole(Roles.Identifier, null); else SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitGotoStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitGotoStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitGotoStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { GotoStatement o = other as GotoStatement; return o != null && MatchString(this.Label, o.Label); } } /// /// or "goto case LabelExpression;" /// public class GotoCaseStatement : Statement { public static readonly TokenRole GotoKeywordRole = new TokenRole("goto"); public static readonly TokenRole CaseKeywordRole = new TokenRole("case"); public CSharpTokenNode GotoToken { get { return GetChildByRole(GotoKeywordRole); } } public CSharpTokenNode CaseToken { get { return GetChildByRole(CaseKeywordRole); } } /// /// Used for "goto case LabelExpression;" /// public Expression LabelExpression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitGotoCaseStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitGotoCaseStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitGotoCaseStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { GotoCaseStatement o = other as GotoCaseStatement; return o != null && this.LabelExpression.DoMatch(o.LabelExpression, match); } } /// /// or "goto default;" /// public class GotoDefaultStatement : Statement { public static readonly TokenRole GotoKeywordRole = new TokenRole("goto"); public static readonly TokenRole DefaultKeywordRole = new TokenRole("default"); public CSharpTokenNode GotoToken { get { return GetChildByRole(GotoKeywordRole); } } public CSharpTokenNode DefaultToken { get { return GetChildByRole(DefaultKeywordRole); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitGotoDefaultStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitGotoDefaultStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitGotoDefaultStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { GotoDefaultStatement o = other as GotoDefaultStatement; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/IfElseStatement.cs ================================================ // // IfElseStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// if (Condition) TrueStatement else FalseStatement /// public class IfElseStatement : Statement { public readonly static TokenRole IfKeywordRole = new TokenRole("if"); public readonly static Role ConditionRole = Roles.Condition; public readonly static Role TrueRole = new Role("True", Statement.Null); public readonly static TokenRole ElseKeywordRole = new TokenRole("else"); public readonly static Role FalseRole = new Role("False", Statement.Null); public CSharpTokenNode IfToken { get { return GetChildByRole(IfKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Condition { get { return GetChildByRole(ConditionRole); } set { SetChildByRole(ConditionRole, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement TrueStatement { get { return GetChildByRole(TrueRole); } set { SetChildByRole(TrueRole, value); } } public CSharpTokenNode ElseToken { get { return GetChildByRole(ElseKeywordRole); } } public Statement FalseStatement { get { return GetChildByRole(FalseRole); } set { SetChildByRole(FalseRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIfElseStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIfElseStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIfElseStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IfElseStatement o = other as IfElseStatement; return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueStatement.DoMatch(o.TrueStatement, match) && this.FalseStatement.DoMatch(o.FalseStatement, match); } public IfElseStatement() { } public IfElseStatement(Expression condition, Statement trueStatement, Statement falseStatement = null) { this.Condition = condition; this.TrueStatement = trueStatement; this.FalseStatement = falseStatement; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/LabelStatement.cs ================================================ // // LabelStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Label: /// public class LabelStatement : Statement { public string Label { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier LabelToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(Roles.Colon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitLabelStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitLabelStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitLabelStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { LabelStatement o = other as LabelStatement; return o != null && MatchString(this.Label, o.Label); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class LocalFunctionDeclarationStatement : Statement { public static readonly Role MethodDeclarationRole = new Role("Method", null); public MethodDeclaration Declaration { get { return GetChildByRole(MethodDeclarationRole); } set { SetChildByRole(MethodDeclarationRole, value); } } public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration) { AddChild(methodDeclaration, MethodDeclarationRole); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitLocalFunctionDeclarationStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitLocalFunctionDeclarationStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitLocalFunctionDeclarationStatement(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/LockStatement.cs ================================================ // // LockStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// lock (Expression) EmbeddedStatement; /// public class LockStatement : Statement { public static readonly TokenRole LockKeywordRole = new TokenRole("lock"); public CSharpTokenNode LockToken { get { return GetChildByRole(LockKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitLockStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitLockStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitLockStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { LockStatement o = other as LockStatement; return o != null && this.Expression.DoMatch(o.Expression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ReturnStatement.cs ================================================ // // ReturnStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// return Expression; /// public class ReturnStatement : Statement { public static readonly TokenRole ReturnKeywordRole = new TokenRole("return"); public CSharpTokenNode ReturnToken { get { return GetChildByRole(ReturnKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public ReturnStatement() { } public ReturnStatement(Expression returnExpression) { AddChild(returnExpression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitReturnStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitReturnStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitReturnStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ReturnStatement o = other as ReturnStatement; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Base class for statements. /// /// /// This class is useful even though it doesn't provide any additional functionality: /// It can be used to communicate more information in APIs, e.g. "this subnode will always be a statement" /// public abstract class Statement : AstNode { #region Null public new static readonly Statement Null = new NullStatement(); sealed class NullStatement : Statement { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator Statement(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : Statement, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public new Statement Clone() { return (Statement)base.Clone(); } public Statement ReplaceWith(Func replaceFunction) { if (replaceFunction == null) throw new ArgumentNullException(nameof(replaceFunction)); return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); } public override NodeType NodeType { get { return NodeType.Statement; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/SwitchStatement.cs ================================================ // // SwitchStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// switch (Expression) { SwitchSections } /// public class SwitchStatement : Statement { public static readonly TokenRole SwitchKeywordRole = new TokenRole("switch"); public static readonly Role SwitchSectionRole = new Role("SwitchSection", null); public CSharpTokenNode SwitchToken { get { return GetChildByRole(SwitchKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public AstNodeCollection SwitchSections { get { return GetChildrenByRole(SwitchSectionRole); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSwitchStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSwitchStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSwitchStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SwitchStatement o = other as SwitchStatement; return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); } } public class SwitchSection : AstNode { #region PatternPlaceholder public static implicit operator SwitchSection(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : SwitchSection, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public static readonly Role CaseLabelRole = new Role("CaseLabel", null); public override NodeType NodeType { get { return NodeType.Unknown; } } public AstNodeCollection CaseLabels { get { return GetChildrenByRole(CaseLabelRole); } } public AstNodeCollection Statements { get { return GetChildrenByRole(Roles.EmbeddedStatement); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSwitchSection(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSwitchSection(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSwitchSection(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SwitchSection o = other as SwitchSection; return o != null && this.CaseLabels.DoMatch(o.CaseLabels, match) && this.Statements.DoMatch(o.Statements, match); } } public class CaseLabel : AstNode { public static readonly TokenRole CaseKeywordRole = new TokenRole("case"); public static readonly TokenRole DefaultKeywordRole = new TokenRole("default"); public override NodeType NodeType { get { return NodeType.Unknown; } } /// /// Gets or sets the expression. The expression can be null - if the expression is null, it's the default switch section. /// public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(Roles.Colon); } } public CaseLabel() { } public CaseLabel(Expression expression) { this.Expression = expression; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCaseLabel(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCaseLabel(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCaseLabel(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CaseLabel o = other as CaseLabel; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/ThrowStatement.cs ================================================ // // ThrowStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// throw Expression; /// public class ThrowStatement : Statement { public static readonly TokenRole ThrowKeywordRole = new TokenRole("throw"); public CSharpTokenNode ThrowToken { get { return GetChildByRole(ThrowKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public ThrowStatement() { } public ThrowStatement(Expression expression) { AddChild(expression, Roles.Expression); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitThrowStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitThrowStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitThrowStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ThrowStatement o = other as ThrowStatement; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/TryCatchStatement.cs ================================================ // // TryCatchStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// try TryBlock CatchClauses finally FinallyBlock /// public class TryCatchStatement : Statement { public static readonly TokenRole TryKeywordRole = new TokenRole("try"); public static readonly Role TryBlockRole = new Role("TryBlock", BlockStatement.Null); public static readonly Role CatchClauseRole = new Role("CatchClause", CatchClause.Null); public static readonly TokenRole FinallyKeywordRole = new TokenRole("finally"); public static readonly Role FinallyBlockRole = new Role("FinallyBlock", BlockStatement.Null); public CSharpTokenNode TryToken { get { return GetChildByRole(TryKeywordRole); } } public BlockStatement TryBlock { get { return GetChildByRole(TryBlockRole); } set { SetChildByRole(TryBlockRole, value); } } public AstNodeCollection CatchClauses { get { return GetChildrenByRole(CatchClauseRole); } } public CSharpTokenNode FinallyToken { get { return GetChildByRole(FinallyKeywordRole); } } public BlockStatement FinallyBlock { get { return GetChildByRole(FinallyBlockRole); } set { SetChildByRole(FinallyBlockRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTryCatchStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTryCatchStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTryCatchStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { TryCatchStatement o = other as TryCatchStatement; return o != null && this.TryBlock.DoMatch(o.TryBlock, match) && this.CatchClauses.DoMatch(o.CatchClauses, match) && this.FinallyBlock.DoMatch(o.FinallyBlock, match); } } /// /// catch (Type VariableName) { Body } /// public class CatchClause : AstNode { public static readonly TokenRole CatchKeywordRole = new TokenRole("catch"); public static readonly TokenRole WhenKeywordRole = new TokenRole("when"); public static readonly Role ConditionRole = Roles.Condition; public static readonly TokenRole CondLPar = new TokenRole("("); public static readonly TokenRole CondRPar = new TokenRole(")"); #region Null public new static readonly CatchClause Null = new NullCatchClause(); sealed class NullCatchClause : CatchClause { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator CatchClause(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : CatchClause, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType { get { return NodeType.Unknown; } } public CSharpTokenNode CatchToken { get { return GetChildByRole(CatchKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public string VariableName { get { return GetChildByRole(Roles.Identifier).Name; } set { if (string.IsNullOrEmpty(value)) SetChildByRole(Roles.Identifier, null); else SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier VariableNameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CSharpTokenNode WhenToken { get { return GetChildByRole(WhenKeywordRole); } } public CSharpTokenNode CondLParToken { get { return GetChildByRole(CondLPar); } } public Expression Condition { get { return GetChildByRole(ConditionRole); } set { SetChildByRole(ConditionRole, value); } } public CSharpTokenNode CondRParToken { get { return GetChildByRole(CondRPar); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCatchClause(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCatchClause(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCatchClause(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CatchClause o = other as CatchClause; return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.VariableName, o.VariableName) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/UncheckedStatement.cs ================================================ // // UncheckedStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// unchecked BodyBlock /// public class UncheckedStatement : Statement { public static readonly TokenRole UncheckedKeywordRole = new TokenRole("unchecked"); public CSharpTokenNode UncheckedToken { get { return GetChildByRole(UncheckedKeywordRole); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public UncheckedStatement() { } public UncheckedStatement(BlockStatement body) { AddChild(body, Roles.Body); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUncheckedStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUncheckedStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUncheckedStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UncheckedStatement o = other as UncheckedStatement; return o != null && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/UnsafeStatement.cs ================================================ // // UnsafeStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// unsafe { Body } /// public class UnsafeStatement : Statement { public static readonly TokenRole UnsafeKeywordRole = new TokenRole("unsafe"); public CSharpTokenNode UnsafeToken { get { return GetChildByRole(UnsafeKeywordRole); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUnsafeStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUnsafeStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUnsafeStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UnsafeStatement o = other as UnsafeStatement; return o != null && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs ================================================ // // UsingStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// [ await ] using (ResourceAcquisition) EmbeddedStatement /// public class UsingStatement : Statement { public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole; public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); public CSharpTokenNode UsingToken { get { return GetChildByRole(UsingKeywordRole); } } public CSharpTokenNode AwaitToken { get { return GetChildByRole(AwaitRole); } } public bool IsAsync { get { return !GetChildByRole(AwaitRole).IsNull; } set { SetChildByRole(AwaitRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public bool IsEnhanced { get; set; } /// /// Either a VariableDeclarationStatement, or an Expression. /// public AstNode ResourceAcquisition { get { return GetChildByRole(ResourceAcquisitionRole); } set { SetChildByRole(ResourceAcquisitionRole, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitUsingStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitUsingStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitUsingStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UsingStatement o = other as UsingStatement; return o != null && this.IsAsync == o.IsAsync && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/VariableDeclarationStatement.cs ================================================ // // VariableDeclarationStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public class VariableDeclarationStatement : Statement { public static readonly Role ModifierRole = EntityDeclaration.ModifierRole; public VariableDeclarationStatement() { } public VariableDeclarationStatement(AstType type, string name, Expression initializer = null) { this.Type = type; this.Variables.Add(new VariableInitializer(name, initializer)); } public Modifiers Modifiers { get { return EntityDeclaration.GetModifiers(this); } set { EntityDeclaration.SetModifiers(this, value); } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public AstNodeCollection Variables { get { return GetChildrenByRole(Roles.Variable); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public VariableInitializer GetVariable(string name) { return Variables.FirstOrNullObject(vi => vi.Name == name); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitVariableDeclarationStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitVariableDeclarationStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitVariableDeclarationStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { VariableDeclarationStatement o = other as VariableDeclarationStatement; return o != null && this.Modifiers == o.Modifiers && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/WhileStatement.cs ================================================ // // WhileStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// "while (Condition) EmbeddedStatement" /// public class WhileStatement : Statement { public static readonly TokenRole WhileKeywordRole = new TokenRole("while"); public CSharpTokenNode WhileToken { get { return GetChildByRole(WhileKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public Expression Condition { get { return GetChildByRole(Roles.Condition); } set { SetChildByRole(Roles.Condition, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public Statement EmbeddedStatement { get { return GetChildByRole(Roles.EmbeddedStatement); } set { SetChildByRole(Roles.EmbeddedStatement, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitWhileStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitWhileStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitWhileStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { WhileStatement o = other as WhileStatement; return o != null && this.Condition.DoMatch(o.Condition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } public WhileStatement() { } public WhileStatement(Expression condition, Statement embeddedStatement) { this.Condition = condition; this.EmbeddedStatement = embeddedStatement; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/YieldBreakStatement.cs ================================================ // // YieldBreakStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// yield break; /// public class YieldBreakStatement : Statement { public static readonly TokenRole YieldKeywordRole = new TokenRole("yield"); public static readonly TokenRole BreakKeywordRole = new TokenRole("break"); public CSharpTokenNode YieldToken { get { return GetChildByRole(YieldKeywordRole); } } public CSharpTokenNode BreakToken { get { return GetChildByRole(BreakKeywordRole); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitYieldBreakStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitYieldBreakStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitYieldBreakStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { YieldBreakStatement o = other as YieldBreakStatement; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/Statements/YieldReturnStatement.cs ================================================ // // YieldStatement.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// yield return Expression; /// public class YieldReturnStatement : Statement { public static readonly TokenRole YieldKeywordRole = new TokenRole("yield"); public static readonly TokenRole ReturnKeywordRole = new TokenRole("return"); public CSharpTokenNode YieldToken { get { return GetChildByRole(YieldKeywordRole); } } public CSharpTokenNode ReturnToken { get { return GetChildByRole(ReturnKeywordRole); } } public Expression Expression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitYieldReturnStatement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitYieldReturnStatement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitYieldReturnStatement(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { YieldReturnStatement o = other as YieldReturnStatement; return o != null && this.Expression.DoMatch(o.Expression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs ================================================ // Copyright (c) 2013 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Extension methods for the syntax tree. /// public static class SyntaxExtensions { public static bool IsComparisonOperator(this OperatorType operatorType) { switch (operatorType) { case OperatorType.Equality: case OperatorType.Inequality: case OperatorType.GreaterThan: case OperatorType.LessThan: case OperatorType.GreaterThanOrEqual: case OperatorType.LessThanOrEqual: return true; default: return false; } } /// /// Returns true if is bitwise and, bitwise or, or exclusive or. /// public static bool IsBitwise(this BinaryOperatorType operatorType) { return operatorType == BinaryOperatorType.BitwiseAnd || operatorType == BinaryOperatorType.BitwiseOr || operatorType == BinaryOperatorType.ExclusiveOr; } public static Statement GetNextStatement(this Statement statement) { AstNode next = statement.NextSibling; while (next != null && !(next is Statement)) next = next.NextSibling; return (Statement)next; } public static bool IsArgList(this AstType type) { var simpleType = type as SimpleType; return simpleType != null && simpleType.Identifier == "__arglist"; } public static void AddNamedArgument(this Syntax.Attribute attribute, string name, Expression argument) { attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument)); } public static T Detach(this T node) where T : AstNode { node.Remove(); return node; } public static Expression UnwrapInDirectionExpression(this Expression expr) { if (!(expr is DirectionExpression dir && dir.FieldDirection == FieldDirection.In)) return expr; return dir.Expression.Detach(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs ================================================ // // SyntaxTree.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class SyntaxTree : AstNode { public static readonly Role MemberRole = new Role("Member", AstNode.Null); public override NodeType NodeType { get { return NodeType.Unknown; } } string fileName; /// /// Gets/Sets the file name of this syntax tree. /// public string FileName { get { return fileName; } set { ThrowIfFrozen(); fileName = value; } } public AstNodeCollection Members { get { return GetChildrenByRole(MemberRole); } } IList conditionalSymbols = null; /// /// Gets the conditional symbols used to parse the source file. Note that this list contains /// the conditional symbols at the start of the first token in the file - including the ones defined /// in the source file. /// public IList ConditionalSymbols { get { return conditionalSymbols ?? EmptyList.Instance; } internal set { conditionalSymbols = value; } } /// /// Gets the expression that was on top of the parse stack. /// This is the only way to get an expression that isn't part of a statment. /// (eg. when an error follows an expression). /// /// This is used for code completion to 'get the expression before a token - like ., <, ('. /// public AstNode TopExpression { get; internal set; } public SyntaxTree() { } /// /// Gets all defined types in this syntax tree. /// /// /// A list containing or nodes. /// public IEnumerable GetTypes(bool includeInnerTypes = false) { Stack nodeStack = new Stack(); nodeStack.Push(this); while (nodeStack.Count > 0) { var curNode = nodeStack.Pop(); if (curNode is TypeDeclaration || curNode is DelegateDeclaration) { yield return (EntityDeclaration)curNode; } foreach (var child in curNode.Children) { if (!(child is Statement || child is Expression) && (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) nodeStack.Push(child); } } } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { SyntaxTree o = other as SyntaxTree; return o != null && this.Members.DoMatch(o.Members, match); } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSyntaxTree(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSyntaxTree(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSyntaxTree(this, data); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs ================================================ // Copyright (c) 2010-2013 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 // 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.ComponentModel; using System.Globalization; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// A line/column position. /// Text editor lines/columns are counted started from one. /// [Serializable] [TypeConverter(typeof(TextLocationConverter))] public struct TextLocation : IComparable, IEquatable { /// /// Represents no text location (0, 0). /// public static readonly TextLocation Empty = new TextLocation(0, 0); /// /// Constant of the minimum line. /// public const int MinLine = 1; /// /// Constant of the minimum column. /// public const int MinColumn = 1; /// /// Creates a TextLocation instance. /// public TextLocation(int line, int column) { this.line = line; this.column = column; } int column, line; /// /// Gets the line number. /// public int Line { get { return line; } } /// /// Gets the column number. /// public int Column { get { return column; } } /// /// Gets whether the TextLocation instance is empty. /// public bool IsEmpty { get { return column < MinLine && line < MinColumn; } } /// /// Gets a string representation for debugging purposes. /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.column, this.line); } /// /// Gets a hash code. /// public override int GetHashCode() { return unchecked(191 * column.GetHashCode() ^ line.GetHashCode()); } /// /// Equality test. /// public override bool Equals(object obj) { if (!(obj is TextLocation)) return false; return (TextLocation)obj == this; } /// /// Equality test. /// public bool Equals(TextLocation other) { return this == other; } /// /// Equality test. /// public static bool operator ==(TextLocation left, TextLocation right) { return left.column == right.column && left.line == right.line; } /// /// Inequality test. /// public static bool operator !=(TextLocation left, TextLocation right) { return left.column != right.column || left.line != right.line; } /// /// Compares two text locations. /// public static bool operator <(TextLocation left, TextLocation right) { if (left.line < right.line) return true; else if (left.line == right.line) return left.column < right.column; else return false; } /// /// Compares two text locations. /// public static bool operator >(TextLocation left, TextLocation right) { if (left.line > right.line) return true; else if (left.line == right.line) return left.column > right.column; else return false; } /// /// Compares two text locations. /// public static bool operator <=(TextLocation left, TextLocation right) { return !(left > right); } /// /// Compares two text locations. /// public static bool operator >=(TextLocation left, TextLocation right) { return !(left < right); } /// /// Compares two text locations. /// public int CompareTo(TextLocation other) { if (this == other) return 0; if (this < other) return -1; else return 1; } } public class TextLocationConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(TextLocation) || base.CanConvertTo(context, destinationType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string) { string[] parts = ((string)value).Split(';', ','); if (parts.Length == 2) { return new TextLocation(int.Parse(parts[0]), int.Parse(parts[1])); } } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is TextLocation) { var loc = (TextLocation)value; return loc.Line + ";" + loc.Column; } return base.ConvertTo(context, culture, value, destinationType); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TokenRole.cs ================================================ namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// A specific role only used for C# tokens /// public sealed class TokenRole : Role { /// /// Gets the token as string. Note that the token Name and Token value may differ. /// public string Token { get; } /// /// Gets the char length of the token. /// public int Length { get; } public TokenRole(string token) : base(token, CSharpTokenNode.Null) { this.Token = token; this.Length = token.Length; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class TupleAstType : AstType { public static readonly Role ElementRole = new Role("Element", TupleTypeElement.Null); public AstNodeCollection Elements { get { return GetChildrenByRole(ElementRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTupleType(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTupleType(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTupleType(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is TupleAstType o && Elements.DoMatch(o.Elements, match); } } public class TupleTypeElement : AstNode { #region Null public new static readonly TupleTypeElement Null = new TupleTypeElement(); sealed class NullTupleTypeElement : TupleTypeElement { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public override NodeType NodeType => NodeType.Unknown; public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTupleTypeElement(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitTupleTypeElement(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitTupleTypeElement(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is TupleTypeElement o && Type.DoMatch(o.Type, match) && MatchString(Name, o.Name); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs ================================================ // // PropertyDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// get/set/init/add/remove /// public class Accessor : EntityDeclaration { public static readonly new Accessor Null = new NullAccessor(); sealed class NullAccessor : Accessor { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } public override NodeType NodeType { get { return NodeType.Unknown; } } public override SymbolKind SymbolKind { get { return SymbolKind.Method; } } /// /// Gets the 'get'/'set'/'init'/'add'/'remove' keyword /// public CSharpTokenNode Keyword { get { for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) { if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole || child.Role == PropertyDeclaration.InitKeywordRole || child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole) { return (CSharpTokenNode)child; } } return CSharpTokenNode.Null; } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitAccessor(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitAccessor(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAccessor(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { Accessor o = other as Accessor; return o != null && !o.IsNull && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ConstructorDeclaration.cs ================================================ // // ConstructorDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ConstructorDeclaration : EntityDeclaration { public static readonly Role InitializerRole = new Role("Initializer", ConstructorInitializer.Null); public override SymbolKind SymbolKind { get { return SymbolKind.Constructor; } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public CSharpTokenNode ColonToken { get { return GetChildByRole(Roles.Colon); } } public ConstructorInitializer Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitConstructorDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitConstructorDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitConstructorDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ConstructorDeclaration o = other as ConstructorDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.Parameters.DoMatch(o.Parameters, match) && this.Initializer.DoMatch(o.Initializer, match) && this.Body.DoMatch(o.Body, match); } } public enum ConstructorInitializerType { Any, Base, This } public class ConstructorInitializer : AstNode { public static readonly TokenRole BaseKeywordRole = new TokenRole("base"); public static readonly TokenRole ThisKeywordRole = new TokenRole("this"); public static readonly new ConstructorInitializer Null = new NullConstructorInitializer(); class NullConstructorInitializer : ConstructorInitializer { public override NodeType NodeType { get { return NodeType.Unknown; } } public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } public override NodeType NodeType { get { return NodeType.Unknown; } } public ConstructorInitializerType ConstructorInitializerType { get; set; } public CSharpTokenNode Keyword { get { if (ConstructorInitializerType == ConstructorInitializerType.Base) return GetChildByRole(BaseKeywordRole); else return GetChildByRole(ThisKeywordRole); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitConstructorInitializer(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitConstructorInitializer(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitConstructorInitializer(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { ConstructorInitializer o = other as ConstructorInitializer; return o != null && !o.IsNull && (this.ConstructorInitializerType == ConstructorInitializerType.Any || this.ConstructorInitializerType == o.ConstructorInitializerType) && this.Arguments.DoMatch(o.Arguments, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/DestructorDeclaration.cs ================================================ // // DestructorDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class DestructorDeclaration : EntityDeclaration { public static readonly TokenRole TildeRole = new TokenRole("~"); public CSharpTokenNode TildeToken { get { return GetChildByRole(TildeRole); } } public override SymbolKind SymbolKind { get { return SymbolKind.Destructor; } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitDestructorDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitDestructorDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitDestructorDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { DestructorDeclaration o = other as DestructorDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public abstract class EntityDeclaration : AstNode { public static readonly Role AttributeRole = new Role("Attribute", null); public static readonly Role ModifierRole = new Role("Modifier", null); public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); public override NodeType NodeType { get { return NodeType.Member; } } public abstract SymbolKind SymbolKind { get; } public AstNodeCollection Attributes { get { return base.GetChildrenByRole(AttributeRole); } } public Modifiers Modifiers { get { return GetModifiers(this); } set { SetModifiers(this, value); } } public bool HasModifier(Modifiers mod) { return (Modifiers & mod) == mod; } public IEnumerable ModifierTokens { get { return GetChildrenByRole(ModifierRole); } } public virtual string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); } } public virtual Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public virtual AstType ReturnType { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public CSharpTokenNode SemicolonToken { get { return GetChildByRole(Roles.Semicolon); } } internal static Modifiers GetModifiers(AstNode node) { Modifiers m = 0; foreach (CSharpModifierToken t in node.GetChildrenByRole(ModifierRole)) { m |= t.Modifier; } return m; } internal static void SetModifiers(AstNode node, Modifiers newValue) { Modifiers oldValue = GetModifiers(node); AstNode insertionPos = node.GetChildrenByRole(AttributeRole).LastOrDefault(); foreach (Modifiers m in CSharpModifierToken.AllModifiers) { if ((m & newValue) != 0) { if ((m & oldValue) == 0) { // Modifier was added var newToken = new CSharpModifierToken(TextLocation.Empty, m); node.InsertChildAfter(insertionPos, newToken, ModifierRole); insertionPos = newToken; } else { // Modifier already exists insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); } } else { if ((m & oldValue) != 0) { // Modifier was removed node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m).Remove(); } } } } protected bool MatchAttributesAndModifiers(EntityDeclaration o, PatternMatching.Match match) { return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch(o.Attributes, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EnumMemberDeclaration.cs ================================================ // // EnumMemberDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class EnumMemberDeclaration : EntityDeclaration { public static readonly Role InitializerRole = new Role("Initializer", Expression.Null); public override SymbolKind SymbolKind { get { return SymbolKind.Field; } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression Initializer { get { return GetChildByRole(InitializerRole); } set { SetChildByRole(InitializerRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitEnumMemberDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitEnumMemberDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitEnumMemberDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { EnumMemberDeclaration o = other as EnumMemberDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EventDeclaration.cs ================================================ // // EventDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.ComponentModel; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class EventDeclaration : EntityDeclaration { public static readonly TokenRole EventKeywordRole = new TokenRole("event"); public override SymbolKind SymbolKind { get { return SymbolKind.Event; } } public CSharpTokenNode EventToken { get { return GetChildByRole(EventKeywordRole); } } public AstNodeCollection Variables { get { return GetChildrenByRole(Roles.Variable); } } // Hide .Name and .NameToken from users; the actual field names // are stored in the VariableInitializer. [EditorBrowsable(EditorBrowsableState.Never)] public override string Name { get { return string.Empty; } set { throw new NotSupportedException(); } } [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { get { return Identifier.Null; } set { throw new NotSupportedException(); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitEventDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitEventDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitEventDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { EventDeclaration o = other as EventDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); } } public class CustomEventDeclaration : EntityDeclaration { public static readonly TokenRole EventKeywordRole = new TokenRole("event"); public static readonly TokenRole AddKeywordRole = new TokenRole("add"); public static readonly TokenRole RemoveKeywordRole = new TokenRole("remove"); public static readonly Role AddAccessorRole = new Role("AddAccessor", Accessor.Null); public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", Accessor.Null); public override SymbolKind SymbolKind { get { return SymbolKind.Event; } } /// /// Gets/Sets the type reference of the interface that is explicitly implemented. /// Null node if this member is not an explicit interface implementation. /// public AstType PrivateImplementationType { get { return GetChildByRole(PrivateImplementationTypeRole); } set { SetChildByRole(PrivateImplementationTypeRole, value); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public Accessor AddAccessor { get { return GetChildByRole(AddAccessorRole); } set { SetChildByRole(AddAccessorRole, value); } } public Accessor RemoveAccessor { get { return GetChildByRole(RemoveAccessorRole); } set { SetChildByRole(RemoveAccessorRole, value); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitCustomEventDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitCustomEventDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitCustomEventDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { CustomEventDeclaration o = other as CustomEventDeclaration; return o != null && MatchString(this.Name, o.Name) && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.AddAccessor.DoMatch(o.AddAccessor, match) && this.RemoveAccessor.DoMatch(o.RemoveAccessor, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs ================================================ // Copyright (c) 2025 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ExtensionDeclaration : EntityDeclaration { public readonly static TokenRole ExtensionKeywordRole = new TokenRole("extension"); public override SymbolKind SymbolKind => throw new System.NotImplementedException(); public AstNodeCollection TypeParameters { get { return GetChildrenByRole(Roles.TypeParameter); } } public AstNodeCollection ReceiverParameters { get { return GetChildrenByRole(Roles.Parameter); } } public AstNodeCollection Constraints { get { return GetChildrenByRole(Roles.Constraint); } } public AstNodeCollection Members { get { return GetChildrenByRole(Roles.TypeMemberRole); } } public ExtensionDeclaration() { } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitExtensionDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitExtensionDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitExtensionDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as ExtensionDeclaration; return o != null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FieldDeclaration.cs ================================================ // // FieldDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.ComponentModel; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class FieldDeclaration : EntityDeclaration { public override SymbolKind SymbolKind { get { return SymbolKind.Field; } } public AstNodeCollection Variables { get { return GetChildrenByRole(Roles.Variable); } } // Hide .Name and .NameToken from users; the actual field names // are stored in the VariableInitializer. [EditorBrowsable(EditorBrowsableState.Never)] public override string Name { get { return string.Empty; } set { throw new NotSupportedException(); } } [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { get { return Identifier.Null; } set { throw new NotSupportedException(); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitFieldDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitFieldDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitFieldDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { FieldDeclaration o = other as FieldDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FixedFieldDeclaration.cs ================================================ // // FixedFieldDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class FixedFieldDeclaration : EntityDeclaration { public static readonly TokenRole FixedKeywordRole = new TokenRole("fixed"); public static readonly Role VariableRole = new Role("FixedVariable", null); public override SymbolKind SymbolKind { get { return SymbolKind.Field; } } public CSharpTokenNode FixedToken { get { return GetChildByRole(FixedKeywordRole); } } public AstNodeCollection Variables { get { return GetChildrenByRole(VariableRole); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitFixedFieldDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitFixedFieldDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitFixedFieldDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as FixedFieldDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/FixedVariableInitializer.cs ================================================ // // FixedFieldDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2011 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Name [ CountExpression ] /// public class FixedVariableInitializer : AstNode { public override NodeType NodeType { get { return NodeType.Unknown; } } public FixedVariableInitializer() { } public FixedVariableInitializer(string name, Expression initializer = null) { this.Name = name; this.CountExpression = initializer; } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public Expression CountExpression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitFixedVariableInitializer(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitFixedVariableInitializer(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitFixedVariableInitializer(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { var o = other as FixedVariableInitializer; return o != null && MatchString(this.Name, o.Name) && this.CountExpression.DoMatch(o.CountExpression, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/IndexerDeclaration.cs ================================================ // // IndexerDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.ComponentModel; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class IndexerDeclaration : EntityDeclaration { public static readonly TokenRole ThisKeywordRole = new TokenRole("this"); public static readonly Role GetterRole = PropertyDeclaration.GetterRole; public static readonly Role SetterRole = PropertyDeclaration.SetterRole; public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", Expression.Null); public override SymbolKind SymbolKind { get { return SymbolKind.Indexer; } } /// /// Gets/Sets the type reference of the interface that is explicitly implemented. /// Null node if this member is not an explicit interface implementation. /// public AstType PrivateImplementationType { get { return GetChildByRole(PrivateImplementationTypeRole); } set { SetChildByRole(PrivateImplementationTypeRole, value); } } public override string Name { get { return "Item"; } set { throw new NotSupportedException(); } } [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { get { return Identifier.Null; } set { throw new NotSupportedException(); } } public CSharpTokenNode LBracketToken { get { return GetChildByRole(Roles.LBracket); } } public CSharpTokenNode ThisToken { get { return GetChildByRole(ThisKeywordRole); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RBracketToken { get { return GetChildByRole(Roles.RBracket); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public Accessor Getter { get { return GetChildByRole(GetterRole); } set { SetChildByRole(GetterRole, value); } } public Accessor Setter { get { return GetChildByRole(SetterRole); } set { SetChildByRole(SetterRole, value); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public Expression ExpressionBody { get { return GetChildByRole(ExpressionBodyRole); } set { SetChildByRole(ExpressionBodyRole, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitIndexerDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitIndexerDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitIndexerDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IndexerDeclaration o = other as IndexerDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.Parameters.DoMatch(o.Parameters, match) && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match) && this.ExpressionBody.DoMatch(o.ExpressionBody, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs ================================================ // // MethodDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class MethodDeclaration : EntityDeclaration { public override SymbolKind SymbolKind { get { return SymbolKind.Method; } } /// /// Gets/Sets the type reference of the interface that is explicitly implemented. /// Null node if this member is not an explicit interface implementation. /// public AstType PrivateImplementationType { get { return GetChildByRole(PrivateImplementationTypeRole); } set { SetChildByRole(PrivateImplementationTypeRole, value); } } public AstNodeCollection TypeParameters { get { return GetChildrenByRole(Roles.TypeParameter); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public AstNodeCollection Constraints { get { return GetChildrenByRole(Roles.Constraint); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } public bool IsExtensionMethod { get { ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole(Roles.Parameter); return pd != null && pd.HasThisModifier; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitMethodDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitMethodDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitMethodDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { MethodDeclaration o = other as MethodDeclaration; return o != null && MatchString(this.Name, o.Name) && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs ================================================ // // OperatorDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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.ComponentModel; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public enum OperatorType { // Unary operators LogicalNot, OnesComplement, Increment, CheckedIncrement, Decrement, CheckedDecrement, True, False, UnaryPlus, UnaryNegation, CheckedUnaryNegation, // Binary operators Addition, CheckedAddition, Subtraction, CheckedSubtraction, Multiply, CheckedMultiply, Division, CheckedDivision, Modulus, BitwiseAnd, BitwiseOr, ExclusiveOr, LeftShift, RightShift, UnsignedRightShift, Equality, Inequality, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, // Implicit and Explicit Implicit, Explicit, CheckedExplicit } public class OperatorDeclaration : EntityDeclaration { public static readonly TokenRole OperatorKeywordRole = new TokenRole("operator"); public static readonly TokenRole CheckedKeywordRole = new TokenRole("checked"); // Unary operators public static readonly TokenRole LogicalNotRole = new TokenRole("!"); public static readonly TokenRole OnesComplementRole = new TokenRole("~"); public static readonly TokenRole IncrementRole = new TokenRole("++"); public static readonly TokenRole DecrementRole = new TokenRole("--"); public static readonly TokenRole TrueRole = new TokenRole("true"); public static readonly TokenRole FalseRole = new TokenRole("false"); // Unary and Binary operators public static readonly TokenRole AdditionRole = new TokenRole("+"); public static readonly TokenRole SubtractionRole = new TokenRole("-"); // Binary operators public static readonly TokenRole MultiplyRole = new TokenRole("*"); public static readonly TokenRole DivisionRole = new TokenRole("/"); public static readonly TokenRole ModulusRole = new TokenRole("%"); public static readonly TokenRole BitwiseAndRole = new TokenRole("&"); public static readonly TokenRole BitwiseOrRole = new TokenRole("|"); public static readonly TokenRole ExclusiveOrRole = new TokenRole("^"); public static readonly TokenRole LeftShiftRole = new TokenRole("<<"); public static readonly TokenRole RightShiftRole = new TokenRole(">>"); public static readonly TokenRole UnsignedRightShiftRole = new TokenRole(">>>"); public static readonly TokenRole EqualityRole = new TokenRole("=="); public static readonly TokenRole InequalityRole = new TokenRole("!="); public static readonly TokenRole GreaterThanRole = new TokenRole(">"); public static readonly TokenRole LessThanRole = new TokenRole("<"); public static readonly TokenRole GreaterThanOrEqualRole = new TokenRole(">="); public static readonly TokenRole LessThanOrEqualRole = new TokenRole("<="); public static readonly TokenRole ExplicitRole = new TokenRole("explicit"); public static readonly TokenRole ImplicitRole = new TokenRole("implicit"); static readonly string[][] names; static OperatorDeclaration() { names = new string[(int)OperatorType.CheckedExplicit + 1][]; names[(int)OperatorType.LogicalNot] = new string[] { "!", "op_LogicalNot" }; names[(int)OperatorType.OnesComplement] = new string[] { "~", "op_OnesComplement" }; names[(int)OperatorType.Increment] = new string[] { "++", "op_Increment" }; names[(int)OperatorType.CheckedIncrement] = new string[] { "++", "op_CheckedIncrement" }; names[(int)OperatorType.Decrement] = new string[] { "--", "op_Decrement" }; names[(int)OperatorType.CheckedDecrement] = new string[] { "--", "op_CheckedDecrement" }; names[(int)OperatorType.True] = new string[] { "true", "op_True" }; names[(int)OperatorType.False] = new string[] { "false", "op_False" }; names[(int)OperatorType.UnaryPlus] = new string[] { "+", "op_UnaryPlus" }; names[(int)OperatorType.UnaryNegation] = new string[] { "-", "op_UnaryNegation" }; names[(int)OperatorType.CheckedUnaryNegation] = new string[] { "-", "op_CheckedUnaryNegation" }; names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" }; names[(int)OperatorType.CheckedAddition] = new string[] { "+", "op_CheckedAddition" }; names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" }; names[(int)OperatorType.CheckedSubtraction] = new string[] { "-", "op_CheckedSubtraction" }; names[(int)OperatorType.Multiply] = new string[] { "*", "op_Multiply" }; names[(int)OperatorType.CheckedMultiply] = new string[] { "*", "op_CheckedMultiply" }; names[(int)OperatorType.Division] = new string[] { "/", "op_Division" }; names[(int)OperatorType.CheckedDivision] = new string[] { "/", "op_CheckedDivision" }; names[(int)OperatorType.Modulus] = new string[] { "%", "op_Modulus" }; names[(int)OperatorType.BitwiseAnd] = new string[] { "&", "op_BitwiseAnd" }; names[(int)OperatorType.BitwiseOr] = new string[] { "|", "op_BitwiseOr" }; names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" }; names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" }; names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" }; names[(int)OperatorType.UnsignedRightShift] = new string[] { ">>>", "op_UnsignedRightShift" }; names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" }; names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" }; names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" }; names[(int)OperatorType.LessThan] = new string[] { "<", "op_LessThan" }; names[(int)OperatorType.GreaterThanOrEqual] = new string[] { ">=", "op_GreaterThanOrEqual" }; names[(int)OperatorType.LessThanOrEqual] = new string[] { "<=", "op_LessThanOrEqual" }; names[(int)OperatorType.Implicit] = new string[] { "implicit", "op_Implicit" }; names[(int)OperatorType.Explicit] = new string[] { "explicit", "op_Explicit" }; names[(int)OperatorType.CheckedExplicit] = new string[] { "explicit", "op_CheckedExplicit" }; } public override SymbolKind SymbolKind { get { return SymbolKind.Operator; } } /// /// Gets/Sets the type reference of the interface that is explicitly implemented. /// Null node if this member is not an explicit interface implementation. /// public AstType PrivateImplementationType { get { return GetChildByRole(PrivateImplementationTypeRole); } set { SetChildByRole(PrivateImplementationTypeRole, value); } } OperatorType operatorType; public OperatorType OperatorType { get { return operatorType; } set { ThrowIfFrozen(); operatorType = value; } } public CSharpTokenNode OperatorToken { get { return GetChildByRole(OperatorKeywordRole); } } public CSharpTokenNode OperatorTypeToken { get { return GetChildByRole(GetRole(OperatorType)); } } public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection Parameters { get { return GetChildrenByRole(Roles.Parameter); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public BlockStatement Body { get { return GetChildByRole(Roles.Body); } set { SetChildByRole(Roles.Body, value); } } /// /// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types. /// public static OperatorType? GetOperatorType(string methodName) { for (int i = 0; i < names.Length; ++i) { if (names[i][1] == methodName) return (OperatorType)i; } return null; } public static TokenRole GetRole(OperatorType type) { switch (type) { case OperatorType.LogicalNot: return LogicalNotRole; case OperatorType.OnesComplement: return OnesComplementRole; case OperatorType.Increment: case OperatorType.CheckedIncrement: return IncrementRole; case OperatorType.Decrement: case OperatorType.CheckedDecrement: return DecrementRole; case OperatorType.True: return TrueRole; case OperatorType.False: return FalseRole; case OperatorType.Addition: case OperatorType.CheckedAddition: case OperatorType.UnaryPlus: return AdditionRole; case OperatorType.Subtraction: case OperatorType.CheckedSubtraction: case OperatorType.UnaryNegation: case OperatorType.CheckedUnaryNegation: return SubtractionRole; case OperatorType.Multiply: case OperatorType.CheckedMultiply: return MultiplyRole; case OperatorType.Division: case OperatorType.CheckedDivision: return DivisionRole; case OperatorType.Modulus: return ModulusRole; case OperatorType.BitwiseAnd: return BitwiseAndRole; case OperatorType.BitwiseOr: return BitwiseOrRole; case OperatorType.ExclusiveOr: return ExclusiveOrRole; case OperatorType.LeftShift: return LeftShiftRole; case OperatorType.RightShift: return RightShiftRole; case OperatorType.UnsignedRightShift: return UnsignedRightShiftRole; case OperatorType.Equality: return EqualityRole; case OperatorType.Inequality: return InequalityRole; case OperatorType.GreaterThan: return GreaterThanRole; case OperatorType.LessThan: return LessThanRole; case OperatorType.GreaterThanOrEqual: return GreaterThanOrEqualRole; case OperatorType.LessThanOrEqual: return LessThanOrEqualRole; case OperatorType.Implicit: return ImplicitRole; case OperatorType.Explicit: case OperatorType.CheckedExplicit: return ExplicitRole; default: throw new System.ArgumentOutOfRangeException(); } } /// /// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.) /// public static string GetName(OperatorType? type) { if (type == null) return null; return names[(int)type][1]; } /// /// Gets whether the operator type is a C# 11 "operator checked". /// public static bool IsChecked(OperatorType type) { return type switch { OperatorType.CheckedAddition => true, OperatorType.CheckedSubtraction => true, OperatorType.CheckedMultiply => true, OperatorType.CheckedDivision => true, OperatorType.CheckedUnaryNegation => true, OperatorType.CheckedIncrement => true, OperatorType.CheckedDecrement => true, OperatorType.CheckedExplicit => true, _ => false, }; } /// /// Gets the token for the operator type ("+", "implicit", etc.). /// Does not include the "checked" modifier. /// public static string GetToken(OperatorType type) { return names[(int)type][0]; } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitOperatorDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitOperatorDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitOperatorDeclaration(this, data); } public override string Name { get { return GetName(this.OperatorType); } set { throw new NotSupportedException(); } } [EditorBrowsable(EditorBrowsableState.Never)] public override Identifier NameToken { get { return Identifier.Null; } set { throw new NotSupportedException(); } } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { OperatorDeclaration o = other as OperatorDeclaration; return o != null && this.MatchAttributesAndModifiers(o, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.OperatorType == o.OperatorType && this.ReturnType.DoMatch(o.ReturnType, match) && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs ================================================ // // ParameterDeclarationExpression.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // 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. #nullable enable using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ParameterDeclaration : AstNode { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole ThisModifierRole = new TokenRole("this"); public static readonly TokenRole ScopedRefRole = new TokenRole("scoped"); public static readonly TokenRole RefModifierRole = new TokenRole("ref"); public static readonly TokenRole ReadonlyModifierRole = ComposedType.ReadonlyRole; public static readonly TokenRole OutModifierRole = new TokenRole("out"); public static readonly TokenRole InModifierRole = new TokenRole("in"); public static readonly TokenRole ParamsModifierRole = new TokenRole("params"); #region PatternPlaceholder public static implicit operator ParameterDeclaration?(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : ParameterDeclaration, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType => NodeType.Unknown; public AstNodeCollection Attributes { get { return GetChildrenByRole(AttributeRole); } } bool hasThisModifier; bool isParams; bool isScopedRef; public CSharpTokenNode ThisKeyword { get { if (hasThisModifier) { return GetChildByRole(ThisModifierRole); } return CSharpTokenNode.Null; } } public bool HasThisModifier { get { return hasThisModifier; } set { ThrowIfFrozen(); hasThisModifier = value; } } public bool IsParams { get { return isParams; } set { ThrowIfFrozen(); isParams = value; } } public bool IsScopedRef { get { return isScopedRef; } set { ThrowIfFrozen(); isScopedRef = value; } } ReferenceKind parameterModifier; public ReferenceKind ParameterModifier { get { return parameterModifier; } set { ThrowIfFrozen(); parameterModifier = value; } } public AstType Type { get { return GetChildByRole(Roles.Type); } set { SetChildByRole(Roles.Type, value); } } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression DefaultExpression { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitParameterDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitParameterDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitParameterDeclaration(this, data); } protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) { var o = other as ParameterDeclaration; return o != null && this.Attributes.DoMatch(o.Attributes, match) && this.ParameterModifier == o.ParameterModifier && this.Type.DoMatch(o.Type, match) && MatchString(this.Name, o.Name) && this.DefaultExpression.DoMatch(o.DefaultExpression, match); } public ParameterDeclaration() { } public new ParameterDeclaration Clone() { return (ParameterDeclaration)base.Clone(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs ================================================ // // PropertyDeclaration.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class PropertyDeclaration : EntityDeclaration { public static readonly TokenRole GetKeywordRole = new TokenRole("get"); public static readonly TokenRole SetKeywordRole = new TokenRole("set"); public static readonly TokenRole InitKeywordRole = new TokenRole("init"); public static readonly Role GetterRole = new Role("Getter", Accessor.Null); public static readonly Role SetterRole = new Role("Setter", Accessor.Null); public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", Expression.Null); public override SymbolKind SymbolKind { get { return SymbolKind.Property; } } /// /// Gets/Sets the type reference of the interface that is explicitly implemented. /// Null node if this member is not an explicit interface implementation. /// public AstType PrivateImplementationType { get { return GetChildByRole(PrivateImplementationTypeRole); } set { SetChildByRole(PrivateImplementationTypeRole, value); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole(Roles.LBrace); } } public Accessor Getter { get { return GetChildByRole(GetterRole); } set { SetChildByRole(GetterRole, value); } } public Accessor Setter { get { return GetChildByRole(SetterRole); } set { SetChildByRole(SetterRole, value); } } public CSharpTokenNode RBraceToken { get { return GetChildByRole(Roles.RBrace); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression Initializer { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public Expression ExpressionBody { get { return GetChildByRole(ExpressionBodyRole); } set { SetChildByRole(ExpressionBodyRole, value); } } public bool IsAutomaticProperty { get { if (!Getter.IsNull && !Getter.Body.IsNull) { return false; } if (!Setter.IsNull && !Setter.Body.IsNull) { return false; } return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPropertyDeclaration(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPropertyDeclaration(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPropertyDeclaration(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PropertyDeclaration o = other as PropertyDeclaration; return o != null && MatchString(this.Name, o.Name) && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match) && this.Initializer.DoMatch(o.Initializer, match) && this.ExpressionBody.DoMatch(o.ExpressionBody, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/VariableInitializer.cs ================================================ // // VariableInitializer.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // // 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. namespace ICSharpCode.Decompiler.CSharp.Syntax { public class VariableInitializer : AstNode { #region Null public new static readonly VariableInitializer Null = new NullVariableInitializer(); sealed class NullVariableInitializer : VariableInitializer { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion #region PatternPlaceholder public static implicit operator VariableInitializer(PatternMatching.Pattern pattern) { return pattern != null ? new PatternPlaceholder(pattern) : null; } sealed class PatternPlaceholder : VariableInitializer, PatternMatching.INode { readonly PatternMatching.Pattern child; public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } public override NodeType NodeType { get { return NodeType.Pattern; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitPatternPlaceholder(this, child); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitPatternPlaceholder(this, child); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitPatternPlaceholder(this, child, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return child.DoMatch(other, match); } bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion public override NodeType NodeType { get { return NodeType.Unknown; } } public VariableInitializer() { } public VariableInitializer(string name, Expression initializer = null) { this.Name = name; this.Initializer = initializer; } public string Name { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } } public Identifier NameToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public CSharpTokenNode AssignToken { get { return GetChildByRole(Roles.Assign); } } public Expression Initializer { get { return GetChildByRole(Roles.Expression); } set { SetChildByRole(Roles.Expression, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitVariableInitializer(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitVariableInitializer(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitVariableInitializer(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { VariableInitializer o = other as VariableInitializer; return o != null && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Syntax { /// /// Converts from type system to the C# AST. /// public class TypeSystemAstBuilder { readonly CSharpResolver resolver; #region Constructor /// /// Creates a new TypeSystemAstBuilder. /// /// /// A resolver initialized for the position where the type will be inserted. /// public TypeSystemAstBuilder(CSharpResolver resolver) { if (resolver == null) throw new ArgumentNullException(nameof(resolver)); this.resolver = resolver; InitProperties(); } /// /// Creates a new TypeSystemAstBuilder. /// public TypeSystemAstBuilder() { InitProperties(); } #endregion #region Properties void InitProperties() { this.UseKeywordsForBuiltinTypes = true; this.UseNullableSpecifierForValueTypes = true; this.ShowAccessibility = true; this.UsePrivateProtectedAccessibility = true; this.ShowModifiers = true; this.ShowBaseTypes = true; this.ShowTypeParameters = true; this.ShowTypeParameterConstraints = true; this.ShowParameterNames = true; this.ShowConstantValues = true; this.UseAliases = true; this.UseSpecialConstants = true; } /// /// Specifies whether the ast builder should add annotations to type references. /// The default value is . /// public bool AddTypeReferenceAnnotations { get; set; } /// /// Specifies whether the ast builder should add ResolveResult annotations to AST nodes. /// The default value is . /// public bool AddResolveResultAnnotations { get; set; } /// /// Controls whether accessibility modifiers are shown. /// The default value is . /// public bool ShowAccessibility { get; set; } /// /// Controls whether "private protected" accessibility modifiers are shown. /// The default value is . /// public bool UsePrivateProtectedAccessibility { get; set; } /// /// Controls whether non-accessibility modifiers are shown. /// The default value is . /// public bool ShowModifiers { get; set; } /// /// Controls whether base type references are shown. /// The default value is . /// public bool ShowBaseTypes { get; set; } /// /// Controls whether type parameter declarations are shown. /// The default value is . /// public bool ShowTypeParameters { get; set; } /// /// Controls whether type parameter names are shown for unbound types. /// The default value is . /// public bool ShowTypeParametersForUnboundTypes { get; set; } /// /// Controls whether constraints on type parameter declarations are shown. /// Has no effect if ShowTypeParameters is false. /// The default value is . /// public bool ShowTypeParameterConstraints { get; set; } /// /// Controls whether the names of parameters are shown. /// The default value is . /// public bool ShowParameterNames { get; set; } /// /// Controls whether to show default values of optional parameters, and the values of constant fields. /// The default value is . /// public bool ShowConstantValues { get; set; } /// /// Controls whether to show attributes. /// The default value is . /// public bool ShowAttributes { get; set; } /// /// Controls whether to sort attributes, if set to attributes are shown in metadata order. /// The default value is . /// public bool SortAttributes { get; set; } /// /// Controls whether to use fully-qualified type names or short type names. /// The default value is . /// public bool AlwaysUseShortTypeNames { get; set; } /// /// Controls whether to use keywords for builtin types. /// The default value is . /// public bool UseKeywordsForBuiltinTypes { get; set; } /// /// Controls whether to use T? or Nullable<T> for nullable value types. /// The default value is . /// public bool UseNullableSpecifierForValueTypes { get; set; } /// /// Determines the name lookup mode for converting a type name. /// /// The default value is NameLookupMode.Expression, which means the name is disambiguated /// for use in expression context. /// public NameLookupMode NameLookupMode { get; set; } /// /// Controls whether to generate a body that throws a System.NotImplementedException. /// The default value is . /// public bool GenerateBody { get; set; } /// /// Controls whether to generate custom events. /// The default value is . /// public bool UseCustomEvents { get; set; } /// /// Controls whether unbound type argument names are inserted in the ast or not. /// The default value is . /// public bool ConvertUnboundTypeArguments { get; set; } /// /// Controls whether aliases should be used inside the type name or not. /// The default value is . /// public bool UseAliases { get; set; } /// /// Controls whether constants like int.MaxValue are converted to a or . /// The default value is . /// public bool UseSpecialConstants { get; set; } /// /// Controls whether integral constants should be printed in hexadecimal format. /// The default value is . /// public bool PrintIntegralValuesAsHex { get; set; } /// /// Controls whether C# 9 "init;" accessors are supported. /// If disabled, emits "set /*init*/;" instead. /// public bool SupportInitAccessors { get; set; } /// /// Controls whether C# 9 "record" class types are supported. /// public bool SupportRecordClasses { get; set; } /// /// Controls whether C# 10 "record" struct types are supported. /// public bool SupportRecordStructs { get; set; } /// /// Controls whether C# 11 "operator >>>" is supported. /// public bool SupportUnsignedRightShift { get; set; } /// /// Controls whether C# 11 "operator checked" is supported. /// public bool SupportOperatorChecked { get; set; } /// /// Controls whether all fully qualified type names should be prefixed with "global::". /// public bool AlwaysUseGlobal { get; set; } #endregion #region Convert Type public AstType ConvertType(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); AstType astType = ConvertTypeHelper(type); AddTypeAnnotation(astType, type); return astType; } private void AddTypeAnnotation(AstType astType, IType type) { if (AddResolveResultAnnotations) astType.AddAnnotation(new TypeResolveResult(type)); } public AstType ConvertType(FullTypeName fullTypeName) { if (resolver != null) { foreach (var asm in resolver.Compilation.Modules) { var def = asm.GetTypeDefinition(fullTypeName); if (def != null) { return ConvertType(def); } } } TopLevelTypeName top = fullTypeName.TopLevelTypeName; AstType type; if (string.IsNullOrEmpty(top.Namespace)) { type = MakeSimpleType(top.Name); } else { type = MakeMemberType(MakeSimpleType(top.Namespace), top.Name); } for (int i = 0; i < fullTypeName.NestingLevel; i++) { type = MakeMemberType(type, fullTypeName.GetNestedTypeName(i)); } return type; } AstType ConvertTypeHelper(IType type) { if (type is TypeWithElementType typeWithElementType) { if (typeWithElementType is PointerType) { return ConvertType(typeWithElementType.ElementType).MakePointerType(); } else if (typeWithElementType is ArrayType) { var astType = ConvertType(typeWithElementType.ElementType).MakeArrayType(((ArrayType)type).Dimensions); if (type.Nullability == Nullability.Nullable) return astType.MakeNullableType(); else return astType; } else if (typeWithElementType is ByReferenceType) { return ConvertType(typeWithElementType.ElementType).MakeRefType(); } else { // not supported as type in C# return ConvertType(typeWithElementType.ElementType); } } else if (type is NullabilityAnnotatedType nat) { var astType = ConvertType(nat.TypeWithoutAnnotation); if (nat.Nullability == Nullability.Nullable) astType = astType.MakeNullableType(); return astType; } else if (type is TupleType tuple) { var astType = new TupleAstType(); foreach (var (etype, ename) in tuple.ElementTypes.Zip(tuple.ElementNames)) { astType.Elements.Add(new TupleTypeElement { Type = ConvertType(etype), Name = ename }); } return astType; } else if (type is FunctionPointerType fpt) { var astType = new FunctionPointerAstType(); if (fpt.CallingConvention == System.Reflection.Metadata.SignatureCallingConvention.Unmanaged) { astType.HasUnmanagedCallingConvention = true; } else if (fpt.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default) { string callconvName = fpt.CallingConvention switch { System.Reflection.Metadata.SignatureCallingConvention.CDecl => "Cdecl", System.Reflection.Metadata.SignatureCallingConvention.StdCall => "Stdcall", System.Reflection.Metadata.SignatureCallingConvention.ThisCall => "Thiscall", System.Reflection.Metadata.SignatureCallingConvention.FastCall => "Fastcall", System.Reflection.Metadata.SignatureCallingConvention.VarArgs => "Varargs", _ => fpt.CallingConvention.ToString() }; astType.HasUnmanagedCallingConvention = true; astType.CallingConventions.Add(new PrimitiveType(callconvName)); } foreach (var customCallConv in fpt.CustomCallingConventions) { AstType callConvSyntax; if (customCallConv.Namespace == "System.Runtime.CompilerServices" && customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && customCallConv.Name.Length > 8) { callConvSyntax = new PrimitiveType(customCallConv.Name.Substring(8)); if (AddResolveResultAnnotations) { callConvSyntax.AddAnnotation(new TypeResolveResult(customCallConv)); } } else { callConvSyntax = ConvertType(customCallConv); } astType.CallingConventions.Add(callConvSyntax); } for (int i = 0; i < fpt.ParameterTypes.Length; i++) { var paramDecl = new ParameterDeclaration(); paramDecl.ParameterModifier = fpt.ParameterReferenceKinds[i]; IType parameterType = fpt.ParameterTypes[i]; if (paramDecl.ParameterModifier != ReferenceKind.None && parameterType is ByReferenceType brt) { paramDecl.Type = ConvertType(brt.ElementType); } else { paramDecl.Type = ConvertType(parameterType); } astType.Parameters.Add(paramDecl); } astType.ReturnType = ConvertType(fpt.ReturnType); if (fpt.ReturnIsRefReadOnly && astType.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } ITypeDefinition treatedAs = fpt.GetDefinition(); if (treatedAs != null) { var result = ConvertTypeHelper(treatedAs); result.AddChild(new Comment(astType.ToString(), CommentType.MultiLine), Roles.Comment); return result; } else { return astType; } } else { AstType astType; switch (type) { case ITypeDefinition _: case UnknownType _: if (type.IsUnbound()) { if (ShowTypeParametersForUnboundTypes) { astType = ConvertTypeHelper(type, type.TypeArguments); } else { IType[] typeArguments = new IType[type.TypeParameterCount]; for (int i = 0; i < typeArguments.Length; i++) { typeArguments[i] = SpecialType.UnboundTypeArgument; } astType = ConvertTypeHelper(type, typeArguments); } } else { astType = ConvertTypeHelper(type, EmptyList.Instance); } break; case ParameterizedType pt: if (UseNullableSpecifierForValueTypes && pt.IsKnownType(KnownTypeCode.NullableOfT)) { return ConvertType(pt.TypeArguments[0]).MakeNullableType(); } astType = ConvertTypeHelper(pt.GenericType, pt.TypeArguments); break; default: switch (type.Kind) { case TypeKind.Dynamic: case TypeKind.NInt: case TypeKind.NUInt: astType = new PrimitiveType(type.Name); break; default: astType = MakeSimpleType(type.Name); break; } break; } if (type.Nullability == Nullability.Nullable) { AddTypeAnnotation(astType, type.ChangeNullability(Nullability.Oblivious)); astType = astType.MakeNullableType(); } return astType; } } AstType ConvertTypeHelper(IType genericType, IReadOnlyList typeArguments) { ITypeDefinition typeDef = genericType.GetDefinition(); Debug.Assert(typeDef != null || genericType.Kind == TypeKind.Unknown); Debug.Assert(typeArguments.Count >= genericType.TypeParameterCount); if (UseKeywordsForBuiltinTypes && typeDef != null) { string keyword = KnownTypeReference.GetCSharpNameByTypeCode(typeDef.KnownTypeCode); if (keyword != null) { return new PrimitiveType(keyword); } } // The number of type parameters belonging to outer classes int outerTypeParameterCount = genericType.DeclaringType?.TypeParameterCount ?? 0; if (resolver != null && typeDef != null) { // Look if there's an alias to the target type if (UseAliases) { for (UsingScope usingScope = resolver.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { foreach (var pair in usingScope.UsingAliases) { if (pair.Value is TypeResolveResult) { if (TypeMatches(pair.Value.Type, typeDef, typeArguments)) return MakeSimpleType(pair.Key); } } } } IType[] localTypeArguments; if (typeDef.TypeParameterCount > outerTypeParameterCount) { localTypeArguments = new IType[typeDef.TypeParameterCount - outerTypeParameterCount]; for (int i = 0; i < localTypeArguments.Length; i++) { localTypeArguments[i] = typeArguments[outerTypeParameterCount + i]; } } else { localTypeArguments = Empty.Array; } ResolveResult rr = resolver.LookupSimpleNameOrTypeName(typeDef.Name, localTypeArguments, NameLookupMode); TypeResolveResult trr = rr as TypeResolveResult; if (trr != null || (localTypeArguments.Length == 0 && resolver.IsVariableReferenceWithSameType(rr, typeDef.Name, out trr))) { if (!trr.IsError && TypeMatches(trr.Type, typeDef, typeArguments)) { // We can use the short type name SimpleType shortResult = MakeSimpleType(typeDef.Name); AddTypeArguments(shortResult, typeDef.TypeParameters, typeArguments, outerTypeParameterCount, typeDef.TypeParameterCount); return shortResult; } } } if (AlwaysUseShortTypeNames || (typeDef == null && genericType.DeclaringType == null)) { var shortResult = MakeSimpleType(genericType.Name); AddTypeArguments(shortResult, genericType.TypeParameters, typeArguments, outerTypeParameterCount, genericType.TypeParameterCount); return shortResult; } MemberType result = new MemberType(); if (genericType.DeclaringType != null) { // Handle nested types result.Target = ConvertTypeHelper(genericType.DeclaringType, typeArguments); // Use correct number of type arguments on the declaring type var declaringType = genericType.DeclaringType; if (outerTypeParameterCount > 0) { declaringType = new ParameterizedType(genericType.DeclaringType, typeArguments.Take(outerTypeParameterCount)); } AddTypeAnnotation(result.Target, declaringType); } else { // Handle top-level types if (string.IsNullOrEmpty(genericType.Namespace)) { result.Target = MakeGlobal(); result.IsDoubleColon = true; } else { result.Target = ConvertNamespace(genericType.Namespace, out _, AlwaysUseGlobal || genericType.Namespace == genericType.Name); } } result.MemberName = genericType.Name; AddTypeArguments(result, genericType.TypeParameters, typeArguments, outerTypeParameterCount, genericType.TypeParameterCount); return result; } /// /// Gets whether 'type' is the same as 'typeDef' parameterized with the given type arguments. /// bool TypeMatches(IType type, ITypeDefinition typeDef, IReadOnlyList typeArguments) { if (typeDef.TypeParameterCount == 0) { return TypeDefMatches(typeDef, type); } else { if (!TypeDefMatches(typeDef, type.GetDefinition())) return false; ParameterizedType pt = type as ParameterizedType; if (pt == null) { return typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument); } var ta = pt.TypeArguments; for (int i = 0; i < ta.Count; i++) { if (!ta[i].Equals(typeArguments[i])) return false; } return true; } } bool TypeDefMatches(ITypeDefinition typeDef, IType type) { if (type == null || type.Name != typeDef.Name || type.Namespace != typeDef.Namespace || type.TypeParameterCount != typeDef.TypeParameterCount) return false; bool defIsNested = typeDef.DeclaringTypeDefinition != null; bool typeIsNested = type.DeclaringType != null; if (defIsNested && typeIsNested) return TypeDefMatches(typeDef.DeclaringTypeDefinition, type.DeclaringType); else return defIsNested == typeIsNested; } /// /// Adds type arguments to the result type. /// /// The result AST node (a SimpleType or MemberType) /// The type parameters /// The list of type arguments /// Index of first type argument to add /// Index after last type argument to add void AddTypeArguments(AstType result, IReadOnlyList typeParameters, IReadOnlyList typeArguments, int startIndex, int endIndex) { Debug.Assert(endIndex <= typeParameters.Count); for (int i = startIndex; i < endIndex; i++) { if (ConvertUnboundTypeArguments && typeArguments[i].Kind == TypeKind.UnboundTypeArgument) { result.AddChild(MakeSimpleType(typeParameters[i].Name), Roles.TypeArgument); } else { result.AddChild(ConvertType(typeArguments[i]), Roles.TypeArgument); } } } public AstType ConvertNamespace(string namespaceName, out NamespaceResolveResult nrr) { return ConvertNamespace(namespaceName, out nrr, requiresGlobalPrefix: false); } AstType ConvertNamespace(string namespaceName, out NamespaceResolveResult nrr, bool requiresGlobalPrefix) { if (resolver != null) { // Look if there's an alias to the target namespace if (UseAliases) { for (UsingScope usingScope = resolver.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { foreach (var pair in usingScope.UsingAliases) { nrr = pair.Value as NamespaceResolveResult; if (nrr != null && nrr.NamespaceName == namespaceName) { var ns = MakeSimpleType(pair.Key); if (AddResolveResultAnnotations) ns.AddAnnotation(nrr); return ns; } } } } } int pos = namespaceName.LastIndexOf('.'); if (pos < 0) { if (IsValidNamespace(namespaceName, out nrr)) { AstType ns; if (requiresGlobalPrefix) { ns = new MemberType { Target = MakeGlobal(), IsDoubleColon = true, MemberName = namespaceName }; } else { ns = MakeSimpleType(namespaceName); } if (AddResolveResultAnnotations && nrr != null) ns.AddAnnotation(nrr); return ns; } else { var ns = new MemberType { Target = MakeGlobal(), IsDoubleColon = true, MemberName = namespaceName }; if (AddResolveResultAnnotations) { var @namespace = resolver.Compilation.RootNamespace.GetChildNamespace(namespaceName); if (@namespace != null) ns.AddAnnotation(nrr = new NamespaceResolveResult(@namespace)); } return ns; } } else { string parentNamespace = namespaceName.Substring(0, pos); string localNamespace = namespaceName.Substring(pos + 1); var parentNS = ConvertNamespace(parentNamespace, out var parentNRR, requiresGlobalPrefix); var ns = new MemberType { Target = parentNS, MemberName = localNamespace }; nrr = null; if (AddResolveResultAnnotations && parentNRR != null) { var newNamespace = parentNRR.Namespace.GetChildNamespace(localNamespace); if (newNamespace != null) { ns.AddAnnotation(nrr = new NamespaceResolveResult(newNamespace)); } } return ns; } } bool IsValidNamespace(string firstNamespacePart, out NamespaceResolveResult nrr) { nrr = null; if (resolver == null) return true; // just assume namespaces are valid if we don't have a resolver nrr = resolver.ResolveSimpleName(firstNamespacePart, EmptyList.Instance) as NamespaceResolveResult; return nrr != null && !nrr.IsError && nrr.NamespaceName == firstNamespacePart; } static SimpleType MakeSimpleType(string name) { if (name == "_") return new SimpleType("@_"); return new SimpleType(name); } SimpleType MakeGlobal() { var global = new SimpleType("global"); if (AddResolveResultAnnotations && resolver != null) global.AddAnnotation(new NamespaceResolveResult(resolver.Compilation.RootNamespace)); return global; } static MemberType MakeMemberType(AstType target, string name) { if (name == "_") return new MemberType(target, "@_"); return new MemberType(target, name); } #endregion #region Convert Attribute public Attribute ConvertAttribute(IAttribute attribute) { Attribute attr = new Attribute(); attr.Type = ConvertAttributeType(attribute.AttributeType); switch (attr.Type) { case SimpleType st: if (st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); break; case MemberType mt: if (mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); break; } if (AddResolveResultAnnotations && attribute.Constructor != null) { attr.AddAnnotation(new MemberResolveResult(null, attribute.Constructor)); } var parameters = attribute.Constructor?.Parameters ?? EmptyList.Instance; for (int i = 0; i < attribute.FixedArguments.Length; i++) { var arg = attribute.FixedArguments[i]; var p = (i < parameters.Count) ? parameters[i] : null; attr.Arguments.Add(ConvertConstantValue(p?.Type ?? arg.Type, arg.Type, arg.Value)); } if (attribute.NamedArguments.Length > 0) { InitializedObjectResolveResult targetResult = new InitializedObjectResolveResult(attribute.AttributeType); foreach (var namedArg in attribute.NamedArguments) { NamedExpression namedArgument = new NamedExpression(namedArg.Name, ConvertConstantValue(namedArg.Type, namedArg.Value)); if (AddResolveResultAnnotations) { IMember member = CustomAttribute.MemberForNamedArgument(attribute.AttributeType, namedArg); if (member != null) { namedArgument.AddAnnotation(new MemberResolveResult(targetResult, member)); } } attr.Arguments.Add(namedArgument); } } if (attribute.HasDecodeErrors) { attr.HasArgumentList = true; attr.AddChild(new Comment("Could not decode attribute arguments.", CommentType.MultiLine), Roles.Comment); // insert explicit rpar token to make the comment appear within the parentheses attr.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.RPar), Roles.RPar); } return attr; } private IEnumerable ConvertAttributes(IEnumerable attributes, string target = null) { if (SortAttributes) attributes = attributes.OrderBy(a => a, new DelegateComparer(CompareAttribute)); return attributes.Select(a => { var section = new AttributeSection(ConvertAttribute(a)); if (target != null) section.AttributeTarget = target; return section; }); static int CompareAttribute(IAttribute a, IAttribute b) { int result = CompareType(a.AttributeType, b.AttributeType); if (result != 0) return result; if (a.HasDecodeErrors && b.HasDecodeErrors) return 0; if (a.HasDecodeErrors) return -1; if (b.HasDecodeErrors) return 1; result = a.FixedArguments.Length - b.FixedArguments.Length; if (result != 0) return result; for (int i = 0; i < a.FixedArguments.Length; i++) { var argA = a.FixedArguments[i]; var argB = b.FixedArguments[i]; result = CompareType(argA.Type, argB.Type); if (result != 0) return result; if (argA.Value is IComparable compA && argB.Value is IComparable compB) result = compA.CompareTo(compB); else result = 0; if (result != 0) return result; } result = a.NamedArguments.Length - b.NamedArguments.Length; if (result != 0) return result; for (int i = 0; i < a.NamedArguments.Length; i++) { var argA = a.NamedArguments[i]; var argB = b.NamedArguments[i]; result = argA.Name.CompareTo(argB.Name); if (result != 0) return result; result = CompareType(argA.Type, argB.Type); if (result != 0) return result; if (argA.Value is IComparable compA && argB.Value is IComparable compB) result = compA.CompareTo(compB); else result = 0; if (result != 0) return result; } return 0; } static int CompareType(IType a, IType b) { return a.FullName.CompareTo(b.FullName); } } #endregion #region Convert Attribute Type public AstType ConvertAttributeType(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); AstType astType = ConvertTypeHelper(type); string shortName = null; if (type.Name.Length > 9 && type.Name.EndsWith("Attribute", StringComparison.Ordinal)) { shortName = type.Name.Remove(type.Name.Length - 9); } if (AlwaysUseShortTypeNames) { switch (astType) { case SimpleType st: st.Identifier = shortName; break; case MemberType mt: mt.MemberName = shortName; break; } } else if (resolver != null) { ApplyShortAttributeNameIfPossible(type, astType, shortName); } AddTypeAnnotation(astType, type); return astType; } private void ApplyShortAttributeNameIfPossible(IType type, AstType astType, string shortName) { switch (astType) { case SimpleType st: ResolveResult shortRR = null; ResolveResult withExtraAttrSuffix = resolver.LookupSimpleNameOrTypeName(type.Name + "Attribute", EmptyList.Instance, NameLookupMode.Type); if (shortName != null) { shortRR = resolver.LookupSimpleNameOrTypeName(shortName, EmptyList.Instance, NameLookupMode.Type); } // short type is either unknown or not an attribute type -> we can use the short name. if (shortRR != null && (shortRR is UnknownIdentifierResolveResult || !IsAttributeType(shortRR))) { st.Identifier = shortName; } else if (IsAttributeType(withExtraAttrSuffix)) { // typeName + "Attribute" is an attribute type -> we cannot use long type name, add '@' to disable implicit "Attribute" suffix. st.Identifier = '@' + st.Identifier; } break; case MemberType mt: if (type.DeclaringType != null) { var declaringTypeDef = type.DeclaringType.GetDefinition(); if (declaringTypeDef != null) { if (shortName != null && !declaringTypeDef.GetNestedTypes(t => t.TypeParameterCount == 0 && t.Name == shortName).Any(IsAttributeType)) { mt.MemberName = shortName; } else if (declaringTypeDef.GetNestedTypes(t => t.TypeParameterCount == 0 && t.Name == type.Name + "Attribute").Any(IsAttributeType)) { mt.MemberName = '@' + mt.MemberName; } } } else if (mt.Target.GetResolveResult() is NamespaceResolveResult nrr) { if (shortName != null && !IsAttributeType(nrr.Namespace.GetTypeDefinition(shortName, 0))) { mt.MemberName = shortName; } else if (IsAttributeType(nrr.Namespace.GetTypeDefinition(type.Name + "Attribute", 0))) { mt.MemberName = '@' + mt.MemberName; } } break; } } private bool IsAttributeType(IType type) { return type != null && type.GetNonInterfaceBaseTypes().Any(t => t.IsKnownType(KnownTypeCode.Attribute)); } private bool IsAttributeType(ResolveResult rr) { return rr is TypeResolveResult trr && IsAttributeType(trr.Type); } #endregion #region Convert Constant Value /// /// Creates an Expression for the given constant value. /// /// Note: the returned expression is not necessarily of the desired type. /// However, the returned expression will always be implicitly convertible to rr.Type, /// and will have the correct value when being converted in this way. /// public Expression ConvertConstantValue(ResolveResult rr) { if (rr == null) throw new ArgumentNullException(nameof(rr)); bool isBoxing = false; if (rr is ConversionResolveResult crr) { // unpack ConversionResolveResult if necessary // (e.g. a boxing conversion or string->object reference conversion) rr = crr.Input; isBoxing = crr.Conversion.IsBoxingConversion; } if (rr is TypeOfResolveResult) { var expr = new TypeOfExpression(ConvertType(((TypeOfResolveResult)rr).ReferencedType)); if (AddResolveResultAnnotations) expr.AddAnnotation(rr); return expr; } else if (rr is ArrayCreateResolveResult acrr) { ArrayCreateExpression ace = new ArrayCreateExpression(); ace.Type = ConvertType(acrr.Type); if (ace.Type is ComposedType composedType) { composedType.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); if (!composedType.HasNullableSpecifier && composedType.PointerRank == 0) ace.Type = composedType.BaseType; } if (acrr.SizeArguments != null && acrr.InitializerElements == null) { ace.AdditionalArraySpecifiers.FirstOrNullObject().Remove(); ace.Arguments.AddRange(acrr.SizeArguments.Select(ConvertConstantValue)); } if (acrr.InitializerElements != null) { ArrayInitializerExpression initializer = new ArrayInitializerExpression(); initializer.Elements.AddRange(acrr.InitializerElements.Select(ConvertConstantValue)); ace.Initializer = initializer; } if (AddResolveResultAnnotations) ace.AddAnnotation(rr); return ace; } else if (rr.IsCompileTimeConstant) { var expr = ConvertConstantValue(rr.Type, rr.ConstantValue); if (isBoxing && (rr.Type.IsCSharpSmallIntegerType() || rr.Type.IsCSharpNativeIntegerType())) { // C# does not have small integer literal types. // We need to add a cast so that the integer literal gets boxed as the correct type. expr = new CastExpression(ConvertType(rr.Type), expr); if (AddResolveResultAnnotations) expr.AddAnnotation(rr); } return expr; } else { return new ErrorExpression(); } } /// /// Creates an Expression for the given constant value. /// /// Note: the returned expression is not necessarily of the specified : /// For example, ConvertConstantValue(typeof(string), null) results in a null literal, /// without a cast to string. /// Similarly, ConvertConstantValue(typeof(short), 1) results in the literal 1, /// which is of type int. /// However, the returned expression will always be implicitly convertible to . /// public Expression ConvertConstantValue(IType type, object constantValue) { return ConvertConstantValue(type, type, constantValue); } /// /// Creates an Expression for the given constant value. /// public Expression ConvertConstantValue(IType expectedType, IType type, object constantValue) { if (type == null) throw new ArgumentNullException(nameof(type)); if (constantValue == null) { if (type.IsReferenceType == true || type.IsKnownType(KnownTypeCode.NullableOfT) || type.Kind.IsAnyPointer()) { var expr = new NullReferenceExpression(); if (AddResolveResultAnnotations) expr.AddAnnotation(new ConstantResolveResult(SpecialType.NullType, null)); return expr; } else { var expr = new DefaultValueExpression(ConvertType(type)); if (AddResolveResultAnnotations) expr.AddAnnotation(new ConstantResolveResult(type, null)); return expr; } } else if (constantValue is IType typeofType) { var expr = new TypeOfExpression(ConvertType(typeofType)); if (AddResolveResultAnnotations) expr.AddAnnotation(new TypeOfResolveResult(type, typeofType)); return expr; } else if (constantValue is ImmutableArray> arr) { var elementType = (type as ArrayType)?.ElementType ?? SpecialType.UnknownType; var expr = new ArrayCreateExpression(); expr.Type = ConvertType(type); if (expr.Type is ComposedType composedType) { composedType.ArraySpecifiers.MoveTo(expr.AdditionalArraySpecifiers); if (!composedType.HasNullableSpecifier && composedType.PointerRank == 0) expr.Type = composedType.BaseType; } expr.Initializer = new ArrayInitializerExpression(arr.Select(e => ConvertConstantValue(elementType, e.Type, e.Value))); return expr; } else { IType underlyingType = NullableType.GetUnderlyingType(type); if (underlyingType.Kind == TypeKind.Enum) { return ConvertEnumValue(underlyingType, (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false)); } else { if (!(PrintIntegralValuesAsHex && underlyingType.IsCSharpPrimitiveIntegerType()) && IsSpecialConstant(underlyingType, constantValue, out var expr)) { return expr; } if (underlyingType.IsKnownType(KnownTypeCode.Double) || underlyingType.IsKnownType(KnownTypeCode.Single)) return ConvertFloatingPointLiteral(underlyingType, constantValue); IType literalType = underlyingType; bool integerTypeMismatch = underlyingType.IsCSharpSmallIntegerType() || underlyingType.IsCSharpNativeIntegerType(); if (integerTypeMismatch) { // C# does not have integer literals of small integer types, // use `int` literal instead. // It also doesn't have native integer literals, those also use `int` (or `uint` for `nuint`). bool unsigned = underlyingType.Kind == TypeKind.NUInt; constantValue = CSharpPrimitiveCast.Cast(unsigned ? TypeCode.UInt32 : TypeCode.Int32, constantValue, false); var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation; literalType = compilation?.FindType(unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32); } LiteralFormat format = LiteralFormat.None; if (PrintIntegralValuesAsHex) { format = LiteralFormat.HexadecimalNumber; } expr = new PrimitiveExpression(constantValue, format); if (AddResolveResultAnnotations && literalType != null) expr.AddAnnotation(new ConstantResolveResult(literalType, constantValue)); if (integerTypeMismatch && !type.Equals(expectedType) || underlyingType.Kind == TypeKind.Unknown) { expr = new CastExpression(ConvertType(type), expr); } return expr; } } } bool IsSpecialConstant(IType expectedType, object constant, out Expression expression) { expression = null; if (!specialConstants.TryGetValue(constant, out var info)) return false; // find IType of constant in compilation. var constantType = expectedType; if (!expectedType.IsKnownType(info.Type)) { var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation; if (compilation == null) return false; constantType = compilation.FindType(info.Type); } // if the field definition cannot be found, do not generate a reference to the field. var field = constantType.GetFields(p => p.Name == info.Member).SingleOrDefault(); if (!UseSpecialConstants || field == null) { // +Infty, -Infty and NaN, cannot be represented in their encoded form. // Use an equivalent arithmetic expression instead. if (info.Type == KnownTypeCode.Double) { switch ((double)constant) { case double.NegativeInfinity: // (-1.0 / 0.0) var left = new PrimitiveExpression(-1.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, -1.0)); var right = new PrimitiveExpression(0.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, double.NegativeInfinity)); return true; case double.PositiveInfinity: // (1.0 / 0.0) left = new PrimitiveExpression(1.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 1.0)); right = new PrimitiveExpression(0.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, double.PositiveInfinity)); return true; case double.NaN: // (0.0 / 0.0) left = new PrimitiveExpression(0.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0)); right = new PrimitiveExpression(0.0).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, double.NaN)); return true; } } if (info.Type == KnownTypeCode.Single) { switch ((float)constant) { case float.NegativeInfinity: // (-1.0f / 0.0f) var left = new PrimitiveExpression(-1.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, -1.0f)); var right = new PrimitiveExpression(0.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0f)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, float.NegativeInfinity)); return true; case float.PositiveInfinity: // (1.0f / 0.0f) left = new PrimitiveExpression(1.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 1.0f)); right = new PrimitiveExpression(0.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0f)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, float.PositiveInfinity)); return true; case float.NaN: // (0.0f / 0.0f) left = new PrimitiveExpression(0.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0f)); right = new PrimitiveExpression(0.0f).WithoutILInstruction().WithRR(new ConstantResolveResult(constantType, 0.0f)); expression = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction() .WithRR(new ConstantResolveResult(constantType, float.NaN)); return true; } } return false; } expression = new TypeReferenceExpression(ConvertType(constantType)); if (AddResolveResultAnnotations) expression.AddAnnotation(new TypeResolveResult(constantType)); expression = new MemberReferenceExpression(expression, info.Member); if (AddResolveResultAnnotations) expression.AddAnnotation(new MemberResolveResult(new TypeResolveResult(constantType), field)); return true; } static readonly Dictionary specialConstants = new Dictionary() { // byte: { byte.MaxValue, (KnownTypeCode.Byte, "MaxValue") }, // sbyte: { sbyte.MinValue, (KnownTypeCode.SByte, "MinValue") }, { sbyte.MaxValue, (KnownTypeCode.SByte, "MaxValue") }, // short: { short.MinValue, (KnownTypeCode.Int16, "MinValue") }, { short.MaxValue, (KnownTypeCode.Int16, "MaxValue") }, // ushort: { ushort.MaxValue, (KnownTypeCode.UInt16, "MaxValue") }, // int: { int.MinValue, (KnownTypeCode.Int32, "MinValue") }, { int.MaxValue, (KnownTypeCode.Int32, "MaxValue") }, // uint: { uint.MaxValue, (KnownTypeCode.UInt32, "MaxValue") }, // long: { long.MinValue, (KnownTypeCode.Int64, "MinValue") }, { long.MaxValue, (KnownTypeCode.Int64, "MaxValue") }, // ulong: { ulong.MaxValue, (KnownTypeCode.UInt64, "MaxValue") }, // float: { float.NaN, (KnownTypeCode.Single, "NaN") }, { float.NegativeInfinity, (KnownTypeCode.Single, "NegativeInfinity") }, { float.PositiveInfinity, (KnownTypeCode.Single, "PositiveInfinity") }, { float.MinValue, (KnownTypeCode.Single, "MinValue") }, { float.MaxValue, (KnownTypeCode.Single, "MaxValue") }, { float.Epsilon, (KnownTypeCode.Single, "Epsilon") }, // double: { double.NaN, (KnownTypeCode.Double, "NaN") }, { double.NegativeInfinity, (KnownTypeCode.Double, "NegativeInfinity") }, { double.PositiveInfinity, (KnownTypeCode.Double, "PositiveInfinity") }, { double.MinValue, (KnownTypeCode.Double, "MinValue") }, { double.MaxValue, (KnownTypeCode.Double, "MaxValue") }, { double.Epsilon, (KnownTypeCode.Double, "Epsilon") }, // decimal: { decimal.MinValue, (KnownTypeCode.Decimal, "MinValue") }, { decimal.MaxValue, (KnownTypeCode.Decimal, "MaxValue") }, }; bool IsFlagsEnum(ITypeDefinition type) { return type.HasAttribute(KnownAttribute.Flags); } Expression ConvertEnumValue(IType type, long val) { ITypeDefinition enumDefinition = type.GetDefinition(); TypeCode enumBaseTypeCode = ReflectionHelper.GetTypeCode(enumDefinition.EnumUnderlyingType); var fields = enumDefinition.Fields .Select(PrepareConstant) .Where(f => f.field != null) .ToArray(); foreach (var (value, field) in fields) { if (value == val) { var mre = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); if (AddResolveResultAnnotations) mre.AddAnnotation(new MemberResolveResult(mre.Target.GetResolveResult(), field)); return mre; } } if (IsFlagsEnum(enumDefinition)) { long enumValue = val; Expression expr = null; long negatedEnumValue = ~val; // limit negatedEnumValue to the appropriate range switch (enumBaseTypeCode) { case TypeCode.Byte: case TypeCode.SByte: negatedEnumValue &= byte.MaxValue; break; case TypeCode.Int16: case TypeCode.UInt16: negatedEnumValue &= ushort.MaxValue; break; case TypeCode.Int32: case TypeCode.UInt32: negatedEnumValue &= uint.MaxValue; break; } Expression negatedExpr = null; foreach (var (fieldValue, field) in fields.OrderByDescending(f => CalculateHammingWeight(unchecked((ulong)f.value)))) { if (fieldValue == 0) continue; // skip None enum value if ((fieldValue & enumValue) == fieldValue) { var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); if (expr == null) expr = fieldExpression; else expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); enumValue &= ~fieldValue; } if ((fieldValue & negatedEnumValue) == fieldValue) { var fieldExpression = new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(type)), field.Name); if (negatedExpr == null) negatedExpr = fieldExpression; else negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression); negatedEnumValue &= ~fieldValue; } } if (enumValue == 0 && expr != null) { if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) { return expr; } } if (negatedEnumValue == 0 && negatedExpr != null) { return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); } } return new CastExpression(ConvertType(type), new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false))); (long value, IField field) PrepareConstant(IField field) { if (!field.IsConst) return (-1, null); object constantValue = field.GetConstantValue(); if (constantValue == null) return (-1, null); return ((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, checkForOverflow: false), field); } // see https://en.wikipedia.org/wiki/Hamming_weight int CalculateHammingWeight(ulong value) { const ulong m1 = 0x5555555555555555; //binary: 0101... const ulong m2 = 0x3333333333333333; //binary: 00110011.. const ulong m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ... const ulong h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3... ulong x = value - ((value >> 1) & m1); //put count of each 2 bits into those 2 bits x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits return unchecked((int)((x * h01) >> 56)); //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... } } static bool IsValidFraction(long num, long den) { if (!(den > 0 && num != 0)) return false; if (den == 1 || Math.Abs(num) == 1) return true; return Math.Abs(num) < den && new int[] { 2, 3, 5 }.Any(x => den % x == 0); } [MethodImpl(MethodImplOptions.NoInlining)] static bool EqualDoubles(in double val1, in double val2) { // We use `in double` to pass the floats through memory, // which ensures we won't get more than 64bits of precision return val1 == val2; } [MethodImpl(MethodImplOptions.NoInlining)] static bool EqualFloats(in float val1, in float val2) { // We use `in float` to pass the floats through memory, // which ensures we won't get more than 32bits of precision return val1 == val2; } static bool IsEqual(long num, long den, object constantValue, bool isDouble) { if (isDouble) { return EqualDoubles((double)constantValue, num / (double)den); } else { return EqualFloats((float)constantValue, num / (float)den); } } const int MAX_DENOMINATOR_DOUBLE = 1000; const int MAX_DENOMINATOR_FLOAT = 360; Expression ConvertFloatingPointLiteral(IType type, object constantValue) { // Coerce constantValue to either float or double: // There are compilers that embed 0 (and possible other values) as int into constant value signatures, // even if the expected type is float or double. constantValue = CSharpPrimitiveCast.Cast(type.GetTypeCode(), constantValue, false); bool isDouble = type.IsKnownType(KnownTypeCode.Double); ICompilation compilation = type.GetDefinition().Compilation; Expression expr = null; string str; if (isDouble) { if (Math.Floor((double)constantValue) == (double)constantValue) { expr = new PrimitiveExpression(constantValue); } str = ((double)constantValue).ToString("r"); } else { if (Math.Floor((float)constantValue) == (float)constantValue) { expr = new PrimitiveExpression(constantValue); } str = ((float)constantValue).ToString("r"); } bool useFraction = (str.Length - (str.StartsWith("-", StringComparison.OrdinalIgnoreCase) ? 2 : 1) > 5); if (useFraction && expr == null) { Debug.Assert(200 < MAX_DENOMINATOR_FLOAT); // For fractions not involving PI, use a smaller MAX_DENOMINATOR // to avoid coincidences such as (1f/MathF.PI) == (113f/355f) (long num, long den) = isDouble ? FractionApprox((double)constantValue, MAX_DENOMINATOR_DOUBLE) : FractionApprox((float)constantValue, 200); if (IsValidFraction(num, den) && IsEqual(num, den, constantValue, isDouble) && Math.Abs(den) != 1) { var left = MakeConstant(type, num); var right = MakeConstant(type, den); expr = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right); } } if (useFraction && expr == null && UseSpecialConstants) { IType mathType; if (isDouble) mathType = compilation.FindType(typeof(Math)); else { mathType = compilation.FindType(new TopLevelTypeName("System", "MathF")); var typeDef = mathType.GetDefinition(); if (typeDef == null || !typeDef.IsDirectImportOf(compilation.MainModule) || !typeDef.GetFields(f => f.Name == "PI" && f.IsConst).Any() || !typeDef.GetFields(f => f.Name == "E" && f.IsConst).Any()) { mathType = compilation.FindType(typeof(Math)); } } expr = TryExtractExpression(mathType, type, constantValue, "PI", isDouble) ?? TryExtractExpression(mathType, type, constantValue, "E", isDouble); } if (expr == null) expr = new PrimitiveExpression(constantValue); if (AddResolveResultAnnotations) expr.AddAnnotation(new ConstantResolveResult(type, constantValue)); return expr; } Expression MakeConstant(IType type, long c) { return new PrimitiveExpression(CSharpPrimitiveCast.Cast(type.GetTypeCode(), c, checkForOverflow: true)); } const float MathF_PI = 3.14159274f; const float MathF_E = 2.71828175f; Expression TryExtractExpression(IType mathType, IType type, object literalValue, string memberName, bool isDouble) { Expression MakeFieldReference() { AstType mathAstType = ConvertType(mathType); var fieldRef = new MemberReferenceExpression(new TypeReferenceExpression(mathAstType), memberName); if (AddResolveResultAnnotations) { var field = mathType.GetFields(f => f.Name == memberName).FirstOrDefault(); if (field != null) { fieldRef.WithRR(new MemberResolveResult(mathAstType.GetResolveResult(), field)); } } if (type.IsKnownType(KnownTypeCode.Double)) return fieldRef; if (mathType.Name == "MathF") return fieldRef; return new CastExpression(ConvertType(type), fieldRef); } Expression ExtractExpression(long n, long d) { Expression fieldReference = MakeFieldReference(); // Math.PI or Math.E or (float)Math.PI or (float)Math.E or MathF.PI or MathF.E Expression expr = fieldReference; if (n != 1) { if (n == -1) { // -field expr = new UnaryOperatorExpression(UnaryOperatorType.Minus, expr); } else { // field * n expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Multiply, MakeConstant(type, n)); } } if (d != 1) { // field * n / d or -field / d or field / d expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, MakeConstant(type, d)); } if (isDouble) { double field = memberName == "PI" ? Math.PI : Math.E; double approxValue = field * n / d; if (EqualDoubles(approxValue, (double)literalValue)) return expr; } else { float field = memberName == "PI" ? MathF_PI : MathF_E; float approxValue = field * n / d; if (EqualFloats(approxValue, (float)literalValue)) return expr; } // Math.PI or Math.E or (float)Math.PI or (float)Math.E or MathF.PI or MathF.E expr = fieldReference.Detach(); if (d == 1) { // n / field expr = new BinaryOperatorExpression(MakeConstant(type, n), BinaryOperatorType.Divide, expr); } else { // n / (d * field) expr = new BinaryOperatorExpression(MakeConstant(type, d), BinaryOperatorType.Multiply, expr); expr = new BinaryOperatorExpression(MakeConstant(type, n), BinaryOperatorType.Divide, expr); } if (isDouble) { double field = memberName == "PI" ? Math.PI : Math.E; double approxValue = (double)n / ((double)d * field); if (EqualDoubles(approxValue, (double)literalValue)) return expr; } else { float field = memberName == "PI" ? MathF_PI : MathF_E; float approxValue = (float)n / ((float)d * field); if (EqualFloats(approxValue, (float)literalValue)) return expr; } return null; } (long num, long den) = isDouble ? FractionApprox((double)literalValue / (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR_DOUBLE) : FractionApprox((float)literalValue / (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR_FLOAT); if (IsValidFraction(num, den)) { var expr = ExtractExpression(num, den); if (expr != null) return expr; } (num, den) = isDouble ? FractionApprox((double)literalValue * (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR_DOUBLE) : FractionApprox((float)literalValue * (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR_FLOAT); if (IsValidFraction(num, den)) { return ExtractExpression(num, den); } return null; } // based on https://www.ics.uci.edu/~eppstein/numth/frap.c // find rational approximation to given real number // David Eppstein / UC Irvine / 8 Aug 1993 // // With corrections from Arno Formella, May 2008 // // usage: a.out r d // r is real number to approx // d is the maximum denominator allowed // // based on the theory of continued fractions // if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) // then best approximation is found by truncating this series // (with some adjustments in the last term). // // Note the fraction can be recovered as the first column of the matrix // ( a1 1 ) ( a2 1 ) ( a3 1 ) ... // ( 1 0 ) ( 1 0 ) ( 1 0 ) // Instead of keeping the sequence of continued fraction terms, // we just keep the last partial product of these matrices. static (long Num, long Den) FractionApprox(double value, int maxDenominator) { if (value > 0x7FFFFFFF) return (0, 0); double startValue = value; if (value < 0) value = -value; long ai; long[,] m = new long[2, 2]; m[0, 0] = m[1, 1] = 1; m[0, 1] = m[1, 0] = 0; double v = value; while (m[1, 0] * (ai = (long)v) + m[1, 1] <= maxDenominator) { long t = m[0, 0] * ai + m[0, 1]; m[0, 1] = m[0, 0]; m[0, 0] = t; t = m[1, 0] * ai + m[1, 1]; m[1, 1] = m[1, 0]; m[1, 0] = t; if (v - ai == 0) break; v = 1 / (v - ai); if (Math.Abs(v) >= long.MaxValue) { // values greater than long.MaxValue cannot be stored in fraction without overflow. // Because the implicit conversion of long.MaxValue to double loses precision, // it's possible that a value v that is strictly greater than long.MaxValue will // nevertheless compare equal, so we use ">=" to compensate. break; } } if (m[1, 0] == 0) return (0, 0); long firstN = m[0, 0]; long firstD = m[1, 0]; ai = (maxDenominator - m[1, 1]) / m[1, 0]; long secondN = m[0, 0] * ai + m[0, 1]; long secondD = m[1, 0] * ai + m[1, 1]; double firstDelta = Math.Abs(value - firstN / (double)firstD); double secondDelta = Math.Abs(value - secondN / (double)secondD); if (firstDelta < secondDelta) return (startValue < 0 ? -firstN : firstN, firstD); return (startValue < 0 ? -secondN : secondN, secondD); } #endregion #region Convert Parameter public ParameterDeclaration ConvertParameter(IParameter parameter) { if (parameter == null) throw new ArgumentNullException(nameof(parameter)); ParameterDeclaration decl = new ParameterDeclaration(); decl.ParameterModifier = parameter.ReferenceKind; decl.IsParams = parameter.IsParams; decl.IsScopedRef = parameter.Lifetime.ScopedRef; if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes())); } IType parameterType; if (parameter.Type.Kind == TypeKind.ByReference) { // avoid 'out ref' parameterType = ((ByReferenceType)parameter.Type).ElementType; } else { parameterType = parameter.Type; } decl.Type = ConvertType(parameterType); if (this.ShowParameterNames) { decl.Name = parameter.Name; } if (parameter.IsDefaultValueAssignmentAllowed() && this.ShowConstantValues) { try { decl.DefaultExpression = ConvertConstantValue(parameterType, parameter.GetConstantValue(throwOnInvalidMetadata: true)); } catch (BadImageFormatException ex) { decl.DefaultExpression = new ErrorExpression(ex.Message); } } return decl; } #endregion #region Convert Entity public AstNode ConvertSymbol(ISymbol symbol) { if (symbol == null) throw new ArgumentNullException(nameof(symbol)); switch (symbol.SymbolKind) { case SymbolKind.Namespace: return ConvertNamespaceDeclaration((INamespace)symbol); case SymbolKind.Variable: return ConvertVariable((IVariable)symbol); case SymbolKind.Parameter: return ConvertParameter((IParameter)symbol); case SymbolKind.TypeParameter: return ConvertTypeParameter((ITypeParameter)symbol); default: IEntity entity = symbol as IEntity; if (entity != null) return ConvertEntity(entity); throw new ArgumentException("Invalid value for SymbolKind: " + symbol.SymbolKind); } } public EntityDeclaration ConvertEntity(IEntity entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); switch (entity.SymbolKind) { case SymbolKind.TypeDefinition: return ConvertTypeDefinition((ITypeDefinition)entity); case SymbolKind.Field: return ConvertField((IField)entity); case SymbolKind.Property: return ConvertProperty((IProperty)entity); case SymbolKind.Indexer: return ConvertIndexer((IProperty)entity); case SymbolKind.Event: return ConvertEvent((IEvent)entity); case SymbolKind.Method: return ConvertMethod((IMethod)entity); case SymbolKind.Operator: return ConvertOperator((IMethod)entity); case SymbolKind.Constructor: return ConvertConstructor((IMethod)entity); case SymbolKind.Destructor: return ConvertDestructor((IMethod)entity); case SymbolKind.Accessor: IMethod accessor = (IMethod)entity; Accessibility ownerAccessibility = accessor.AccessorOwner?.Accessibility ?? Accessibility.None; return ConvertAccessor(accessor, accessor.AccessorKind, ownerAccessibility, false); default: throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind); } } EntityDeclaration ConvertTypeDefinition(ITypeDefinition typeDefinition) { Modifiers modifiers = Modifiers.None; if (this.ShowAccessibility) { modifiers |= ModifierFromAccessibility(typeDefinition.Accessibility, UsePrivateProtectedAccessibility); } if (this.ShowModifiers) { if (typeDefinition.IsStatic) { modifiers |= Modifiers.Static; } else if (typeDefinition.IsAbstract) { modifiers |= Modifiers.Abstract; } else if (typeDefinition.IsSealed) { modifiers |= Modifiers.Sealed; } } ClassType classType; switch (typeDefinition.Kind) { case TypeKind.Struct: case TypeKind.Void: classType = ClassType.Struct; modifiers &= ~Modifiers.Sealed; if (ShowModifiers) { if (typeDefinition.IsReadOnly) { modifiers |= Modifiers.Readonly; } if (typeDefinition.IsByRefLike) { modifiers |= Modifiers.Ref; } } if (SupportRecordStructs && typeDefinition.IsRecord) { classType = ClassType.RecordStruct; } break; case TypeKind.Enum: classType = ClassType.Enum; modifiers &= ~Modifiers.Sealed; break; case TypeKind.Interface: classType = ClassType.Interface; modifiers &= ~Modifiers.Abstract; break; case TypeKind.Delegate: IMethod invoke = typeDefinition.GetDelegateInvokeMethod(); if (invoke != null) { return ConvertDelegate(invoke, modifiers); } else { goto default; } default: classType = ClassType.Class; if (SupportRecordClasses && typeDefinition.IsRecord) { classType = ClassType.RecordClass; } break; } var decl = new TypeDeclaration(); decl.ClassType = classType; decl.Modifiers = modifiers; if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(typeDefinition.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new TypeResolveResult(typeDefinition)); } decl.Name = typeDefinition.Name == "_" ? "@_" : typeDefinition.Name; int outerTypeParameterCount = (typeDefinition.DeclaringTypeDefinition == null) ? 0 : typeDefinition.DeclaringTypeDefinition.TypeParameterCount; if (this.ShowTypeParameters) { foreach (ITypeParameter tp in typeDefinition.TypeParameters.Skip(outerTypeParameterCount)) { decl.TypeParameters.Add(ConvertTypeParameter(tp)); } } if (this.ShowBaseTypes) { foreach (IType baseType in typeDefinition.DirectBaseTypes) { if (typeDefinition.Kind == TypeKind.Enum && baseType.IsKnownType(KnownTypeCode.Enum)) { // if the declared type is an enum, replace all references to System.Enum with the enum-underlying type if (!typeDefinition.EnumUnderlyingType.IsKnownType(KnownTypeCode.Int32)) { decl.BaseTypes.Add(ConvertType(typeDefinition.EnumUnderlyingType)); } } else if ((typeDefinition.Kind == TypeKind.Struct || typeDefinition.Kind == TypeKind.Void) && baseType.IsKnownType(KnownTypeCode.ValueType)) { // if the declared type is a struct, ignore System.ValueType continue; } else if (baseType.IsKnownType(KnownTypeCode.Object)) { // always ignore System.Object continue; } else if (SupportRecordClasses && typeDefinition.IsRecord && baseType.Name == "IEquatable" && baseType.Namespace == "System" && baseType.TypeArguments.Count == 1 && baseType.TypeArguments[0].Equals(typeDefinition.AsParameterizedType())) { // omit "IEquatable" in records continue; } else { decl.BaseTypes.Add(ConvertType(baseType)); } } } if (this.ShowTypeParameters && this.ShowTypeParameterConstraints) { foreach (ITypeParameter tp in typeDefinition.TypeParameters.Skip(outerTypeParameterCount)) { var constraint = ConvertTypeParameterConstraint(tp); if (constraint != null) decl.Constraints.Add(constraint); } } return decl; } DelegateDeclaration ConvertDelegate(IMethod invokeMethod, Modifiers modifiers) { ITypeDefinition d = invokeMethod.DeclaringTypeDefinition; DelegateDeclaration decl = new DelegateDeclaration(); decl.Modifiers = modifiers & ~Modifiers.Sealed; if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(d.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(invokeMethod.GetReturnTypeAttributes(), "return")); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new TypeResolveResult(d)); } decl.ReturnType = ConvertType(invokeMethod.ReturnType); if (invokeMethod.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } decl.Name = d.Name; int outerTypeParameterCount = (d.DeclaringTypeDefinition == null) ? 0 : d.DeclaringTypeDefinition.TypeParameterCount; if (this.ShowTypeParameters) { foreach (ITypeParameter tp in d.TypeParameters.Skip(outerTypeParameterCount)) { decl.TypeParameters.Add(ConvertTypeParameter(tp)); } } foreach (IParameter p in invokeMethod.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } if (this.ShowTypeParameters && this.ShowTypeParameterConstraints) { foreach (ITypeParameter tp in d.TypeParameters.Skip(outerTypeParameterCount)) { var constraint = ConvertTypeParameterConstraint(tp); if (constraint != null) decl.Constraints.Add(constraint); } } return decl; } FieldDeclaration ConvertField(IField field) { FieldDeclaration decl = new FieldDeclaration(); if (ShowModifiers) { Modifiers m = GetMemberModifiers(field); if (field.IsConst) { m &= ~Modifiers.Static; m |= Modifiers.Const; } else if (field.IsReadOnly) { m |= Modifiers.Readonly; } else if (field.IsVolatile) { m |= Modifiers.Volatile; } decl.Modifiers = m; } if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(field.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, field)); } decl.ReturnType = ConvertType(field.ReturnType); if (decl.ReturnType is ComposedType ct && ct.HasRefSpecifier && field.ReturnTypeIsRefReadOnly) { ct.HasReadOnlySpecifier = true; } Expression initializer = null; if (field.IsConst && this.ShowConstantValues) { try { initializer = ConvertConstantValue(field.Type, field.GetConstantValue(throwOnInvalidMetadata: true)); } catch (BadImageFormatException ex) { initializer = new ErrorExpression(ex.Message); } } decl.Variables.Add(new VariableInitializer(field.Name, initializer)); return decl; } BlockStatement GenerateBodyBlock() { if (GenerateBody) { return new BlockStatement { new ThrowStatement(new ObjectCreateExpression(ConvertType(new TopLevelTypeName("System", "NotImplementedException", 0)))) }; } else { return BlockStatement.Null; } } Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute) { if (accessor == null) return Accessor.Null; Accessor decl = new Accessor(); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return")); if (addParameterAttribute && accessor.Parameters.Count > 0) { decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param")); } } if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility) decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility, UsePrivateProtectedAccessibility); if (this.ShowModifiers && accessor.HasReadonlyModifier()) decl.Modifiers |= Modifiers.Readonly; TokenRole keywordRole = kind switch { MethodSemanticsAttributes.Getter => PropertyDeclaration.GetKeywordRole, MethodSemanticsAttributes.Setter => PropertyDeclaration.SetKeywordRole, MethodSemanticsAttributes.Adder => CustomEventDeclaration.AddKeywordRole, MethodSemanticsAttributes.Remover => CustomEventDeclaration.RemoveKeywordRole, _ => null }; if (kind == MethodSemanticsAttributes.Setter && SupportInitAccessors && accessor.IsInitOnly) { keywordRole = PropertyDeclaration.InitKeywordRole; } if (keywordRole != null) { decl.AddChild(new CSharpTokenNode(TextLocation.Empty, keywordRole), keywordRole); } if (accessor.IsInitOnly && keywordRole != PropertyDeclaration.InitKeywordRole) { decl.AddChild(new Comment("init", CommentType.MultiLine), Roles.Comment); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, accessor)); } if (GenerateBody) { decl.Body = GenerateBodyBlock(); } else { decl.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Semicolon), Roles.Semicolon); } return decl; } PropertyDeclaration ConvertProperty(IProperty property) { PropertyDeclaration decl = new PropertyDeclaration(); decl.Modifiers = GetMemberModifiers(property); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(property.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, property)); } decl.ReturnType = ConvertType(property.ReturnType); if (property.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } decl.Name = property.Name; decl.Getter = ConvertAccessor(property.Getter, MethodSemanticsAttributes.Getter, property.Accessibility, false); decl.Setter = ConvertAccessor(property.Setter, MethodSemanticsAttributes.Setter, property.Accessibility, true); decl.PrivateImplementationType = GetExplicitInterfaceType(property); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); return decl; } static void MergeReadOnlyModifiers(EntityDeclaration decl, Accessor accessor1, Accessor accessor2) { if (accessor1.HasModifier(Modifiers.Readonly) && accessor2.IsNull) { accessor1.Modifiers &= ~Modifiers.Readonly; decl.Modifiers |= Modifiers.Readonly; } else if (accessor1.HasModifier(Modifiers.Readonly) && accessor2.HasModifier(Modifiers.Readonly)) { accessor1.Modifiers &= ~Modifiers.Readonly; accessor2.Modifiers &= ~Modifiers.Readonly; decl.Modifiers |= Modifiers.Readonly; } } IndexerDeclaration ConvertIndexer(IProperty indexer) { IndexerDeclaration decl = new IndexerDeclaration(); decl.Modifiers = GetMemberModifiers(indexer); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(indexer.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, indexer)); } decl.ReturnType = ConvertType(indexer.ReturnType); if (indexer.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } foreach (IParameter p in indexer.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } decl.Getter = ConvertAccessor(indexer.Getter, MethodSemanticsAttributes.Getter, indexer.Accessibility, false); decl.Setter = ConvertAccessor(indexer.Setter, MethodSemanticsAttributes.Setter, indexer.Accessibility, true); decl.PrivateImplementationType = GetExplicitInterfaceType(indexer); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); return decl; } EntityDeclaration ConvertEvent(IEvent ev) { if (this.UseCustomEvents) { CustomEventDeclaration decl = new CustomEventDeclaration(); decl.Modifiers = GetMemberModifiers(ev); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(ev.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, ev)); } decl.ReturnType = ConvertType(ev.ReturnType); decl.Name = ev.Name; decl.AddAccessor = ConvertAccessor(ev.AddAccessor, MethodSemanticsAttributes.Adder, ev.Accessibility, true); decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, MethodSemanticsAttributes.Remover, ev.Accessibility, true); decl.PrivateImplementationType = GetExplicitInterfaceType(ev); MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor); return decl; } else { EventDeclaration decl = new EventDeclaration(); decl.Modifiers = GetMemberModifiers(ev); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(ev.GetAttributes())); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, ev)); } decl.ReturnType = ConvertType(ev.ReturnType); decl.Variables.Add(new VariableInitializer(ev.Name)); return decl; } } MethodDeclaration ConvertMethod(IMethod method) { MethodDeclaration decl = new MethodDeclaration(); decl.Modifiers = GetMemberModifiers(method); if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(method.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(method.GetReturnTypeAttributes(), "return")); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, method)); } decl.ReturnType = ConvertType(method.ReturnType); if (method.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } decl.Name = method.Name; if (this.ShowTypeParameters) { foreach (ITypeParameter tp in method.TypeParameters) { decl.TypeParameters.Add(ConvertTypeParameter(tp)); } } foreach (IParameter p in method.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any()) decl.Parameters.First().HasThisModifier = true; if (this.ShowTypeParameters && this.ShowTypeParameterConstraints && !method.IsOverride && !method.IsExplicitInterfaceImplementation) { foreach (ITypeParameter tp in method.TypeParameters) { var constraint = ConvertTypeParameterConstraint(tp); if (constraint != null) decl.Constraints.Add(constraint); } } decl.Body = GenerateBodyBlock(); decl.PrivateImplementationType = GetExplicitInterfaceType(method); return decl; } EntityDeclaration ConvertOperator(IMethod op) { int dot = op.Name.LastIndexOf('.'); string name = op.Name.Substring(dot + 1); OperatorType? opType = OperatorDeclaration.GetOperatorType(name); if (opType == null) return ConvertMethod(op); if (opType == OperatorType.UnsignedRightShift && !SupportUnsignedRightShift) return ConvertMethod(op); if (!SupportOperatorChecked && OperatorDeclaration.IsChecked(opType.Value)) return ConvertMethod(op); OperatorDeclaration decl = new OperatorDeclaration(); decl.Modifiers = GetMemberModifiers(op); decl.OperatorType = opType.Value; decl.ReturnType = ConvertType(op.ReturnType); if (op.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { ct.HasReadOnlySpecifier = true; } foreach (IParameter p in op.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } if (ShowAttributes) { decl.Attributes.AddRange(ConvertAttributes(op.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(op.GetReturnTypeAttributes(), "return")); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, op)); } decl.Body = GenerateBodyBlock(); decl.PrivateImplementationType = GetExplicitInterfaceType(op); return decl; } ConstructorDeclaration ConvertConstructor(IMethod ctor) { ConstructorDeclaration decl = new ConstructorDeclaration(); decl.Modifiers = GetMemberModifiers(ctor); if (ShowAttributes) decl.Attributes.AddRange(ConvertAttributes(ctor.GetAttributes())); if (ctor.DeclaringTypeDefinition != null) decl.Name = ctor.DeclaringTypeDefinition.Name; foreach (IParameter p in ctor.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, ctor)); } decl.Body = GenerateBodyBlock(); return decl; } DestructorDeclaration ConvertDestructor(IMethod dtor) { DestructorDeclaration decl = new DestructorDeclaration(); if (ShowAttributes) decl.Attributes.AddRange(ConvertAttributes(dtor.GetAttributes())); if (dtor.DeclaringTypeDefinition != null) decl.Name = dtor.DeclaringTypeDefinition.Name; if (AddResolveResultAnnotations) { decl.AddAnnotation(new MemberResolveResult(null, dtor)); } decl.Body = GenerateBodyBlock(); return decl; } #endregion #region Convert Modifiers public static Modifiers ModifierFromAccessibility(Accessibility accessibility, bool usePrivateProtected) { switch (accessibility) { case Accessibility.Private: return Modifiers.Private; case Accessibility.Public: return Modifiers.Public; case Accessibility.Protected: return Modifiers.Protected; case Accessibility.Internal: return Modifiers.Internal; case Accessibility.ProtectedOrInternal: return Modifiers.Protected | Modifiers.Internal; case Accessibility.ProtectedAndInternal: return usePrivateProtected ? Modifiers.Private | Modifiers.Protected : Modifiers.Protected; default: return Modifiers.None; } } bool NeedsAccessibility(IMember member) { var declaringType = member.DeclaringType; if (member.IsExplicitInterfaceImplementation) return false; switch (member.SymbolKind) { case SymbolKind.Constructor: return !member.IsStatic; case SymbolKind.Destructor: return false; default: if (declaringType?.Kind == TypeKind.Interface) { return member.Accessibility != Accessibility.Public; } return member is not IMethod method || !method.IsLocalFunction; } } Modifiers GetMemberModifiers(IMember member) { Modifiers m = Modifiers.None; if (this.ShowAccessibility && NeedsAccessibility(member)) { m |= ModifierFromAccessibility(member.Accessibility, UsePrivateProtectedAccessibility); } if (this.ShowModifiers) { if (member is LocalFunctionMethod localFunction) { if (localFunction.IsStaticLocalFunction) { m |= Modifiers.Static; } } else { if (member.IsStatic) { m |= Modifiers.Static; } if (member is IMethod method && method.ThisIsRefReadOnly && method.DeclaringTypeDefinition?.IsReadOnly == false) { m |= Modifiers.Readonly; } var declaringType = member.DeclaringType; if (declaringType.Kind == TypeKind.Interface) { if (!member.IsStatic && !member.IsVirtual && !member.IsAbstract && !member.IsOverride && member.Accessibility != Accessibility.Private && member is IMethod method2 && method2.HasBody) { m |= Modifiers.Sealed; } if (member.IsStatic) { // modifiers of static members in interfaces: if (member.IsAbstract) m |= Modifiers.Abstract; else if (member.IsVirtual && !member.IsOverride) m |= Modifiers.Virtual; } } else { if (member.IsAbstract) m |= Modifiers.Abstract; else if (member.IsVirtual && !member.IsOverride) m |= Modifiers.Virtual; if (member.IsOverride && !member.IsExplicitInterfaceImplementation) m |= Modifiers.Override; if (member.IsSealed && !member.IsExplicitInterfaceImplementation) m |= Modifiers.Sealed; } } } return m; } #endregion #region Convert Type Parameter internal TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp) { TypeParameterDeclaration decl = new TypeParameterDeclaration(); decl.Variance = tp.Variance; decl.Name = tp.Name; if (ShowAttributes) decl.Attributes.AddRange(ConvertAttributes(tp.GetAttributes())); return decl; } internal Constraint ConvertTypeParameterConstraint(ITypeParameter tp) { if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && !tp.AllowsRefLikeType && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) { return null; } Constraint c = new Constraint(); c.TypeParameter = MakeSimpleType(tp.Name); if (tp.HasReferenceTypeConstraint) { if (tp.NullabilityConstraint == Nullability.Nullable) { c.BaseTypes.Add(new PrimitiveType("class").MakeNullableType()); } else { c.BaseTypes.Add(new PrimitiveType("class")); } } else if (tp.HasValueTypeConstraint) { if (tp.HasUnmanagedConstraint) { c.BaseTypes.Add(new PrimitiveType("unmanaged")); } else { c.BaseTypes.Add(new PrimitiveType("struct")); } } else if (tp.NullabilityConstraint == Nullability.NotNullable) { c.BaseTypes.Add(new PrimitiveType("notnull")); } foreach (TypeConstraint t in tp.TypeConstraints) { if (!IsObjectOrValueType(t.Type) || t.Attributes.Count > 0) { AstType astType = ConvertType(t.Type); if (t.Attributes.Count > 0) { var attrSection = new AttributeSection(); attrSection.Attributes.AddRange(t.Attributes.Select(ConvertAttribute)); astType = new ComposedType { Attributes = { attrSection }, BaseType = astType }; } c.BaseTypes.Add(astType); } } if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) { c.BaseTypes.Add(new PrimitiveType("new")); } if (tp.AllowsRefLikeType) { c.BaseTypes.Add(new PrimitiveType("allows ref struct")); } return c; } static bool IsObjectOrValueType(IType type) { ITypeDefinition d = type.GetDefinition(); return d != null && (d.KnownTypeCode == KnownTypeCode.Object || d.KnownTypeCode == KnownTypeCode.ValueType); } #endregion #region Convert Variable public VariableDeclarationStatement ConvertVariable(IVariable v) { VariableDeclarationStatement decl = new VariableDeclarationStatement(); decl.Modifiers = v.IsConst ? Modifiers.Const : Modifiers.None; decl.Type = ConvertType(v.Type); Expression initializer = null; if (v.IsConst) { try { initializer = ConvertConstantValue(v.Type, v.GetConstantValue(throwOnInvalidMetadata: true)); } catch (BadImageFormatException ex) { initializer = new ErrorExpression(ex.Message); } } decl.Variables.Add(new VariableInitializer(v.Name, initializer)); return decl; } #endregion NamespaceDeclaration ConvertNamespaceDeclaration(INamespace ns) { return new NamespaceDeclaration(ns.FullName); } AstType GetExplicitInterfaceType(IMember member) { if (member.IsExplicitInterfaceImplementation) { var baseMember = member.ExplicitlyImplementedInterfaceMembers.FirstOrDefault(); if (baseMember != null) return ConvertType(baseMember.DeclaringType); } return null; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Syntax/VariableDesignation.cs ================================================ // Copyright (c) 2020 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public abstract class VariableDesignation : AstNode { public override NodeType NodeType => NodeType.Unknown; #region Null public new static readonly VariableDesignation Null = new NullVariableDesignation(); sealed class NullVariableDesignation : VariableDesignation { public override bool IsNull { get { return true; } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion } /// /// Identifier /// public class SingleVariableDesignation : VariableDesignation { public string Identifier { get { return GetChildByRole(Roles.Identifier).Name; } set { SetChildByRole(Roles.Identifier, Syntax.Identifier.Create(value)); } } public Identifier IdentifierToken { get { return GetChildByRole(Roles.Identifier); } set { SetChildByRole(Roles.Identifier, value); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitSingleVariableDesignation(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitSingleVariableDesignation(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitSingleVariableDesignation(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is SingleVariableDesignation o && MatchString(this.Identifier, o.Identifier); } } /// /// ( VariableDesignation (, VariableDesignation)* ) /// public class ParenthesizedVariableDesignation : VariableDesignation { public CSharpTokenNode LParToken { get { return GetChildByRole(Roles.LPar); } } public AstNodeCollection VariableDesignations { get { return GetChildrenByRole(Roles.VariableDesignationRole); } } public CSharpTokenNode RParToken { get { return GetChildByRole(Roles.RPar); } } public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitParenthesizedVariableDesignation(this); } public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitParenthesizedVariableDesignation(this); } public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitParenthesizedVariableDesignation(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { return other is ParenthesizedVariableDesignation o && VariableDesignations.DoMatch(o.VariableDesignations, match); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs ================================================ // 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 // 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.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Add checked/unchecked blocks. /// public class AddCheckedBlocks : IAstTransform { #region Annotation sealed class CheckedUncheckedAnnotation { /// /// true=checked, false=unchecked /// public bool IsChecked; /// /// Require an explicit unchecked block (can't rely on the project-level unchecked context) /// public bool IsExplicit; } public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true }; public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false }; public static readonly object ExplicitUncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false, IsExplicit = true }; #endregion /* We treat placing checked/unchecked blocks as an optimization problem, with the following goals: 1. Use minimum number of checked blocks+expressions 2. Prefer checked expressions over checked blocks 3. Make the scope of checked expressions as small as possible 4. Open checked blocks as late as possible, and close checked blocks as late as possible (where goal 1 has the highest priority) Goal 4a (open checked blocks as late as possible) is necessary so that we don't move variable declarations into checked blocks, as the variable might still be used after the checked block. (this could cause DeclareVariables to omit the variable declaration, producing incorrect code) Goal 4b (close checked blocks as late as possible) makes the code look nicer in this case: checked { int c = a + b; int r = a + c; return r; } If the checked block was closed as early as possible, the variable r would have to be declared outside (this would work, but look badly) */ #region struct Cost struct Cost { // highest possible cost so that the Blocks+Expressions addition doesn't overflow public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff); public readonly int Blocks; public readonly int Expressions; public Cost(int blocks, int expressions) { this.Blocks = blocks; this.Expressions = expressions; } public static bool operator <(Cost a, Cost b) { return a.Blocks + a.Expressions < b.Blocks + b.Expressions || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks; } public static bool operator >(Cost a, Cost b) { return a.Blocks + a.Expressions > b.Blocks + b.Expressions || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks; } public static bool operator <=(Cost a, Cost b) { return a.Blocks + a.Expressions < b.Blocks + b.Expressions || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks; } public static bool operator >=(Cost a, Cost b) { return a.Blocks + a.Expressions > b.Blocks + b.Expressions || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks; } public static Cost operator +(Cost a, Cost b) { return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions); } public override string ToString() { return string.Format("[{0} + {1}]", Blocks, Expressions); } /// /// Gets the new cost if an expression with this cost is wrapped in a checked/unchecked expression. /// internal Cost WrapInCheckedExpr() { if (Expressions == 0) { return new Cost(Blocks, 1); } else { // hack: penalize multiple layers of nested expressions // This doesn't really fit into the original idea of minimizing the number of block+expressions; // but tends to produce better-looking results due to less nesting. return new Cost(Blocks, Expressions + 2); } } } #endregion #region class InsertedNode /// /// Holds the blocks and expressions that should be inserted /// abstract class InsertedNode { public static InsertedNode operator +(InsertedNode a, InsertedNode b) { if (a == null) return b; if (b == null) return a; return new InsertedNodeList(a, b); } public abstract void Insert(); } class InsertedNodeList : InsertedNode { readonly InsertedNode child1, child2; public InsertedNodeList(AddCheckedBlocks.InsertedNode child1, AddCheckedBlocks.InsertedNode child2) { this.child1 = child1; this.child2 = child2; } public override void Insert() { child1.Insert(); child2.Insert(); } } class InsertedExpression : InsertedNode { readonly Expression expression; readonly bool isChecked; public InsertedExpression(Expression expression, bool isChecked) { this.expression = expression; this.isChecked = isChecked; } public override void Insert() { if (isChecked) expression.ReplaceWith(e => new CheckedExpression { Expression = e }); else expression.ReplaceWith(e => new UncheckedExpression { Expression = e }); } } class InsertedBlock : InsertedNode { readonly Statement firstStatement; // inclusive readonly Statement lastStatement; // exclusive readonly bool isChecked; public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked) { this.firstStatement = firstStatement; this.lastStatement = lastStatement; this.isChecked = isChecked; } public override void Insert() { BlockStatement newBlock = new BlockStatement(); // Move all statements except for the first Statement next; for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) { next = stmt.GetNextStatement(); newBlock.Add(stmt.Detach()); } // Replace the first statement with the new (un)checked block if (isChecked) firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock }); else firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock }); // now also move the first node into the new block newBlock.Statements.InsertAfter(null, firstStatement); } } #endregion #region class Result /// /// Holds the result of an insertion operation. /// class Result { public Cost CostInCheckedContext; public InsertedNode NodesToInsertInCheckedContext; public Cost CostInUncheckedContext; public InsertedNode NodesToInsertInUncheckedContext; } #endregion public void Run(AstNode node, TransformContext context) { BlockStatement block = node as BlockStatement; if (block == null) { for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { Run(child, context); } } else { Result r = GetResultFromBlock(block); if (context.DecompileRun.Settings.CheckForOverflowUnderflow) { r.NodesToInsertInCheckedContext?.Insert(); } else { r.NodesToInsertInUncheckedContext?.Insert(); } } } Result GetResultFromBlock(BlockStatement block) { // For a block, we are tracking 4 possibilities: // a) context is checked, no unchecked block open Cost costCheckedContext = new Cost(0, 0); InsertedNode nodesCheckedContext = null; // b) context is checked, an unchecked block is open Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite; InsertedNode nodesCheckedContextUncheckedBlockOpen = null; Statement uncheckedBlockStart = null; // c) context is unchecked, no checked block open Cost costUncheckedContext = new Cost(0, 0); InsertedNode nodesUncheckedContext = null; // d) context is unchecked, a checked block is open Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite; InsertedNode nodesUncheckedContextCheckedBlockOpen = null; Statement checkedBlockStart = null; Statement statement = block.Statements.FirstOrDefault(); while (true) { // Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4b) if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) { costCheckedContext = costCheckedContextUncheckedBlockOpen; nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false); } if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) { costUncheckedContext = costUncheckedContextCheckedBlockOpen; nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true); } if (statement == null) break; // Now try opening blocks. We use '<=' so that blocks are opened as late as possible. (goal 4a) if (costCheckedContext + new Cost(1, 0) <= costCheckedContextUncheckedBlockOpen) { costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0); nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext; uncheckedBlockStart = statement; } if (costUncheckedContext + new Cost(1, 0) <= costUncheckedContextCheckedBlockOpen) { costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0); nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext; checkedBlockStart = statement; } // Now handle the statement Result stmtResult = GetResult(statement); costCheckedContext += stmtResult.CostInCheckedContext; nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext; costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext; nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext; costUncheckedContext += stmtResult.CostInUncheckedContext; nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext; costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext; nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext; if (statement is LabelStatement || statement is LocalFunctionDeclarationStatement) { // We can't move labels into blocks because that might cause goto-statements // to be unable to jump to the labels. // Also, we can't move local functions into blocks, because that might cause // them to become out of scope from the call-sites. costCheckedContextUncheckedBlockOpen = Cost.Infinite; costUncheckedContextCheckedBlockOpen = Cost.Infinite; } statement = statement.GetNextStatement(); } return new Result { CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext, CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext }; } Result GetResult(AstNode node) { if (node is BlockStatement) return GetResultFromBlock((BlockStatement)node); Result result = new Result(); for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { Result childResult = GetResult(child); result.CostInCheckedContext += childResult.CostInCheckedContext; result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext; result.CostInUncheckedContext += childResult.CostInUncheckedContext; result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext; } Expression expr = node as Expression; if (expr != null) { CheckedUncheckedAnnotation annotation = expr.Annotation(); if (annotation != null) { if (annotation.IsExplicit) { // We don't yet support distinguishing CostInUncheckedContext vs. CostInExplicitUncheckedContext, // so we always force an unchecked() expression here. if (annotation.IsChecked) throw new NotImplementedException("explicit checked"); // should not be needed result.CostInCheckedContext = result.CostInUncheckedContext.WrapInCheckedExpr(); result.CostInUncheckedContext = result.CostInUncheckedContext.WrapInCheckedExpr(); result.NodesToInsertInUncheckedContext += new InsertedExpression(expr, annotation.IsChecked); result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext; } else { // If the annotation requires this node to be in a specific context, add a huge cost to the other context // That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any // solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}") if (annotation.IsChecked) result.CostInUncheckedContext += new Cost(10000, 0); else result.CostInCheckedContext += new Cost(10000, 0); } } // Embed this node in an checked/unchecked expression: if (expr.Parent is ExpressionStatement) { // We cannot use checked/unchecked for top-level-expressions. } else if (expr.Role.IsValid(Expression.Null)) { // We use '<' so that expressions are introduced on the deepest level possible (goal 3) var costIfWrapWithChecked = result.CostInCheckedContext.WrapInCheckedExpr(); var costIfWrapWithUnchecked = result.CostInUncheckedContext.WrapInCheckedExpr(); if (costIfWrapWithChecked < result.CostInUncheckedContext) { result.CostInUncheckedContext = costIfWrapWithChecked; result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true); } else if (costIfWrapWithUnchecked < result.CostInCheckedContext) { result.CostInCheckedContext = costIfWrapWithUnchecked; result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false); } } } return result; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/AddXmlDocumentationTransform.cs ================================================ // 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 // 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.IO; using System.Linq; using System.Reflection.Metadata; using System.Xml; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Adds XML documentation for member definitions. /// public class AddXmlDocumentationTransform : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { if (!context.Settings.ShowXmlDocumentation || context.DecompileRun.DocumentationProvider == null) return; try { var provider = context.DecompileRun.DocumentationProvider; foreach (var entityDecl in rootNode.DescendantsAndSelf.OfType()) { if (!(entityDecl.GetSymbol() is IEntity entity)) continue; string doc = provider.GetDocumentation(entity); if (doc != null) { InsertXmlDocumentation(entityDecl, new StringReader(doc)); } } } catch (XmlException ex) { string[] msg = (" Exception while reading XmlDoc: " + ex).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var insertionPoint = rootNode.FirstChild; for (int i = 0; i < msg.Length; i++) rootNode.InsertChildBefore(insertionPoint, new Comment(msg[i], CommentType.Documentation), Roles.Comment); } } static void InsertXmlDocumentation(AstNode node, StringReader r) { // Find the first non-empty line: string firstLine; do { firstLine = r.ReadLine(); if (firstLine == null) return; } while (string.IsNullOrWhiteSpace(firstLine)); string indentation = firstLine.Substring(0, firstLine.Length - firstLine.TrimStart().Length); string line = firstLine; int skippedWhitespaceLines = 0; // Copy all lines from input to output, except for empty lines at the end. while (line != null) { if (string.IsNullOrWhiteSpace(line)) { skippedWhitespaceLines++; } else { while (skippedWhitespaceLines > 0) { Comment emptyLine = new Comment(string.Empty, CommentType.Documentation); emptyLine.AddAnnotation(node.GetResolveResult()); node.Parent.InsertChildBefore(node, emptyLine, Roles.Comment); skippedWhitespaceLines--; } if (line.StartsWith(indentation, StringComparison.Ordinal)) line = line.Substring(indentation.Length); Comment comment = new Comment(" " + line, CommentType.Documentation); comment.AddAnnotation(node.GetResolveResult()); node.Parent.InsertChildBefore(node, comment, Roles.Comment); } line = r.ReadLine(); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Combines query expressions and removes transparent identifiers. /// public class CombineQueryExpressions : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { if (!context.Settings.QueryExpressions) return; CombineQueries(rootNode, new Dictionary()); } static readonly InvocationExpression castPattern = new InvocationExpression { Target = new MemberReferenceExpression { Target = new AnyNode("inExpr"), MemberName = "Cast", TypeArguments = { new AnyNode("targetType") } } }; void CombineQueries(AstNode node, Dictionary fromOrLetIdentifiers) { AstNode next; for (AstNode child = node.FirstChild; child != null; child = next) { // store reference to next child before transformation next = child.NextSibling; CombineQueries(child, fromOrLetIdentifiers); } if (node is QueryExpression query) { QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); if (fromClause.Expression is QueryExpression innerQuery) { if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, fromOrLetIdentifiers)) { RemoveTransparentIdentifierReferences(query, fromOrLetIdentifiers); } else { QueryContinuationClause continuation = new QueryContinuationClause(); continuation.PrecedingQuery = innerQuery.Detach(); continuation.Identifier = fromClause.Identifier; continuation.CopyAnnotationsFrom(fromClause); fromClause.ReplaceWith(continuation); } } else { Match m = castPattern.Match(fromClause.Expression); if (m.Success) { fromClause.Type = m.Get("targetType").Single().Detach(); fromClause.Expression = m.Get("inExpr").Single().Detach(); } } } } static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { Expression = new AnonymousTypeCreateExpression { Initializers = { new Repeat( new Choice { new IdentifierExpression(Pattern.AnyString).WithName("expr"), // name is equivalent to name = name new MemberReferenceExpression(new AnyNode(), Pattern.AnyString).WithName("expr"), // expr.name is equivalent to name = expr.name new NamedExpression { Name = Pattern.AnyString, Expression = new AnyNode() }.WithName("expr") } ) { MinCount = 1 } } } }; bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, Dictionary letClauses) { if (!CSharpDecompiler.IsTransparentIdentifier(fromClause.Identifier)) return false; QuerySelectClause selectClause = innerQuery.Clauses.Last() as QuerySelectClause; Match match = selectTransparentIdentifierPattern.Match(selectClause); if (!match.Success) return false; // from * in (from x in ... select new { members of anonymous type }) ... // => // from x in ... { let x = ... } ... fromClause.Remove(); selectClause.Remove(); // Move clauses from innerQuery to query QueryClause insertionPos = null; foreach (var clause in innerQuery.Clauses) { query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); } foreach (var expr in match.Get("expr")) { switch (expr) { case IdentifierExpression identifier: letClauses[identifier.Identifier] = identifier.Annotation(); break; case MemberReferenceExpression member: AddQueryLetClause(member.MemberName, member); break; case NamedExpression namedExpression: if (namedExpression.Expression is IdentifierExpression identifierExpression && namedExpression.Name == identifierExpression.Identifier) { letClauses[namedExpression.Name] = identifierExpression.Annotation(); continue; } AddQueryLetClause(namedExpression.Name, namedExpression.Expression); break; } } return true; void AddQueryLetClause(string name, Expression expression) { QueryLetClause letClause = new QueryLetClause { Identifier = name, Expression = expression.Detach() }; var annotation = new LetIdentifierAnnotation(); letClause.AddAnnotation(annotation); letClauses[name] = annotation; query.Clauses.InsertAfter(insertionPos, letClause); } } /// /// Removes all occurrences of transparent identifiers /// void RemoveTransparentIdentifierReferences(AstNode node, Dictionary fromOrLetIdentifiers) { foreach (AstNode child in node.Children) { RemoveTransparentIdentifierReferences(child, fromOrLetIdentifiers); } if (node is MemberReferenceExpression mre && mre.Target is IdentifierExpression ident && CSharpDecompiler.IsTransparentIdentifier(ident.Identifier)) { IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName); mre.TypeArguments.MoveTo(newIdent.TypeArguments); newIdent.CopyAnnotationsFrom(mre); newIdent.RemoveAnnotations(); // remove the reference to the property of the anonymous type if (fromOrLetIdentifiers.TryGetValue(mre.MemberName, out var annotation)) newIdent.AddAnnotation(annotation); mre.ReplaceWith(newIdent); return; } } } public class LetIdentifierAnnotation { } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs ================================================ // 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 // 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.Diagnostics; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Base class for AST visitors that need the current type/method context info. /// public abstract class ContextTrackingVisitor : DepthFirstAstVisitor { protected ITypeDefinition currentTypeDefinition; protected IMethod currentMethod; protected void Initialize(TransformContext context) { currentTypeDefinition = context.CurrentTypeDefinition; currentMethod = context.CurrentMember as IMethod; } protected void Uninitialize() { currentTypeDefinition = null; currentMethod = null; } public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration) { ITypeDefinition oldType = currentTypeDefinition; try { currentTypeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition; return base.VisitTypeDeclaration(typeDeclaration); } finally { currentTypeDefinition = oldType; } } public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration) { var oldMethod = currentMethod; try { currentMethod = methodDeclaration.GetSymbol() as IMethod; return base.VisitMethodDeclaration(methodDeclaration); } finally { currentMethod = oldMethod; } } public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { var oldMethod = currentMethod; try { currentMethod = constructorDeclaration.GetSymbol() as IMethod; return base.VisitConstructorDeclaration(constructorDeclaration); } finally { currentMethod = oldMethod; } } public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { var oldMethod = currentMethod; try { currentMethod = destructorDeclaration.GetSymbol() as IMethod; return base.VisitDestructorDeclaration(destructorDeclaration); } finally { currentMethod = oldMethod; } } public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { var oldMethod = currentMethod; try { currentMethod = operatorDeclaration.GetSymbol() as IMethod; return base.VisitOperatorDeclaration(operatorDeclaration); } finally { currentMethod = oldMethod; } } public override TResult VisitAccessor(Accessor accessor) { var oldMethod = currentMethod; try { currentMethod = accessor.GetSymbol() as IMethod; return base.VisitAccessor(accessor); } finally { currentMethod = oldMethod; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/CustomPatterns.cs ================================================ // 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 // 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.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.CSharp.Transforms { sealed class TypePattern : Pattern { readonly string ns; readonly string name; public TypePattern(Type type) { this.ns = type.Namespace; this.name = type.Name; } public override bool DoMatch(INode other, Match match) { ComposedType ct = other as ComposedType; AstType o; if (ct != null && !ct.HasRefSpecifier && !ct.HasNullableSpecifier && ct.PointerRank == 0 && !ct.ArraySpecifiers.Any()) { // Special case: ILSpy sometimes produces a ComposedType but then removed all array specifiers // from it. In that case, we need to look at the base type for the annotations. o = ct.BaseType; } else { o = other as AstType; if (o == null) return false; } var trr = o.GetResolveResult() as TypeResolveResult; return trr != null && trr.Type.Namespace == ns && trr.Type.Name == name; } public override string ToString() { return name; } } sealed class LdTokenPattern : Pattern { AnyNode childNode; public LdTokenPattern(string groupName) { this.childNode = new AnyNode(groupName); } public override bool DoMatch(INode other, Match match) { InvocationExpression ie = other as InvocationExpression; if (ie != null && ie.Annotation() != null && ie.Arguments.Count == 1) { return childNode.DoMatch(ie.Arguments.Single(), match); } return false; } public override string ToString() { return "ldtoken(...)"; } } /// /// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators) /// sealed class TypeOfPattern : Pattern { INode childNode; public TypeOfPattern(string groupName) { childNode = new MemberReferenceExpression( new InvocationExpression( new MemberReferenceExpression( new TypeReferenceExpression { Type = new TypePattern(typeof(Type)).ToType() }, "GetTypeFromHandle"), new TypeOfExpression(new AnyNode(groupName)) ), "TypeHandle"); } public override bool DoMatch(INode other, Match match) { return childNode.DoMatch(other, match); } public override string ToString() { return "typeof(...)"; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Insert variable declarations. /// public class DeclareVariables : IAstTransform { /// /// Represents a position immediately before nextNode. /// nextNode is either an ExpressionStatement in a BlockStatement, or an initializer in a for-loop. /// [DebuggerDisplay("level = {level}, nextNode = {nextNode}")] struct InsertionPoint { /// /// The nesting level of `nextNode` within the AST. /// Used to speed up FindCommonParent(). /// internal int level; internal AstNode nextNode; /// Go up one level internal InsertionPoint Up() { return new InsertionPoint { level = level - 1, nextNode = nextNode.Parent }; } internal InsertionPoint UpTo(int targetLevel) { InsertionPoint result = this; while (result.level > targetLevel) { result.nextNode = result.nextNode.Parent; result.level -= 1; } return result; } } enum VariableInitKind { None, NeedsDefaultValue, NeedsSkipInit } [DebuggerDisplay("VariableToDeclare(Name={Name})")] class VariableToDeclare { public readonly ILVariable ILVariable; public IType Type => ILVariable.Type; public string Name => ILVariable.Name; /// /// Whether the variable needs to be default-initialized. /// public VariableInitKind DefaultInitialization; /// /// Integer value that can be used to compare to VariableToDeclare instances /// to determine which variable was used first in the source code. /// /// The variable with the lower SourceOrder value has the insertion point /// that comes first in the source code. /// public int SourceOrder; /// /// The insertion point, i.e. the node before which the variable declaration should be inserted. /// public InsertionPoint InsertionPoint; /// /// The first use of the variable. /// public IdentifierExpression FirstUse; public VariableToDeclare ReplacementDueToCollision; public bool InvolvedInCollision; public bool RemovedDueToCollision => ReplacementDueToCollision != null; public bool DeclaredInDeconstruction; public VariableToDeclare(ILVariable variable, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder) { this.ILVariable = variable; if (variable.UsesInitialValue) { if (variable.InitialValueIsInitialized) { this.DefaultInitialization = VariableInitKind.NeedsDefaultValue; } else { this.DefaultInitialization = VariableInitKind.NeedsSkipInit; } } else { this.DefaultInitialization = VariableInitKind.None; } this.InsertionPoint = insertionPoint; this.FirstUse = firstUse; this.SourceOrder = sourceOrder; } } readonly Dictionary variableDict = new Dictionary(); TransformContext context; public void Run(AstNode rootNode, TransformContext context) { try { if (this.context != null) throw new InvalidOperationException("Reentrancy in DeclareVariables?"); this.context = context; variableDict.Clear(); EnsureExpressionStatementsAreValid(rootNode); FindInsertionPoints(rootNode, 0); ResolveCollisions(); InsertDeconstructionVariableDeclarations(); InsertVariableDeclarations(context); UpdateAnnotations(rootNode); } finally { this.context = null; variableDict.Clear(); } } /// /// Analyze the input AST (containing undeclared variables) /// for where those variables would be declared by this transform. /// Analysis does not modify the AST. /// public void Analyze(AstNode rootNode) { variableDict.Clear(); FindInsertionPoints(rootNode, 0); ResolveCollisions(); } /// /// Get the position where the declaration for the variable will be inserted. /// public AstNode GetDeclarationPoint(ILVariable variable) { VariableToDeclare v = variableDict[variable]; while (v.ReplacementDueToCollision != null) { v = v.ReplacementDueToCollision; } return v.InsertionPoint.nextNode; } /// /// Determines whether a variable was merged with other variables. /// public bool WasMerged(ILVariable variable) { VariableToDeclare v = variableDict[variable]; return v.InvolvedInCollision || v.RemovedDueToCollision; } public void ClearAnalysisResults() { variableDict.Clear(); } #region EnsureExpressionStatementsAreValid void EnsureExpressionStatementsAreValid(AstNode rootNode) { foreach (var stmt in rootNode.DescendantsAndSelf.OfType()) { if (stmt.Expression is DirectionExpression dir && IsValidInStatementExpression(dir.Expression)) { stmt.Expression = dir.Expression.Detach(); } else if (!IsValidInStatementExpression(stmt.Expression)) { // fetch ILFunction var function = stmt.Ancestors.SelectMany(a => a.Annotations.OfType()).First(f => f.Parent == null); // if possible use C# 7.0 discard-assignment if (context.Settings.Discards && !ExpressionBuilder.HidesVariableWithName(function, "_")) { stmt.Expression = new AssignmentExpression( new IdentifierExpression("_"), // no ResolveResult stmt.Expression.Detach()); } else { // assign result to dummy variable var type = stmt.Expression.GetResolveResult().Type; var v = function.RegisterVariable( VariableKind.StackSlot, type, AssignVariableNames.GenerateVariableName(function, type, context.DecompileRun.UsingScope, stmt.Expression.Annotations.OfType() .Where(AssignVariableNames.IsSupportedInstruction).FirstOrDefault(), mustResolveConflicts: true) ); stmt.Expression = new AssignmentExpression( new IdentifierExpression(v.Name).WithRR(new ILVariableResolveResult(v, v.Type)), stmt.Expression.Detach()); } } } } private static bool IsValidInStatementExpression(Expression expr) { switch (expr) { case InvocationExpression _: case ObjectCreateExpression _: case AssignmentExpression _: case ErrorExpression _: return true; case UnaryOperatorExpression uoe: switch (uoe.Operator) { case UnaryOperatorType.PostIncrement: case UnaryOperatorType.PostDecrement: case UnaryOperatorType.Increment: case UnaryOperatorType.Decrement: case UnaryOperatorType.Await: return true; case UnaryOperatorType.NullConditionalRewrap: return IsValidInStatementExpression(uoe.Expression); default: return false; } default: return false; } } #endregion #region FindInsertionPoints List<(InsertionPoint InsertionPoint, BlockContainer Scope)> scopeTracking = new List<(InsertionPoint, BlockContainer)>(); /// /// Finds insertion points for all variables used within `node` /// and adds them to the variableDict. /// /// `level` == nesting depth of `node` within root node. /// /// /// Insertion point for a variable = common parent of all uses of that variable /// = smallest possible scope that contains all the uses of the variable /// void FindInsertionPoints(AstNode node, int nodeLevel) { BlockContainer scope = node.Annotation(); if (scope != null && IsRelevantScope(scope)) { // track loops and function bodies as scopes, for comparison with CaptureScope. scopeTracking.Add((new InsertionPoint { level = nodeLevel, nextNode = node }, scope)); } else if (node is LambdaExpression { Body: Expression expr }) { // expression-bodied lambdas don't have a BlockStatement linking to the BlockContainer scope = node.Annotation()?.Body as BlockContainer; if (scope != null) { scopeTracking.Add((new InsertionPoint { level = nodeLevel + 1, nextNode = expr }, scope)); } } else { scope = null; // don't remove a scope if we didn't add one } try { for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { FindInsertionPoints(child, nodeLevel + 1); } if (node is IdentifierExpression identExpr) { var rr = identExpr.GetResolveResult() as ILVariableResolveResult; if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) { FindInsertionPointForVariable(rr.Variable); } else if (identExpr.Annotation() is ILFunction localFunction && localFunction.Kind == ILFunctionKind.LocalFunction) { foreach (var v in localFunction.CapturedVariables) { if (VariableNeedsDeclaration(v.Kind)) FindInsertionPointForVariable(v); } } void FindInsertionPointForVariable(ILVariable variable) { InsertionPoint newPoint; int startIndex = scopeTracking.Count - 1; BlockContainer captureScope = variable.CaptureScope; while (captureScope != null && !IsRelevantScope(captureScope)) { captureScope = BlockContainer.FindClosestContainer(captureScope.Parent); } if (captureScope != null && startIndex > 0 && captureScope != scopeTracking[startIndex].Scope) { while (startIndex > 0 && scopeTracking[startIndex].Scope != captureScope) startIndex--; newPoint = scopeTracking[startIndex + 1].InsertionPoint; } else { newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr }; if (variable.UsesInitialValue) { // Uninitialized variables are logically initialized at the beginning of the function // Because it's possible that the variable has a loop-carried dependency, // declare it outside of any loops. while (startIndex >= 0) { if (scopeTracking[startIndex].Scope.EntryPoint.IncomingEdgeCount > 1) { // declare variable outside of loop newPoint = scopeTracking[startIndex].InsertionPoint; } else if (scopeTracking[startIndex].Scope.Parent is ILFunction) { // stop at beginning of function break; } startIndex--; } } } if (variableDict.TryGetValue(variable, out VariableToDeclare v)) { v.InsertionPoint = FindCommonParent(v.InsertionPoint, newPoint); } else { v = new VariableToDeclare(variable, newPoint, identExpr, sourceOrder: variableDict.Count); variableDict.Add(variable, v); } } } } finally { if (scope != null) scopeTracking.RemoveAt(scopeTracking.Count - 1); } } private static bool IsRelevantScope(BlockContainer scope) { return scope.EntryPoint.IncomingEdgeCount > 1 || scope.Parent is ILFunction; } internal static bool VariableNeedsDeclaration(VariableKind kind) { switch (kind) { case VariableKind.PinnedRegionLocal: case VariableKind.Parameter: case VariableKind.ExceptionLocal: case VariableKind.ExceptionStackSlot: case VariableKind.UsingLocal: case VariableKind.ForeachLocal: case VariableKind.PatternLocal: return false; default: return true; } } /// /// Finds an insertion point in a common parent instruction. /// InsertionPoint FindCommonParent(InsertionPoint oldPoint, InsertionPoint newPoint) { // First ensure we're looking at nodes on the same level: oldPoint = oldPoint.UpTo(newPoint.level); newPoint = newPoint.UpTo(oldPoint.level); Debug.Assert(newPoint.level == oldPoint.level); // Then go up the tree until both points share the same parent: while (oldPoint.nextNode.Parent != newPoint.nextNode.Parent) { oldPoint = oldPoint.Up(); newPoint = newPoint.Up(); } // return oldPoint as that one comes first in the source code return oldPoint; } #endregion /// /// Some variable declarations in C# are illegal (colliding), /// even though the variable live ranges are not overlapping. /// /// Multiple declarations in same block: /// /// int i = 1; use(1); /// int i = 2; use(2); /// /// /// "Hiding" declaration in nested block: /// /// int i = 1; use(1); /// if (...) { /// int i = 2; use(2); /// } /// /// /// Nested blocks are illegal even if the parent block /// declares the variable later: /// /// if (...) { /// int i = 1; use(i); /// } /// int i = 2; use(i); /// /// /// ResolveCollisions() detects all these cases, and combines the variable declarations /// to a single declaration that is usable for the combined scopes. /// void ResolveCollisions() { var multiDict = new MultiDictionary(); foreach (var v in variableDict.Values) { // We can only insert variable declarations in blocks, but FindInsertionPoints() didn't // guarantee that it finds only blocks. // Fix that up now. while (!(v.InsertionPoint.nextNode.Parent is BlockStatement or LambdaExpression)) { if (v.InsertionPoint.nextNode.Parent is ForStatement f && v.InsertionPoint.nextNode == f.Initializers.FirstOrDefault() && IsMatchingAssignment(v, out _)) { // Special case: the initializer of a ForStatement can also declare a variable (with scope local to the for loop). break; } v.InsertionPoint = v.InsertionPoint.Up(); } // Note: 'out var', pattern matching etc. is not considered a valid insertion point here, because the scope of the // resulting variable is not restricted to the parent node of the insertion point, but extends to the whole BlockStatement. // We moved up the insertion point to the whole BlockStatement so that we can resolve collisions, // later we might decide to declare the variable more locally (as 'out var') instead if still possible. // Go through all potentially colliding variables: foreach (var prev in multiDict[v.Name]) { if (prev.RemovedDueToCollision) continue; // Go up until both nodes are on the same level: InsertionPoint point1 = prev.InsertionPoint.UpTo(v.InsertionPoint.level); InsertionPoint point2 = v.InsertionPoint.UpTo(prev.InsertionPoint.level); Debug.Assert(point1.level == point2.level); if (point1.nextNode.Parent == point2.nextNode.Parent) { Debug.Assert(prev.Type.Equals(v.Type)); // We found a collision! v.InvolvedInCollision = true; prev.ReplacementDueToCollision = v; // Continue checking other entries in multiDict against the new position of `v`. if (prev.SourceOrder < v.SourceOrder) { // Switch v's insertion point to prev's insertion point: v.InsertionPoint = point1; // Since prev was first, it has the correct SourceOrder/FirstUse values // for the new combined variable: v.SourceOrder = prev.SourceOrder; v.FirstUse = prev.FirstUse; } else { // v is first in source order, so it keeps its old insertion point // (and other properties), except that the insertion point is // moved up to prev's level. v.InsertionPoint = point2; } v.DefaultInitialization |= prev.DefaultInitialization; // I think we don't need to re-check the dict entries that we already checked earlier, // because the new v.InsertionPoint only collides with another point x if either // the old v.InsertionPoint or the old prev.InsertionPoint already collided with x. } } multiDict.Add(v.Name, v); } } private void InsertDeconstructionVariableDeclarations() { var usedVariables = new HashSet(); foreach (var g in variableDict.Values.GroupBy(v => v.InsertionPoint.nextNode)) { if (!(g.Key is ExpressionStatement { Expression: AssignmentExpression { Left: TupleExpression left, Operator: AssignmentOperatorType.Assign } assignment })) continue; usedVariables.Clear(); var deconstruct = assignment.Annotation(); if (deconstruct == null || deconstruct.Init.Count > 0 || deconstruct.Conversions.Instructions.Count > 0) continue; if (!deconstruct.Assignments.Instructions.All(IsDeclarableVariable)) continue; var designation = StatementBuilder.TranslateDeconstructionDesignation(deconstruct, isForeach: false); left.ReplaceWith(new DeclarationExpression { Type = new SimpleType("var"), Designation = designation }); foreach (var v in usedVariables) { variableDict[v].DeclaredInDeconstruction = true; } bool IsDeclarableVariable(ILInstruction inst) { if (!inst.MatchStLoc(out var v, out var value)) return false; if (!g.Any(vd => vd.ILVariable == v && !vd.RemovedDueToCollision)) return false; if (!usedVariables.Add(v)) return false; var expectedType = ((LdLoc)value).Variable.Type; if (!v.Type.Equals(expectedType)) return false; if (!(v.Kind == VariableKind.StackSlot || v.Kind == VariableKind.Local)) return false; return true; } } } bool IsMatchingAssignment(VariableToDeclare v, out AssignmentExpression assignment) { assignment = v.InsertionPoint.nextNode as AssignmentExpression; if (assignment == null) { assignment = (v.InsertionPoint.nextNode as ExpressionStatement)?.Expression as AssignmentExpression; if (assignment == null) return false; } return assignment.Operator == AssignmentOperatorType.Assign && assignment.Left is IdentifierExpression identExpr && identExpr.Identifier == v.Name && identExpr.TypeArguments.Count == 0; } bool CombineDeclarationAndInitializer(VariableToDeclare v, TransformContext context) { if (v.Type.IsByRefLike) return true; // by-ref-like variables always must be initialized at their declaration. if (v.InsertionPoint.nextNode.Role == ForStatement.InitializerRole) return true; // for-statement initializers always should combine declaration and initialization. return !context.Settings.SeparateLocalVariableDeclarations; } void InsertVariableDeclarations(TransformContext context) { var replacements = new List<(AstNode, AstNode)>(); foreach (var (ilVariable, v) in variableDict) { if (v.RemovedDueToCollision || v.DeclaredInDeconstruction) continue; if (CombineDeclarationAndInitializer(v, context) && IsMatchingAssignment(v, out AssignmentExpression assignment)) { // 'int v; v = expr;' can be combined to 'int v = expr;' AstType type; if (context.Settings.AnonymousTypes && v.Type.ContainsAnonymousType()) { type = new SimpleType("var"); } else { type = context.TypeSystemAstBuilder.ConvertType(v.Type); } if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) { composedType.HasReadOnlySpecifier = true; } if (v.ILVariable.Kind == VariableKind.PinnedLocal) { type.InsertChildAfter(null, new Comment("pinned", CommentType.MultiLine), Roles.Comment); } var vds = new VariableDeclarationStatement(type, v.Name, assignment.Right.Detach()); var init = vds.Variables.Single(); init.AddAnnotation(assignment.Left.GetResolveResult()); foreach (object annotation in assignment.Left.Annotations.Concat(assignment.Annotations)) { if (!(annotation is ResolveResult)) { init.AddAnnotation(annotation); } } replacements.Add((v.InsertionPoint.nextNode, vds)); } else if (CanBeDeclaredAsOutVariable(v, out var dirExpr)) { // 'T v; SomeCall(out v);' can be combined to 'SomeCall(out T v);' AstType type; bool isOutVar = false; if (context.Settings.AnonymousTypes && v.Type.ContainsAnonymousType()) { type = new SimpleType("var"); isOutVar = true; } else if (dirExpr.Annotation() != null) { type = new SimpleType("var"); isOutVar = true; } else { type = context.TypeSystemAstBuilder.ConvertType(v.Type); } string name; // Variable is not used and discards are allowed, we can simplify this to 'out T _'. // TODO: if no variable named _ is declared and var is used instead of T, use out _. // Note: ExpressionBuilder.HidesVariableWithName produces inaccurate results, because it // does not take lambdas and local functions into account, that are defined in the same // scope as v. if (context.Settings.Discards && v.ILVariable.LoadCount == 0 && v.ILVariable.StoreCount == 0 && v.ILVariable.AddressCount == 1) { name = "_"; } else { name = v.Name; } var ovd = new OutVarDeclarationExpression(type, name); ovd.Variable.AddAnnotation(new ILVariableResolveResult(ilVariable)); ovd.CopyAnnotationsFrom(dirExpr); if (isOutVar) { ovd.RemoveAnnotations(); ovd.AddAnnotation(new OutVarResolveResult(v.Type)); } replacements.Add((dirExpr, ovd)); } else { // Insert a separate declaration statement. Expression initializer = null; AstType type = context.TypeSystemAstBuilder.ConvertType(v.Type); if (v.DefaultInitialization == VariableInitKind.NeedsDefaultValue) { initializer = new DefaultValueExpression(type.Clone()); } var vds = new VariableDeclarationStatement(type, v.Name, initializer); vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(ilVariable)); if (v.InsertionPoint.nextNode.Parent is LambdaExpression lambda) { Debug.Assert(lambda.Body is not BlockStatement); lambda.Body = new BlockStatement() { new ReturnStatement((Expression)lambda.Body.Detach()) }; } if (v.InsertionPoint.nextNode.Parent is ReturnStatement) { v.InsertionPoint = v.InsertionPoint.Up(); } Debug.Assert(v.InsertionPoint.nextNode.Role == BlockStatement.StatementRole); if (v.DefaultInitialization == VariableInitKind.NeedsSkipInit) { AstType unsafeType = context.TypeSystemAstBuilder.ConvertType( context.TypeSystem.FindType(KnownTypeCode.Unsafe)); if (context.Settings.OutVariables) { var outVarDecl = new OutVarDeclarationExpression(type.Clone(), v.Name); outVarDecl.Variable.AddAnnotation(new ILVariableResolveResult(ilVariable)); v.InsertionPoint.nextNode.Parent.InsertChildBefore( v.InsertionPoint.nextNode, new ExpressionStatement { Expression = new InvocationExpression { Target = new MemberReferenceExpression { Target = new TypeReferenceExpression(unsafeType), MemberName = "SkipInit" }, Arguments = { outVarDecl } } }, BlockStatement.StatementRole); } else { v.InsertionPoint.nextNode.Parent.InsertChildBefore( v.InsertionPoint.nextNode, vds, BlockStatement.StatementRole); v.InsertionPoint.nextNode.Parent.InsertChildBefore( v.InsertionPoint.nextNode, new ExpressionStatement { Expression = new InvocationExpression { Target = new MemberReferenceExpression { Target = new TypeReferenceExpression(unsafeType), MemberName = "SkipInit" }, Arguments = { new DirectionExpression( FieldDirection.Out, new IdentifierExpression(v.Name) .WithRR(new ILVariableResolveResult(ilVariable)) ) } } }, BlockStatement.StatementRole); } } else { v.InsertionPoint.nextNode.Parent.InsertChildBefore( v.InsertionPoint.nextNode, vds, BlockStatement.StatementRole); } } } // perform replacements at end, so that we don't replace a node while it is still referenced by a VariableToDeclare foreach (var (oldNode, newNode) in replacements) { oldNode.ReplaceWith(newNode); } } private bool CanBeDeclaredAsOutVariable(VariableToDeclare v, out DirectionExpression dirExpr) { dirExpr = v.FirstUse.Parent as DirectionExpression; if (dirExpr == null || dirExpr.FieldDirection != FieldDirection.Out) return false; if (!context.Settings.OutVariables) return false; if (v.DefaultInitialization != VariableInitKind.None) return false; for (AstNode node = v.FirstUse; node != null; node = node.Parent) { if (node.Role == Roles.EmbeddedStatement) { return false; } switch (node) { case IfElseStatement: // variable declared in if condition appears in parent scope case ExpressionStatement: return node == v.InsertionPoint.nextNode; case Statement: return false; // other statements (e.g. while) don't allow variables to be promoted to parent scope case LambdaExpression lambda: return lambda.Body == v.InsertionPoint.nextNode; } } return false; } /// /// Update ILVariableResolveResult annotations of all ILVariables that have been replaced by ResolveCollisions. /// void UpdateAnnotations(AstNode rootNode) { foreach (var node in rootNode.Descendants) { ILVariable ilVar; switch (node) { case IdentifierExpression id: ilVar = id.GetILVariable(); break; case VariableInitializer vi: ilVar = vi.GetILVariable(); break; default: continue; } if (ilVar == null || !VariableNeedsDeclaration(ilVar.Kind)) continue; var v = variableDict[ilVar]; if (!v.RemovedDueToCollision) continue; while (v.RemovedDueToCollision) { v = v.ReplacementDueToCollision; } node.RemoveAnnotations(); node.AddAnnotation(new ILVariableResolveResult(v.ILVariable, v.Type)); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Escape invalid identifiers. /// /// /// This transform is not enabled by default. /// public class EscapeInvalidIdentifiers : IAstTransform { bool IsValid(char ch) { if (char.IsLetterOrDigit(ch)) return true; if (ch == '_') return true; return false; } string ReplaceInvalid(string s) { string name = string.Concat(s.Select(ch => IsValid(ch) ? ch.ToString() : string.Format("_{0:X4}", (int)ch))); if (name.Length >= 1 && !(char.IsLetter(name[0]) || name[0] == '_')) name = "_" + name; return name; } public void Run(AstNode rootNode, TransformContext context) { foreach (var ident in rootNode.DescendantsAndSelf.OfType()) { ident.Name = ReplaceInvalid(ident.Name); } } } /// /// This transform is used to remove assembly-attributes that are generated by the compiler, /// thus don't need to be declared. (We have to remove them, in order to avoid conflicts while compiling.) /// /// This transform is only enabled, when exporting a full assembly as project. public class RemoveCompilerGeneratedAssemblyAttributes : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { foreach (var section in rootNode.Children.OfType()) { if (section.AttributeTarget == "assembly") { foreach (var attribute in section.Attributes) { var trr = attribute.Type.Annotation(); if (trr == null) continue; string fullName = trr.Type.FullName; var arguments = attribute.Arguments; switch (fullName) { case "System.Diagnostics.DebuggableAttribute": { attribute.Remove(); break; } case "System.Runtime.CompilerServices.CompilationRelaxationsAttribute": { if (arguments.Count == 1 && arguments.First() is PrimitiveExpression expr && expr.Value is int value && value == 8) attribute.Remove(); break; } case "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute": { if (arguments.Count != 1) break; if (!(arguments.First() is NamedExpression expr1) || expr1.Name != "WrapNonExceptionThrows") break; if (!(expr1.Expression is PrimitiveExpression expr2) || !(expr2.Value is bool value) || value != true) break; attribute.Remove(); break; } case "System.Runtime.Versioning.TargetFrameworkAttribute": { attribute.Remove(); break; } case "System.Security.Permissions.SecurityPermissionAttribute": { if (arguments.Count != 2) break; if (!(arguments.First() is MemberReferenceExpression expr1) || expr1.MemberName != "RequestMinimum") break; if (!(expr1.NextSibling is NamedExpression expr2) || expr2.Name != "SkipVerification") break; if (!(expr2.Expression is PrimitiveExpression expr3) || !(expr3.Value is bool value2) || value2 != true) break; attribute.Remove(); break; } } } } else if (section.AttributeTarget == "module") { foreach (var attribute in section.Attributes) { var trr = attribute.Type.Annotation(); if (trr == null) continue; switch (trr.Type.FullName) { case "System.Security.UnverifiableCodeAttribute": case "System.Runtime.CompilerServices.RefSafetyRulesAttribute": attribute.Remove(); break; } } } else { continue; } if (section.Attributes.Count == 0) { section.Remove(); } } } } /// /// This transform is used to remove attributes that are embedded /// public class RemoveEmbeddedAttributes : DepthFirstAstVisitor, IAstTransform { internal static readonly HashSet attributeNames = new HashSet() { "System.Runtime.CompilerServices.IsReadOnlyAttribute", "System.Runtime.CompilerServices.IsByRefLikeAttribute", "System.Runtime.CompilerServices.IsUnmanagedAttribute", "System.Runtime.CompilerServices.NullableAttribute", "System.Runtime.CompilerServices.NullableContextAttribute", "System.Runtime.CompilerServices.NativeIntegerAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute", "System.Runtime.CompilerServices.RefSafetyRulesAttribute", "System.Runtime.CompilerServices.ScopedRefAttribute", "System.Runtime.CompilerServices.RequiresLocationAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute", }; internal static readonly HashSet nonEmbeddedAttributeNames = new HashSet() { // non-embedded attributes, but we still want to remove them "System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", "System.Runtime.CompilerServices.RequiredMemberAttribute", "System.Runtime.CompilerServices.IsExternalInit", }; public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { var typeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition; if (typeDefinition == null) return; if (attributeNames.Contains(typeDefinition.FullName)) { if (!typeDefinition.HasAttribute(KnownAttribute.Embedded)) return; } else if (!nonEmbeddedAttributeNames.Contains(typeDefinition.FullName)) return; if (typeDeclaration.Parent is NamespaceDeclaration ns && ns.Members.Count == 1) ns.Remove(); else typeDeclaration.Remove(); } public void Run(AstNode rootNode, TransformContext context) { rootNode.AcceptVisitor(this); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Rename entities to solve name collisions that make the code uncompilable. /// /// /// Currently, we only rename private fields that collide with property or event names. /// This helps especially with compiler-generated events that were not detected as a pattern. /// public class FixNameCollisions : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { var renamedSymbols = new Dictionary(); foreach (var typeDecl in rootNode.DescendantsAndSelf.OfType()) { var memberNames = typeDecl.Members.Select(m => { var type = m.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); return type.IsNull ? m.Name : type + "." + m.Name; }).ToHashSet(); // memberNames does not include fields or non-custom events because those // don't have a single name, but a list of VariableInitializers. foreach (var fieldDecl in typeDecl.Members.OfType()) { if (fieldDecl.Variables.Count != 1) continue; string oldName = fieldDecl.Variables.Single().Name; ISymbol symbol = fieldDecl.GetSymbol(); if (memberNames.Contains(oldName) && ((IField)symbol).Accessibility == Accessibility.Private) { string newName = PickNewName(memberNames, oldName); if (symbol != null) { fieldDecl.Variables.Single().Name = newName; renamedSymbols[symbol] = newName; } } } } foreach (var node in rootNode.DescendantsAndSelf) { if (node is IdentifierExpression || node is MemberReferenceExpression) { ISymbol symbol = node.GetSymbol(); if (symbol != null && renamedSymbols.TryGetValue(symbol, out string newName)) { node.GetChildByRole(Roles.Identifier).Name = newName; } } } } string PickNewName(ISet memberNames, string name) { if (!memberNames.Contains("m_" + name)) return "m_" + name; for (int num = 2; ; num++) { string newName = name + num; if (!memberNames.Contains(newName)) return newName; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.Transforms { class FlattenSwitchBlocks : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { foreach (var switchSection in rootNode.Descendants.OfType()) { if (switchSection.Statements.Count != 1) continue; var blockStatement = switchSection.Statements.First() as BlockStatement; if (blockStatement == null || blockStatement.Statements.Any(ContainsLocalDeclaration)) continue; blockStatement.Remove(); blockStatement.Statements.MoveTo(switchSection.Statements); } bool ContainsLocalDeclaration(AstNode node) { if (node is VariableDeclarationStatement || node is LocalFunctionDeclarationStatement || node is OutVarDeclarationExpression) return true; if (node is BlockStatement) return false; foreach (var child in node.Children) { if (ContainsLocalDeclaration(child)) return true; } return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/IAstTransform.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.Transforms { public interface IAstTransform { void Run(AstNode rootNode, TransformContext context); } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs ================================================ // 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 // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Converts extension method calls into infix syntax. /// public class IntroduceExtensionMethods : DepthFirstAstVisitor, IAstTransform { TransformContext context; CSharpResolver resolver; CSharpConversions conversions; public void Run(AstNode rootNode, TransformContext context) { this.context = context; this.conversions = CSharpConversions.Get(context.TypeSystem); InitializeContext(rootNode.Annotation()); rootNode.AcceptVisitor(this); } void InitializeContext(UsingScope usingScope) { if (!string.IsNullOrEmpty(context.CurrentTypeDefinition?.Namespace)) { foreach (string ns in context.CurrentTypeDefinition.Namespace.Split('.')) { usingScope = usingScope.WithNestedNamespace(ns); } } var currentContext = new CSharpTypeResolveContext(context.TypeSystem.MainModule, usingScope, context.CurrentTypeDefinition); this.resolver = new CSharpResolver(currentContext); } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { var usingScope = resolver.CurrentUsingScope; foreach (string ident in namespaceDeclaration.Identifiers) { usingScope = usingScope.WithNestedNamespace(ident); } var previousResolver = this.resolver; try { this.resolver = this.resolver.WithCurrentUsingScope(usingScope); base.VisitNamespaceDeclaration(namespaceDeclaration); } finally { this.resolver = previousResolver; } } public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { var previousResolver = this.resolver; this.resolver = resolver.WithCurrentTypeDefinition(typeDeclaration.GetSymbol() as ITypeDefinition); try { base.VisitTypeDeclaration(typeDeclaration); } finally { this.resolver = previousResolver; } } public override void VisitInvocationExpression(InvocationExpression invocationExpression) { base.VisitInvocationExpression(invocationExpression); if (!CanTransformToExtensionMethodCall(resolver, invocationExpression, out var memberRefExpr, out var target, out var firstArgument)) { return; } var method = (IMethod)invocationExpression.GetSymbol(); if (firstArgument is DirectionExpression dirExpr) { if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) return; firstArgument = dirExpr.Expression; target = firstArgument.GetResolveResult(); dirExpr.Detach(); } else if (firstArgument is NullReferenceExpression) { Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace)); firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); } if (invocationExpression.Target is IdentifierExpression identifierExpression) { identifierExpression.Detach(); memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach()); invocationExpression.Target = memberRefExpr; } else { memberRefExpr.Target = firstArgument.Detach(); } if (invocationExpression.GetResolveResult() is CSharpInvocationResolveResult irr) { // do not forget to update the CSharpInvocationResolveResult => set IsExtensionMethodInvocation == true invocationExpression.RemoveAnnotations(); var newResolveResult = new CSharpInvocationResolveResult( irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors, isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation, irr.GetArgumentToParameterMap(), irr.InitializerStatements); invocationExpression.AddAnnotation(newResolveResult); } } static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, InvocationExpression invocationExpression, out MemberReferenceExpression memberRefExpr, out ResolveResult target, out Expression firstArgument) { var method = invocationExpression.GetSymbol() as IMethod; memberRefExpr = null; target = null; firstArgument = null; if (method == null || !method.IsExtensionMethod || !invocationExpression.Arguments.Any()) return false; IReadOnlyList typeArguments; switch (invocationExpression.Target) { case MemberReferenceExpression mre: typeArguments = mre.TypeArguments.Any() ? method.TypeArguments : EmptyList.Instance; memberRefExpr = mre; break; case IdentifierExpression ide: typeArguments = ide.TypeArguments.Any() ? method.TypeArguments : EmptyList.Instance; memberRefExpr = null; break; default: return false; } firstArgument = invocationExpression.Arguments.First(); if (firstArgument is NamedArgumentExpression) return false; target = firstArgument.GetResolveResult(); if (target is ConstantResolveResult crr && crr.ConstantValue == null) { target = new ConversionResolveResult(method.Parameters[0].Type, crr, Conversion.NullLiteralConversion); } else if (firstArgument is DirectionExpression de) { target = de.Expression.GetResolveResult(); } Debug.Assert(target != null); ResolveResult[] args = new ResolveResult[invocationExpression.Arguments.Count - 1]; string[] argNames = null; int pos = 0; foreach (var arg in invocationExpression.Arguments.Skip(1)) { if (arg is NamedArgumentExpression nae) { if (argNames == null) { argNames = new string[args.Length]; } argNames[pos] = nae.Name; args[pos] = nae.Expression.GetResolveResult(); } else { args[pos] = arg.GetResolveResult(); } pos++; } return resolver.CanTransformToExtensionMethodCall(method, typeArguments, target, args, argNames); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs ================================================ // 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 // 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.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Decompiles query expressions. /// Based on C# 4.0 spec, §7.16.2 Query expression translation /// public class IntroduceQueryExpressions : IAstTransform { TransformContext context; public void Run(AstNode rootNode, TransformContext context) { if (!context.Settings.QueryExpressions) return; this.context = context; DecompileQueries(rootNode); // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group') // and fix them, either by adding a degenerate select, or by combining them with another query. foreach (QueryExpression query in rootNode.Descendants.OfType()) { QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); if (IsDegenerateQuery(query)) { // introduce select for degenerate query query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier).CopyAnnotationsFrom(fromClause) }); } // See if the data source of this query is a degenerate query, // and combine the queries if possible. QueryExpression innerQuery = fromClause.Expression as QueryExpression; while (IsDegenerateQuery(innerQuery)) { QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First(); ILVariable innerVariable = innerFromClause.Annotation()?.Variable; ILVariable rangeVariable = fromClause.Annotation()?.Variable; // Replace the fromClause with all clauses from the inner query fromClause.Remove(); QueryClause insertionPos = null; foreach (var clause in innerQuery.Clauses) { CombineRangeVariables(clause, innerVariable, rangeVariable); query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); } fromClause = innerFromClause; innerQuery = fromClause.Expression as QueryExpression; } } } private void CombineRangeVariables(QueryClause clause, ILVariable oldVariable, ILVariable newVariable) { foreach (var identifier in clause.DescendantNodes().OfType()) { var variable = identifier.Parent.Annotation()?.Variable; if (variable == oldVariable) { identifier.Parent.RemoveAnnotations(); identifier.Parent.AddAnnotation(new ILVariableResolveResult(newVariable)); identifier.ReplaceWith(Identifier.Create(newVariable.Name)); } } } bool IsDegenerateQuery(QueryExpression query) { if (query == null) return false; var lastClause = query.Clauses.LastOrDefault(); return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause); } void DecompileQueries(AstNode node) { Expression query = DecompileQuery(node as InvocationExpression); if (query != null) { if (node.Parent is ExpressionStatement && CanUseDiscardAssignment()) query = new AssignmentExpression(new IdentifierExpression("_"), query); node.ReplaceWith(query); } AstNode next; for (AstNode child = (query ?? node).FirstChild; child != null; child = next) { // store reference to next child before transformation next = child.NextSibling; DecompileQueries(child); } } bool CanUseDiscardAssignment() { // TODO : check whether there exists a variable named '_' in scope. return context.Settings.Discards; } QueryExpression DecompileQuery(InvocationExpression invocation) { if (invocation == null) return null; MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; if (mre == null || IsNullConditional(mre.Target)) return null; switch (mre.MemberName) { case "Select": { if (invocation.Arguments.Count != 1) return null; if (!IsComplexQuery(mre)) return null; Expression expr = invocation.Arguments.Single(); if (MatchSimpleLambda(expr, out ParameterDeclaration parameter, out Expression body)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QuerySelectClause { Expression = WrapExpressionInParenthesesIfNecessary(body.Detach(), parameter.Name) }.CopyAnnotationsFrom(expr)); return query; } return null; } case "GroupBy": { if (invocation.Arguments.Count == 2) { Expression keyLambda = invocation.Arguments.ElementAt(0); Expression projectionLambda = invocation.Arguments.ElementAt(1); if (MatchSimpleLambda(keyLambda, out ParameterDeclaration parameter1, out Expression keySelector) && MatchSimpleLambda(projectionLambda, out ParameterDeclaration parameter2, out Expression elementSelector) && parameter1.Name == parameter2.Name) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter1, mre.Target.Detach())); var queryGroupClause = new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }; queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation(), projectionLambda.Annotation())); query.Clauses.Add(queryGroupClause); return query; } } else if (invocation.Arguments.Count == 1) { Expression lambda = invocation.Arguments.Single(); if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression keySelector)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameter.Name).CopyAnnotationsFrom(parameter), Key = keySelector.Detach() }); return query; } } return null; } case "SelectMany": { if (invocation.Arguments.Count != 2) return null; var fromExpressionLambda = invocation.Arguments.ElementAt(0); if (!MatchSimpleLambda(fromExpressionLambda, out ParameterDeclaration parameter, out Expression collectionSelector)) return null; if (IsNullConditional(collectionSelector)) return null; LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression; if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); if (p1.Name == parameter.Name) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(p1, mre.Target.Detach())); query.Clauses.Add(MakeFromClause(p2, collectionSelector.Detach()).CopyAnnotationsFrom(fromExpressionLambda)); query.Clauses.Add(new QuerySelectClause { Expression = WrapExpressionInParenthesesIfNecessary(((Expression)lambda.Body).Detach(), parameter.Name) }); return query; } } return null; } case "Where": { if (invocation.Arguments.Count != 1) return null; if (!IsComplexQuery(mre)) return null; Expression expr = invocation.Arguments.Single(); if (MatchSimpleLambda(expr, out ParameterDeclaration parameter, out Expression body)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() }.CopyAnnotationsFrom(expr)); return query; } return null; } case "OrderBy": case "OrderByDescending": case "ThenBy": case "ThenByDescending": { if (invocation.Arguments.Count != 1) return null; if (!IsComplexQuery(mre)) return null; var lambda = invocation.Arguments.Single(); if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression orderExpression)) { if (ValidateThenByChain(invocation, parameter.Name)) { QueryOrderClause orderClause = new QueryOrderClause(); while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { // insert new ordering at beginning orderClause.Orderings.InsertAfter( null, new QueryOrdering { Expression = orderExpression.Detach(), Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) }.CopyAnnotationsFrom(lambda)); InvocationExpression tmp = (InvocationExpression)mre.Target; mre = (MemberReferenceExpression)tmp.Target; lambda = tmp.Arguments.Single(); MatchSimpleLambda(lambda, out parameter, out orderExpression); } // insert new ordering at beginning orderClause.Orderings.InsertAfter( null, new QueryOrdering { Expression = orderExpression.Detach(), Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) }.CopyAnnotationsFrom(lambda)); QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(orderClause); return query; } } return null; } case "Join": case "GroupJoin": { if (invocation.Arguments.Count != 4) return null; Expression source1 = mre.Target; Expression source2 = invocation.Arguments.ElementAt(0); if (IsNullConditional(source2)) return null; Expression outerLambda = invocation.Arguments.ElementAt(1); if (!MatchSimpleLambda(outerLambda, out ParameterDeclaration element1, out Expression key1)) return null; Expression innerLambda = invocation.Arguments.ElementAt(2); if (!MatchSimpleLambda(innerLambda, out ParameterDeclaration element2, out Expression key2)) return null; LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); if (ValidateParameter(p1) && ValidateParameter(p2) && p1.Name == element1.Name && (p2.Name == element2.Name || mre.MemberName == "GroupJoin")) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(element1, source1.Detach())); QueryJoinClause joinClause = new QueryJoinClause(); joinClause.JoinIdentifier = element2.Name; // join elementName2 joinClause.JoinIdentifierToken.CopyAnnotationsFrom(element2); joinClause.InExpression = source2.Detach(); // in source2 joinClause.OnExpression = key1.Detach(); // on key1 joinClause.EqualsExpression = key2.Detach(); // equals key2 if (mre.MemberName == "GroupJoin") { joinClause.IntoIdentifier = p2.Name; // into p2.Name joinClause.IntoIdentifierToken.CopyAnnotationsFrom(p2); } joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation(), innerLambda.Annotation())); query.Clauses.Add(joinClause); query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda)); return query; } } return null; } default: return null; } } static bool IsComplexQuery(MemberReferenceExpression mre) { return ((mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause); } QueryFromClause MakeFromClause(ParameterDeclaration parameter, Expression body) { QueryFromClause fromClause = new QueryFromClause { Identifier = parameter.Name, Expression = body }; fromClause.CopyAnnotationsFrom(parameter); return fromClause; } class ApplyAnnotationVisitor : DepthFirstAstVisitor { private LetIdentifierAnnotation annotation; private string identifier; public ApplyAnnotationVisitor(LetIdentifierAnnotation annotation, string identifier) { this.annotation = annotation; this.identifier = identifier; } public override AstNode VisitIdentifier(Identifier identifier) { if (identifier.Name == this.identifier) identifier.AddAnnotation(annotation); return identifier; } } bool IsNullConditional(Expression target) { return target is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional; } /// /// This fixes #437: Decompilation of query expression loses material parentheses /// We wrap the expression in parentheses if: /// - the Select-call is explicit (see caller(s)) /// - the expression is a plain identifier matching the parameter name /// Expression WrapExpressionInParenthesesIfNecessary(Expression expression, string parameterName) { if (expression is IdentifierExpression ident && parameterName.Equals(ident.Identifier, StringComparison.Ordinal)) return new ParenthesizedExpression(expression); return expression; } /// /// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation. /// bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName) { if (invocation == null || invocation.Arguments.Count != 1) return false; if (!(invocation.Target is MemberReferenceExpression mre)) return false; if (!MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out _)) return false; if (parameter.Name != expectedParameterName) return false; if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending") return !IsNullConditional(mre.Target); else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName); else return false; } /// Matches simple lambdas of the form "a => b" bool MatchSimpleLambda(Expression expr, out ParameterDeclaration parameter, out Expression body) { if (expr is LambdaExpression lambda && lambda.Parameters.Count == 1 && lambda.Body is Expression) { ParameterDeclaration p = lambda.Parameters.Single(); if (ValidateParameter(p)) { parameter = p; body = (Expression)lambda.Body; return true; } } parameter = null; body = null; return false; } private static bool ValidateParameter(ParameterDeclaration p) { return p.ParameterModifier == Decompiler.TypeSystem.ReferenceKind.None && p.Attributes.Count == 0; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs ================================================ // 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 // 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.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { public class IntroduceUnsafeModifier : DepthFirstAstVisitor, IAstTransform { public void Run(AstNode compilationUnit, TransformContext context) { compilationUnit.AcceptVisitor(this); } public static bool IsUnsafe(AstNode node) { return node.AcceptVisitor(new IntroduceUnsafeModifier()); } protected override bool VisitChildren(AstNode node) { bool result = false; AstNode next; for (AstNode child = node.FirstChild; child != null; child = next) { // Store next to allow the loop to continue // if the visitor removes/replaces child. next = child.NextSibling; result |= child.AcceptVisitor(this); } if (result && node is EntityDeclaration && !(node is Accessor)) { ((EntityDeclaration)node).Modifiers |= Modifiers.Unsafe; return false; } return result; } public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { base.VisitPointerReferenceExpression(pointerReferenceExpression); return true; } public override bool VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { // C# sizeof(MyStruct) requires unsafe{} // (not for sizeof(int), but that gets constant-folded and thus decompiled to 4) base.VisitSizeOfExpression(sizeOfExpression); return true; } public override bool VisitComposedType(ComposedType composedType) { if (composedType.PointerRank > 0) return true; else return base.VisitComposedType(composedType); } public override bool VisitFunctionPointerType(FunctionPointerAstType functionPointerType) { return true; } public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression); if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) { var bop = unaryOperatorExpression.Expression as BinaryOperatorExpression; if (bop != null && bop.Operator == BinaryOperatorType.Add && bop.GetResolveResult() is OperatorResolveResult orr && orr.Operands.FirstOrDefault()?.Type.Kind == TypeKind.Pointer) { // transform "*(ptr + int)" to "ptr[int]" IndexerExpression indexer = new IndexerExpression(); indexer.Target = bop.Left.Detach(); indexer.Arguments.Add(bop.Right.Detach()); indexer.CopyAnnotationsFrom(unaryOperatorExpression); indexer.CopyAnnotationsFrom(bop); unaryOperatorExpression.ReplaceWith(indexer); } return true; } else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) { return true; } else { return result; } } public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { bool result = base.VisitMemberReferenceExpression(memberReferenceExpression); 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.RemoveAnnotations(); // only copy the ResolveResult from the MRE pre.CopyAnnotationsFrom(memberReferenceExpression); memberReferenceExpression.ReplaceWith(pre); } if (HasUnsafeResolveResult(memberReferenceExpression)) return true; return result; } public override bool VisitIdentifierExpression(IdentifierExpression identifierExpression) { bool result = base.VisitIdentifierExpression(identifierExpression); if (HasUnsafeResolveResult(identifierExpression)) return true; return result; } public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { bool result = base.VisitStackAllocExpression(stackAllocExpression); if (HasUnsafeResolveResult(stackAllocExpression)) return true; return result; } public override bool VisitInvocationExpression(InvocationExpression invocationExpression) { bool result = base.VisitInvocationExpression(invocationExpression); if (HasUnsafeResolveResult(invocationExpression)) return true; return result; } public override bool VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { base.VisitFixedVariableInitializer(fixedVariableInitializer); return true; } private bool HasUnsafeResolveResult(AstNode node) { var rr = node.GetResolveResult(); if (rr == null) return false; if (IsUnsafeType(rr.Type)) return true; if ((rr as MemberResolveResult)?.Member is IParameterizedMember pm) { if (pm.Parameters.Any(p => IsUnsafeType(p.Type))) return true; } else if (rr is MethodGroupResolveResult) { var chosenMethod = node.GetSymbol(); if (chosenMethod is IParameterizedMember pm2) { if (IsUnsafeType(pm2.ReturnType)) return true; if (pm2.Parameters.Any(p => IsUnsafeType(p.Type))) return true; } } return false; } private bool IsUnsafeType(IType type) { switch (type?.Kind) { case TypeKind.Pointer: case TypeKind.FunctionPointer: return true; case TypeKind.ByReference: case TypeKind.Array: return IsUnsafeType(((Decompiler.TypeSystem.Implementation.TypeWithElementType)type).ElementType); default: return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs ================================================ // 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 // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Introduces using declarations. /// public class IntroduceUsingDeclarations : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { // First determine all the namespaces that need to be imported: var requiredImports = new FindRequiredImports(context); rootNode.AcceptVisitor(requiredImports); List resolvedNamespaces = new List(); if (context.Settings.UsingDeclarations) { var insertionPoint = rootNode.Children.LastOrDefault(n => n is PreProcessorDirective p && p.Type == PreProcessorDirectiveType.Define); // Now add using declarations for those namespaces: IOrderedEnumerable sortedImports = requiredImports.ImportedNamespaces .OrderBy(n => n.StartsWith("System", StringComparison.Ordinal)) .ThenByDescending(n => n); foreach (string ns in sortedImports) { Debug.Assert(context.RequiredNamespacesSuperset.Contains(ns), $"Should not insert using declaration for namespace that is missing from the superset: {ns}"); // we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards // (always inserting at the start of the list) string[] parts = ns.Split('.'); AstType nsType = new SimpleType(parts[0]); for (int i = 1; i < parts.Length; i++) { nsType = new MemberType { Target = nsType, MemberName = parts[i] }; } var resolvedNamespace = context.TypeSystem.GetNamespaceByFullName(ns); if (resolvedNamespace != null) { resolvedNamespaces.Add(resolvedNamespace); } rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole); } } var usingScope = new UsingScope( new CSharpTypeResolveContext(context.TypeSystem.MainModule), context.TypeSystem.RootNamespace, resolvedNamespaces.ToImmutableArray() ); rootNode.AddAnnotation(usingScope); // verify that the SimpleTypes refer to the correct type (no ambiguities) rootNode.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(context, usingScope)); } sealed class FindRequiredImports : DepthFirstAstVisitor { string currentNamespace; public readonly HashSet DeclaredNamespaces = new HashSet() { string.Empty }; public readonly HashSet ImportedNamespaces = new HashSet(); public FindRequiredImports(TransformContext context) { this.currentNamespace = context.CurrentTypeDefinition?.Namespace ?? string.Empty; } bool IsParentOfCurrentNamespace(string ns) { if (ns.Length == 0) return true; if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) { if (currentNamespace.Length == ns.Length) return true; if (currentNamespace[ns.Length] == '.') return true; } return false; } public override void VisitSimpleType(SimpleType simpleType) { var trr = simpleType.Annotation(); AddImportedNamespace(trr?.Type); base.VisitSimpleType(simpleType); // also visit type arguments } private void AddImportedNamespace(IType type) { if (type != null && !IsParentOfCurrentNamespace(type.Namespace)) { ImportedNamespaces.Add(type.Namespace); } } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { string oldNamespace = currentNamespace; foreach (string ident in namespaceDeclaration.Identifiers) { currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident); DeclaredNamespaces.Add(currentNamespace); } base.VisitNamespaceDeclaration(namespaceDeclaration); currentNamespace = oldNamespace; } public override void VisitForeachStatement(ForeachStatement foreachStatement) { var annotation = foreachStatement.Annotation(); if (annotation?.GetEnumeratorCall is CallInstruction { Method: { IsExtensionMethod: true, DeclaringType: var type } }) { AddImportedNamespace(type); } base.VisitForeachStatement(foreachStatement); } public override void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation) { var annotation = parenthesizedVariableDesignation.Annotation(); if (annotation?.Method is IMethod { DeclaringType: var type }) { AddImportedNamespace(type); } base.VisitParenthesizedVariableDesignation(parenthesizedVariableDesignation); } public override void VisitTupleExpression(TupleExpression tupleExpression) { var annotation = tupleExpression.Annotation(); if (annotation?.Method is IMethod { DeclaringType: var type }) { AddImportedNamespace(type); } base.VisitTupleExpression(tupleExpression); } public override void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { foreach (var item in arrayInitializerExpression.Elements) { var optionalCall = item.Annotation(); if (optionalCall?.Method is { IsExtensionMethod: true, Name: "Add" }) { AddImportedNamespace(optionalCall.Method.DeclaringType); } } base.VisitArrayInitializerExpression(arrayInitializerExpression); } } sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor { readonly bool ignoreUsingScope; readonly DecompilerSettings settings; CSharpResolver resolver; TypeSystemAstBuilder astBuilder; bool inPrimaryConstructor; public FullyQualifyAmbiguousTypeNamesVisitor(TransformContext context, UsingScope usingScope) { this.ignoreUsingScope = !context.Settings.UsingDeclarations; this.settings = context.Settings; this.resolver = new CSharpResolver(new CSharpTypeResolveContext(context.TypeSystem.MainModule)); if (!ignoreUsingScope) { if (!string.IsNullOrEmpty(context.CurrentTypeDefinition?.Namespace)) { foreach (string ns in context.CurrentTypeDefinition.Namespace.Split('.')) { usingScope = usingScope.WithNestedNamespace(ns); } } this.resolver = this.resolver.WithCurrentUsingScope(usingScope) .WithCurrentTypeDefinition(context.CurrentTypeDefinition); } this.astBuilder = CreateAstBuilder(resolver); } TypeSystemAstBuilder CreateAstBuilder(CSharpResolver resolver, IL.ILFunction function = null) { if (function != null) { var variables = new Dictionary(); foreach (var v in function.Variables) { if (v.Kind != IL.VariableKind.Parameter && v.Name != null && !variables.ContainsKey(v.Name)) variables.Add(v.Name, new DefaultVariable(v.Type, v.Name)); } resolver = resolver.AddVariables(variables); } return new TypeSystemAstBuilder(resolver) { UseNullableSpecifierForValueTypes = settings.LiftNullables, AlwaysUseGlobal = settings.AlwaysUseGlobal, AddResolveResultAnnotations = true, UseAliases = true }; } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { if (ignoreUsingScope) { base.VisitNamespaceDeclaration(namespaceDeclaration); return; } var previousResolver = resolver; var previousAstBuilder = astBuilder; var usingScope = resolver.CurrentUsingScope; foreach (string ident in namespaceDeclaration.Identifiers) { usingScope = usingScope.WithNestedNamespace(ident); } resolver = resolver.WithCurrentUsingScope(usingScope); try { astBuilder = CreateAstBuilder(resolver); base.VisitNamespaceDeclaration(namespaceDeclaration); } finally { astBuilder = previousAstBuilder; resolver = previousResolver; } } public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { if (ignoreUsingScope) { base.VisitTypeDeclaration(typeDeclaration); return; } if (typeDeclaration.HasPrimaryConstructor) { inPrimaryConstructor = true; try { typeDeclaration.PrimaryConstructorParameters.AcceptVisitor(this); } finally { inPrimaryConstructor = false; } } var previousResolver = resolver; var previousAstBuilder = astBuilder; resolver = resolver.WithCurrentTypeDefinition(typeDeclaration.GetSymbol() as ITypeDefinition); try { astBuilder = CreateAstBuilder(resolver); base.VisitTypeDeclaration(typeDeclaration); } finally { astBuilder = previousAstBuilder; resolver = previousResolver; } } public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { // Parameters of primary constructors are visited separately from the rest of the // type declaration since their types are at the same scope as the type declaration // and so need to use the outer resolver. This check ensures that the visitor only // runs once per parameter since their AstNodes will get revisited by the call to // `base.VisitTypeDeclaration(typeDeclaration)` in `VisitTypeDeclaration` above. if (inPrimaryConstructor || parameterDeclaration.Parent is not TypeDeclaration) base.VisitParameterDeclaration(parameterDeclaration); } public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { Visit(methodDeclaration, base.VisitMethodDeclaration); } public override void VisitAccessor(Accessor accessor) { Visit(accessor, base.VisitAccessor); } public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { Visit(constructorDeclaration, base.VisitConstructorDeclaration); } public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { Visit(destructorDeclaration, base.VisitDestructorDeclaration); } public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { Visit(operatorDeclaration, base.VisitOperatorDeclaration); } void Visit(T entityDeclaration, Action baseCall) where T : EntityDeclaration { if (ignoreUsingScope) { baseCall(entityDeclaration); return; } if (entityDeclaration.GetSymbol() is IMethod method) { var previousResolver = resolver; var previousAstBuilder = astBuilder; if (CSharpDecompiler.IsWindowsFormsInitializeComponentMethod(method)) { var currentContext = new CSharpTypeResolveContext(previousResolver.Compilation.MainModule); resolver = new CSharpResolver(currentContext); } else { resolver = resolver.WithCurrentMember(method); } try { var function = entityDeclaration.Annotation(); astBuilder = CreateAstBuilder(resolver, function); baseCall(entityDeclaration); } finally { resolver = previousResolver; astBuilder = previousAstBuilder; } } else { baseCall(entityDeclaration); } } public override void VisitSimpleType(SimpleType simpleType) { TypeResolveResult rr; if ((rr = simpleType.Annotation()) == null) { base.VisitSimpleType(simpleType); return; } astBuilder.NameLookupMode = simpleType.GetNameLookupMode(); if (astBuilder.NameLookupMode == NameLookupMode.Type) { AstType outermostType = simpleType; while (outermostType.Parent is AstType) outermostType = (AstType)outermostType.Parent; if (outermostType.Parent is TypeReferenceExpression) { // ILSpy uses TypeReferenceExpression in expression context even when the C# parser // wouldn't know that it's a type reference. // Fall back to expression-mode lookup in these cases: astBuilder.NameLookupMode = NameLookupMode.Expression; } } if (simpleType.Parent is Syntax.Attribute) { simpleType.ReplaceWith(astBuilder.ConvertAttributeType(rr.Type)); } else { simpleType.ReplaceWith(astBuilder.ConvertType(rr.Type)); } } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Transforms { class NormalizeBlockStatements : DepthFirstAstVisitor, IAstTransform { TransformContext context; bool hasNamespace; NamespaceDeclaration singleNamespaceDeclaration; public override void VisitSyntaxTree(SyntaxTree syntaxTree) { singleNamespaceDeclaration = null; hasNamespace = false; base.VisitSyntaxTree(syntaxTree); if (context.Settings.FileScopedNamespaces && singleNamespaceDeclaration != null) { singleNamespaceDeclaration.IsFileScoped = true; } } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { singleNamespaceDeclaration = null; if (!hasNamespace) { hasNamespace = true; singleNamespaceDeclaration = namespaceDeclaration; } base.VisitNamespaceDeclaration(namespaceDeclaration); } public override void VisitIfElseStatement(IfElseStatement ifElseStatement) { base.VisitIfElseStatement(ifElseStatement); DoTransform(ifElseStatement.TrueStatement, ifElseStatement); DoTransform(ifElseStatement.FalseStatement, ifElseStatement); } public override void VisitWhileStatement(WhileStatement whileStatement) { base.VisitWhileStatement(whileStatement); InsertBlock(whileStatement.EmbeddedStatement); } public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { base.VisitDoWhileStatement(doWhileStatement); InsertBlock(doWhileStatement.EmbeddedStatement); } public override void VisitForeachStatement(ForeachStatement foreachStatement) { base.VisitForeachStatement(foreachStatement); InsertBlock(foreachStatement.EmbeddedStatement); } public override void VisitForStatement(ForStatement forStatement) { base.VisitForStatement(forStatement); InsertBlock(forStatement.EmbeddedStatement); } public override void VisitFixedStatement(FixedStatement fixedStatement) { base.VisitFixedStatement(fixedStatement); InsertBlock(fixedStatement.EmbeddedStatement); } public override void VisitLockStatement(LockStatement lockStatement) { base.VisitLockStatement(lockStatement); InsertBlock(lockStatement.EmbeddedStatement); } public override void VisitUsingStatement(UsingStatement usingStatement) { base.VisitUsingStatement(usingStatement); DoTransform(usingStatement.EmbeddedStatement, usingStatement); } void DoTransform(Statement statement, Statement parent) { if (statement.IsNull) return; if (context.Settings.AlwaysUseBraces) { if (!IsElseIf(statement, parent)) { InsertBlock(statement); } } else { if (statement is BlockStatement b && b.Statements.Count == 1 && IsAllowedAsEmbeddedStatement(b.Statements.First(), parent)) { statement.ReplaceWith(b.Statements.First().Detach()); } else if (!IsAllowedAsEmbeddedStatement(statement, parent)) { InsertBlock(statement); } } } bool IsElseIf(Statement statement, Statement parent) { return parent is IfElseStatement && statement.Role == IfElseStatement.FalseRole; } static void InsertBlock(Statement statement) { if (statement.IsNull) return; if (!(statement is BlockStatement)) { var b = new BlockStatement(); statement.ReplaceWith(b); if (statement is EmptyStatement && !statement.Children.Any()) { b.CopyAnnotationsFrom(statement); } else { b.Add(statement); } } } bool IsAllowedAsEmbeddedStatement(Statement statement, Statement parent) { switch (statement) { case IfElseStatement ies: return parent is IfElseStatement && ies.Role == IfElseStatement.FalseRole; case VariableDeclarationStatement vds: case WhileStatement ws: case DoWhileStatement dws: case SwitchStatement ss: case ForeachStatement fes: case ForStatement fs: case LockStatement ls: case FixedStatement fxs: return false; case UsingStatement us: return parent is UsingStatement && !us.IsEnhanced; default: return !(parent?.Parent is IfElseStatement); } } void IAstTransform.Run(AstNode rootNode, TransformContext context) { this.context = context; rootNode.AcceptVisitor(this); } public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) { SimplifyPropertyDeclaration(propertyDeclaration); } base.VisitPropertyDeclaration(propertyDeclaration); } public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) { SimplifyIndexerDeclaration(indexerDeclaration); } base.VisitIndexerDeclaration(indexerDeclaration); } static readonly PropertyDeclaration CalculatedGetterOnlyPropertyPattern = new PropertyDeclaration() { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Name = Pattern.AnyString, PrivateImplementationType = new AnyNodeOrNull(), ReturnType = new AnyNode(), Getter = new Accessor() { Modifiers = Modifiers.Any, Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } }; static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, PrivateImplementationType = new AnyNodeOrNull(), Parameters = { new Repeat(new AnyNode()) }, ReturnType = new AnyNode(), Getter = new Accessor() { Modifiers = Modifiers.Any, Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } }; /// /// Modifiers that are emitted on accessors, but can be moved to the property declaration. /// const Modifiers movableModifiers = Modifiers.Readonly; void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration) { var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); if (!m.Success) return; if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0) return; propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers; propertyDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); propertyDeclaration.CopyAnnotationsFrom(propertyDeclaration.Getter); propertyDeclaration.Getter.Remove(); } void SimplifyIndexerDeclaration(IndexerDeclaration indexerDeclaration) { var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration); if (!m.Success) return; if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0) return; indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers; indexerDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); indexerDeclaration.CopyAnnotationsFrom(indexerDeclaration.Getter); indexerDeclaration.Getter.Remove(); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs ================================================ // 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 // 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.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement. /// public sealed class PatternStatementTransform : ContextTrackingVisitor, IAstTransform { readonly DeclareVariables declareVariables = new DeclareVariables(); TransformContext context; public void Run(AstNode rootNode, TransformContext context) { if (this.context != null) throw new InvalidOperationException("Reentrancy in PatternStatementTransform.Run?"); try { this.context = context; base.Initialize(context); declareVariables.Analyze(rootNode); rootNode.AcceptVisitor(this); } finally { this.context = null; base.Uninitialize(); declareVariables.ClearAnalysisResults(); } } #region Visitor Overrides protected override AstNode VisitChildren(AstNode node) { // Go through the children, and keep visiting a node as long as it changes. // Because some transforms delete/replace nodes before and after the node being transformed, we rely // on the transform's return value to know where we need to keep iterating. for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { AstNode oldChild; do { oldChild = child; child = child.AcceptVisitor(this); Debug.Assert(child != null && child.Parent == node); } while (child != oldChild); } return node; } public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement) { AstNode result = TransformForeachOnMultiDimArray(expressionStatement); if (result != null) return result; result = TransformFor(expressionStatement); if (result != null) return result; return base.VisitExpressionStatement(expressionStatement); } public override AstNode VisitForStatement(ForStatement forStatement) { AstNode result = TransformForeachOnArray(forStatement); if (result != null) return result; return base.VisitForStatement(forStatement); } public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement) { AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement); if (simplifiedIfElse != null) return simplifiedIfElse; return base.VisitIfElseStatement(ifElseStatement); } public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (context.Settings.AutomaticProperties && (!propertyDeclaration.Setter.IsNull || context.Settings.GetterOnlyAutomaticProperties)) { AstNode result = TransformAutomaticProperty(propertyDeclaration); if (result != null) return result; } return base.VisitPropertyDeclaration(propertyDeclaration); } public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { // first apply transforms to the accessor bodies base.VisitCustomEventDeclaration(eventDeclaration); if (context.Settings.AutomaticEvents) { AstNode result = TransformAutomaticEvents(eventDeclaration); if (result != null) return result; } return eventDeclaration; } public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration) { return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration); } public override AstNode VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { return TransformDestructorBody(destructorDeclaration) ?? base.VisitDestructorDeclaration(destructorDeclaration); } public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement); } #endregion /// /// $variable = $initializer; /// static readonly AstNode variableAssignPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), new AnyNode("initializer") )); #region for static readonly WhileStatement forPattern = new WhileStatement { Condition = new BinaryOperatorExpression { Left = new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)), Operator = BinaryOperatorType.Any, Right = new AnyNode("endExpr") }, EmbeddedStatement = new BlockStatement { Statements = { new Repeat(new AnyNode("statement")), new NamedNode( "iterator", new ExpressionStatement( new AssignmentExpression { Left = new Backreference("ident"), Operator = AssignmentOperatorType.Any, Right = new AnyNode() })) } } }; public ForStatement TransformFor(ExpressionStatement node) { if (!context.Settings.ForStatement) return null; Match m1 = variableAssignPattern.Match(node); if (!m1.Success) return null; var variable = m1.Get("variable").Single().GetILVariable(); AstNode next = node.NextSibling; if (next is ForStatement forStatement && ForStatementUsesVariable(forStatement, variable)) { node.Remove(); next.InsertChildAfter(null, node, ForStatement.InitializerRole); return (ForStatement)next; } Match m3 = forPattern.Match(next); if (!m3.Success) return null; // ensure the variable in the for pattern is the same as in the declaration if (variable != m3.Get("ident").Single().GetILVariable()) return null; WhileStatement loop = (WhileStatement)next; // Cannot convert to for loop, if any variable that is used in the "iterator" part of the pattern, // will be declared in the body of the while-loop. var iteratorStatement = m3.Get("iterator").Single(); if (IteratorVariablesDeclaredInsideLoopBody(iteratorStatement)) return null; // Cannot convert to for loop, because that would change the semantics of the program. // continue in while jumps to the condition block. // Whereas continue in for jumps to the increment block. if (loop.DescendantNodes(DescendIntoStatement).OfType().Any(s => s is ContinueStatement)) return null; node.Remove(); BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m3.Get("statement")) newBody.Add(stmt.Detach()); forStatement = new ForStatement(); forStatement.CopyAnnotationsFrom(loop); forStatement.Initializers.Add(node); forStatement.Condition = loop.Condition.Detach(); forStatement.Iterators.Add(iteratorStatement.Detach()); forStatement.EmbeddedStatement = newBody; loop.ReplaceWith(forStatement); return forStatement; } bool DescendIntoStatement(AstNode node) { if (node is Expression || node is ExpressionStatement) return false; if (node is WhileStatement || node is ForeachStatement || node is DoWhileStatement || node is ForStatement) return false; return true; } bool ForStatementUsesVariable(ForStatement statement, IL.ILVariable variable) { if (statement.Condition.DescendantsAndSelf.OfType().Any(ie => ie.GetILVariable() == variable)) return true; if (statement.Iterators.Any(i => i.DescendantsAndSelf.OfType().Any(ie => ie.GetILVariable() == variable))) return true; return false; } bool IteratorVariablesDeclaredInsideLoopBody(Statement iteratorStatement) { foreach (var id in iteratorStatement.DescendantsAndSelf.OfType()) { var v = id.GetILVariable(); if (v == null || !DeclareVariables.VariableNeedsDeclaration(v.Kind)) continue; if (declareVariables.GetDeclarationPoint(v).Parent == iteratorStatement.Parent) return true; } return false; } #endregion #region foreach static readonly ForStatement forOnArrayPattern = new ForStatement { Initializers = { new ExpressionStatement( new AssignmentExpression( new NamedNode("indexVariable", new IdentifierExpression(Pattern.AnyString)), new PrimitiveExpression(0) )) }, Condition = new BinaryOperatorExpression( new IdentifierExpressionBackreference("indexVariable"), BinaryOperatorType.LessThan, new MemberReferenceExpression(new NamedNode("arrayVariable", new IdentifierExpression(Pattern.AnyString)), "Length") ), Iterators = { new ExpressionStatement( new AssignmentExpression( new IdentifierExpressionBackreference("indexVariable"), new BinaryOperatorExpression(new IdentifierExpressionBackreference("indexVariable"), BinaryOperatorType.Add, new PrimitiveExpression(1)) )) }, EmbeddedStatement = new BlockStatement { Statements = { new ExpressionStatement(new AssignmentExpression( new NamedNode("itemVariable", new IdentifierExpression(Pattern.AnyString)), new IndexerExpression(new IdentifierExpressionBackreference("arrayVariable"), new IdentifierExpressionBackreference("indexVariable")) )), new Repeat(new AnyNode("statements")) } } }; bool VariableCanBeUsedAsForeachLocal(IL.ILVariable itemVar, Statement loop) { if (itemVar == null || !(itemVar.Kind == IL.VariableKind.Local || itemVar.Kind == IL.VariableKind.StackSlot)) { // only locals/temporaries can be converted into foreach loop variable return false; } var blockContainer = loop.Annotation(); if (!itemVar.IsSingleDefinition) { // foreach variable cannot be assigned to. // As a special case, we accept taking the address for a method call, // but only if the call is the only use, so that any mutation by the call // cannot be observed. if (!AddressUsedForSingleCall(itemVar, blockContainer)) { return false; } } if (itemVar.CaptureScope != null && itemVar.CaptureScope != blockContainer) { // captured variables cannot be declared in the loop unless the loop is their capture scope return false; } AstNode declPoint = declareVariables.GetDeclarationPoint(itemVar); return declPoint.Ancestors.Contains(loop) && !declareVariables.WasMerged(itemVar); } static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop) { if (v.StoreCount == 1 && v.AddressCount == 1 && v.LoadCount == 0 && v.Type.IsReferenceType == false) { if (v.AddressInstructions[0].Parent is IL.Call call && v.AddressInstructions[0].ChildIndex == 0 && !call.Method.IsStatic) { // used as this pointer for a method call // this is OK iff the call is not within a nested loop for (var node = call.Parent; node != null; node = node.Parent) { if (node == loop) return true; else if (node is IL.BlockContainer) break; } } } return false; } Statement TransformForeachOnArray(ForStatement forStatement) { if (!context.Settings.ForEachStatement) return null; Match m = forOnArrayPattern.Match(forStatement); if (!m.Success) return null; var itemVariable = m.Get("itemVariable").Single().GetILVariable(); var indexVariable = m.Get("indexVariable").Single().GetILVariable(); var arrayVariable = m.Get("arrayVariable").Single().GetILVariable(); if (itemVariable == null || indexVariable == null || arrayVariable == null) return null; if (arrayVariable.Type.Kind != TypeKind.Array && !arrayVariable.Type.IsKnownType(KnownTypeCode.String)) return null; if (!VariableCanBeUsedAsForeachLocal(itemVariable, forStatement)) return null; if (indexVariable.StoreCount != 2 || indexVariable.LoadCount != 3 || indexVariable.AddressCount != 0) return null; var body = new BlockStatement(); foreach (var statement in m.Get("statements")) body.Statements.Add(statement.Detach()); var foreachStmt = new ForeachStatement { VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type), VariableDesignation = new SingleVariableDesignation { Identifier = itemVariable.Name }, InExpression = m.Get("arrayVariable").Single().Detach(), EmbeddedStatement = body }; foreachStmt.CopyAnnotationsFrom(forStatement); itemVariable.Kind = IL.VariableKind.ForeachLocal; // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). foreachStmt.VariableDesignation.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); // TODO : add ForeachAnnotation forStatement.ReplaceWith(foreachStmt); return foreachStmt; } static readonly ForStatement forOnArrayMultiDimPattern = new ForStatement { Initializers = { }, Condition = new BinaryOperatorExpression( new NamedNode("indexVariable", new IdentifierExpression(Pattern.AnyString)), BinaryOperatorType.LessThanOrEqual, new NamedNode("upperBoundVariable", new IdentifierExpression(Pattern.AnyString)) ), Iterators = { new ExpressionStatement( new AssignmentExpression( new IdentifierExpressionBackreference("indexVariable"), new BinaryOperatorExpression(new IdentifierExpressionBackreference("indexVariable"), BinaryOperatorType.Add, new PrimitiveExpression(1)) )) }, EmbeddedStatement = new BlockStatement { Statements = { new AnyNode("lowerBoundAssign"), new Repeat(new AnyNode("statements")) } } }; /// /// $variable = $collection.GetUpperBound($index); /// static readonly AstNode variableAssignUpperBoundPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), new InvocationExpression( new MemberReferenceExpression( new NamedNode("collection", new IdentifierExpression(Pattern.AnyString)), "GetUpperBound" ), new NamedNode("index", new PrimitiveExpression(PrimitiveExpression.AnyValue)) ))); /// /// $variable = $collection.GetLowerBound($index); /// static readonly ExpressionStatement variableAssignLowerBoundPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), new InvocationExpression( new MemberReferenceExpression( new NamedNode("collection", new IdentifierExpression(Pattern.AnyString)), "GetLowerBound" ), new NamedNode("index", new PrimitiveExpression(PrimitiveExpression.AnyValue)) ))); /// /// $variable = $collection[$index1, $index2, ...]; /// static readonly ExpressionStatement foreachVariableOnMultArrayAssignPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), new IndexerExpression( new NamedNode("collection", new IdentifierExpression(Pattern.AnyString)), new Repeat(new NamedNode("index", new IdentifierExpression(Pattern.AnyString)) ) ))); bool MatchLowerBound(int indexNum, out IL.ILVariable index, IL.ILVariable collection, Statement statement) { index = null; var m = variableAssignLowerBoundPattern.Match(statement); if (!m.Success) return false; if (!int.TryParse(m.Get("index").Single().Value.ToString(), out int i) || indexNum != i) return false; index = m.Get("variable").Single().GetILVariable(); return m.Get("collection").Single().GetILVariable() == collection; } bool MatchForeachOnMultiDimArray(IL.ILVariable[] upperBounds, IL.ILVariable collection, Statement firstInitializerStatement, out IdentifierExpression foreachVariable, out IList statements, out IL.ILVariable[] lowerBounds) { int i = 0; foreachVariable = null; statements = null; lowerBounds = new IL.ILVariable[upperBounds.Length]; Statement stmt = firstInitializerStatement; Match m = default(Match); while (i < upperBounds.Length && MatchLowerBound(i, out IL.ILVariable indexVariable, collection, stmt)) { m = forOnArrayMultiDimPattern.Match(stmt.GetNextStatement()); if (!m.Success) return false; var upperBound = m.Get("upperBoundVariable").Single().GetILVariable(); if (upperBounds[i] != upperBound) return false; stmt = m.Get("lowerBoundAssign").Single(); lowerBounds[i] = indexVariable; i++; } if (collection.Type.Kind != TypeKind.Array) return false; var m2 = foreachVariableOnMultArrayAssignPattern.Match(stmt); if (!m2.Success) return false; var collection2 = m2.Get("collection").Single().GetILVariable(); if (collection2 != collection) return false; foreachVariable = m2.Get("variable").Single(); statements = m.Get("statements").ToList(); return true; } Statement TransformForeachOnMultiDimArray(ExpressionStatement expressionStatement) { if (!context.Settings.ForEachStatement) return null; Match m; Statement stmt = expressionStatement; IL.ILVariable collection = null; IL.ILVariable[] upperBounds = null; List statementsToDelete = new List(); int i = 0; // first we look for all the upper bound initializations do { m = variableAssignUpperBoundPattern.Match(stmt); if (!m.Success) break; if (upperBounds == null) { collection = m.Get("collection").Single().GetILVariable(); if (!(collection?.Type is Decompiler.TypeSystem.ArrayType arrayType)) break; upperBounds = new IL.ILVariable[arrayType.Dimensions]; } else { statementsToDelete.Add(stmt); } var nextCollection = m.Get("collection").Single().GetILVariable(); if (nextCollection != collection) break; if (!int.TryParse(m.Get("index").Single().Value?.ToString() ?? "", out int index) || index != i) break; upperBounds[i] = m.Get("variable").Single().GetILVariable(); stmt = stmt.GetNextStatement(); i++; } while (stmt != null && i < upperBounds.Length); if (upperBounds?.LastOrDefault() == null || collection == null) return null; if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds)) return null; statementsToDelete.Add(stmt); statementsToDelete.Add(stmt.GetNextStatement()); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) || !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) || !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0)) return null; var body = new BlockStatement(); foreach (var statement in statements) body.Statements.Add(statement.Detach()); var foreachStmt = new ForeachStatement { VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type), VariableDesignation = new SingleVariableDesignation { Identifier = itemVariable.Name }, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = body }; foreach (var statement in statementsToDelete) statement.Detach(); //foreachStmt.CopyAnnotationsFrom(forStatement); itemVariable.Kind = IL.VariableKind.ForeachLocal; // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). foreachStmt.VariableDesignation.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); // TODO : add ForeachAnnotation expressionStatement.ReplaceWith(foreachStmt); return foreachStmt; } #endregion #region Automatic Properties static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, ReturnType = new AnyNode(), PrivateImplementationType = new OptionalNode(new AnyNode()), Name = Pattern.AnyString, Getter = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Body = new BlockStatement { new ReturnStatement { Expression = new AnyNode("fieldReference") } } }, Setter = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Body = new BlockStatement { new AssignmentExpression { Left = new Backreference("fieldReference"), Right = new IdentifierExpression("value") } } } }; static readonly PropertyDeclaration automaticReadonlyPropertyPattern = new PropertyDeclaration { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, ReturnType = new AnyNode(), PrivateImplementationType = new OptionalNode(new AnyNode()), Name = Pattern.AnyString, Getter = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Body = new BlockStatement { new ReturnStatement { Expression = new AnyNode("fieldReference") } } } }; bool CanTransformToAutomaticProperty(IProperty property, bool accessorsMustBeCompilerGenerated) { if (!property.CanGet) return false; if (accessorsMustBeCompilerGenerated && !property.Getter.IsCompilerGenerated()) return false; if (property.Setter is IMethod setter) { if (accessorsMustBeCompilerGenerated && !setter.IsCompilerGenerated()) return false; if (setter.HasReadonlyModifier()) return false; } return true; } PropertyDeclaration TransformAutomaticProperty(PropertyDeclaration propertyDeclaration) { IProperty property = propertyDeclaration.GetSymbol() as IProperty; if (!CanTransformToAutomaticProperty(property, !property.DeclaringTypeDefinition.Fields.Any(f => f.Name == "_" + property.Name && f.IsCompilerGenerated()))) return null; IField field = null; Match m = automaticPropertyPattern.Match(propertyDeclaration); if (m.Success) { field = m.Get("fieldReference").Single().GetSymbol() as IField; } else { Match m2 = automaticReadonlyPropertyPattern.Match(propertyDeclaration); if (m2.Success) { field = m2.Get("fieldReference").Single().GetSymbol() as IField; } } if (field == null || !NameCouldBeBackingFieldOfAutomaticProperty(field.Name, out _)) return null; if (propertyDeclaration.Setter.HasModifier(Modifiers.Readonly) || (propertyDeclaration.HasModifier(Modifiers.Readonly) && !propertyDeclaration.Setter.IsNull)) return null; if (field.IsCompilerGenerated() && field.DeclaringTypeDefinition == property.DeclaringTypeDefinition) { RemoveCompilerGeneratedAttribute(propertyDeclaration.Getter.Attributes); RemoveCompilerGeneratedAttribute(propertyDeclaration.Setter.Attributes); propertyDeclaration.Getter.Body = null; propertyDeclaration.Setter.Body = null; propertyDeclaration.Modifiers &= ~Modifiers.Readonly; propertyDeclaration.Getter.Modifiers &= ~Modifiers.Readonly; var fieldDecl = propertyDeclaration.Parent?.Children.OfType() .FirstOrDefault(fd => field.Equals(fd.GetSymbol())); if (fieldDecl != null) { fieldDecl.Remove(); // Add C# 7.3 attributes on backing field: CSharpDecompiler.RemoveAttribute(fieldDecl, KnownAttribute.CompilerGenerated); CSharpDecompiler.RemoveAttribute(fieldDecl, KnownAttribute.DebuggerBrowsable); foreach (var section in fieldDecl.Attributes) { section.AttributeTarget = "field"; propertyDeclaration.Attributes.Add(section.Detach()); } } } // Since the property instance is not changed, we can continue in the visitor as usual, so return null return null; } void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections) { RemoveCompilerGeneratedAttribute(attributeSections, "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); } void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections, params string[] attributesToRemove) { foreach (AttributeSection section in attributeSections) { foreach (var attr in section.Attributes) { var tr = attr.Type.GetSymbol() as IType; if (tr != null && attributesToRemove.Contains(tr.FullName)) { attr.Remove(); } } if (section.Attributes.Count == 0) section.Remove(); } } #endregion public override AstNode VisitIdentifier(Identifier identifier) { if (context.Settings.AutomaticProperties) { var newIdentifier = ReplaceBackingFieldUsage(identifier); if (newIdentifier != null) { identifier.ReplaceWith(newIdentifier); return newIdentifier; } } if (context.Settings.AutomaticEvents) { var newIdentifier = ReplaceEventFieldAnnotation(identifier); if (newIdentifier != null) return newIdentifier; } return base.VisitIdentifier(identifier); } internal static bool IsBackingFieldOfAutomaticProperty(IField field, out IProperty property) { property = null; if (!NameCouldBeBackingFieldOfAutomaticProperty(field.Name, out string propertyName)) return false; if (!field.IsCompilerGenerated()) return false; property = field.DeclaringTypeDefinition .GetProperties(p => p.Name == propertyName, GetMemberOptions.IgnoreInheritedMembers) .FirstOrDefault(); return property != null; } /// /// This matches the following patterns /// /// <Property>k__BackingField (used by C#) /// _Property (used by VB) /// /// static readonly System.Text.RegularExpressions.Regex automaticPropertyBackingFieldNameRegex = new System.Text.RegularExpressions.Regex(@"^(<(?.+)>k__BackingField|_(?.+))$"); static bool NameCouldBeBackingFieldOfAutomaticProperty(string name, out string propertyName) { propertyName = null; var m = automaticPropertyBackingFieldNameRegex.Match(name); if (!m.Success) return false; propertyName = m.Groups["name"].Value; return true; } Identifier ReplaceBackingFieldUsage(Identifier identifier) { if (NameCouldBeBackingFieldOfAutomaticProperty(identifier.Name, out _)) { var parent = identifier.Parent; var mrr = parent.Annotation(); var field = mrr?.Member as IField; if (field != null && IsBackingFieldOfAutomaticProperty(field, out var property) && CanTransformToAutomaticProperty(property, !(field.IsCompilerGenerated() && field.Name == "_" + property.Name)) && currentMethod.AccessorOwner != property) { if (!property.CanSet && !context.Settings.GetterOnlyAutomaticProperties) return null; parent.RemoveAnnotations(); parent.AddAnnotation(new MemberResolveResult(mrr.TargetResult, property)); return Identifier.Create(property.Name); } } return null; } Identifier ReplaceEventFieldAnnotation(Identifier identifier) { var parent = identifier.Parent; var mrr = parent.Annotation(); if (mrr?.Member is not IField field || field.Accessibility != Accessibility.Private) return null; var module = field.ParentModule as MetadataModule; if (module == null) return null; if (module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)field.MetadataToken, out var eventHandle)) { var eventDef = module.ResolveEntity(eventHandle) as IEvent; if (eventDef != null && currentMethod.AccessorOwner != eventDef) { parent.RemoveAnnotations(); parent.AddAnnotation(new MemberResolveResult(mrr.TargetResult, eventDef)); identifier.Name = eventDef.Name; return identifier; } } return null; } #region Automatic Events static readonly Expression fieldReferencePattern = new Choice { new IdentifierExpression(Pattern.AnyString), new MemberReferenceExpression { Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, MemberName = Pattern.AnyString } }; static readonly Accessor automaticEventPatternV2 = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Body = new BlockStatement { new AssignmentExpression { Left = new NamedNode("field", fieldReferencePattern), Operator = AssignmentOperatorType.Assign, Right = new CastExpression( new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new Backreference("field"), new IdentifierExpression("value")) ) }, } }; static readonly Accessor automaticEventPatternV4 = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Body = new BlockStatement { new AssignmentExpression { Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), Operator = AssignmentOperatorType.Assign, Right = new NamedNode("field", fieldReferencePattern) }, new DoWhileStatement { EmbeddedStatement = new BlockStatement { new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), new AssignmentExpression { Left = new NamedNode("var3", new IdentifierExpression(Pattern.AnyString)), Operator = AssignmentOperatorType.Assign, Right = new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value"))) }, new AssignmentExpression { Left = new IdentifierExpressionBackreference("var1"), Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), "CompareExchange"), new Expression[] { // arguments new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, new IdentifierExpressionBackreference("var3"), new IdentifierExpressionBackreference("var2") } )} }, Condition = new BinaryOperatorExpression { Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), Operator = BinaryOperatorType.InEquality, Right = new IdentifierExpressionBackreference("var2") }, } } }; static readonly Accessor automaticEventPatternV4AggressivelyInlined = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Body = new BlockStatement { new AssignmentExpression { Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), Operator = AssignmentOperatorType.Assign, Right = new NamedNode("field", fieldReferencePattern) }, new DoWhileStatement { EmbeddedStatement = new BlockStatement { new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), new AssignmentExpression { Left = new IdentifierExpressionBackreference("var1"), Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), "CompareExchange"), new Expression[] { // arguments new NamedArgumentExpression("value", new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value")))), new NamedArgumentExpression("location1", new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }), new NamedArgumentExpression("comparand", new IdentifierExpressionBackreference("var2")) } )} }, Condition = new BinaryOperatorExpression { Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), Operator = BinaryOperatorType.InEquality, Right = new IdentifierExpressionBackreference("var2") }, } } }; static readonly Accessor automaticEventPatternV4MCS = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Body = new BlockStatement { new AssignmentExpression { Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), Operator = AssignmentOperatorType.Assign, Right = new NamedNode( "field", new MemberReferenceExpression { Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, MemberName = Pattern.AnyString } ) }, new DoWhileStatement { EmbeddedStatement = new BlockStatement { new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), new AssignmentExpression { Left = new IdentifierExpressionBackreference("var1"), Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), "CompareExchange", new AstType[] { new Repeat(new AnyNode()) }), // optional type arguments new Expression[] { // arguments new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value"))), new IdentifierExpressionBackreference("var1") } ) } }, Condition = new BinaryOperatorExpression { Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), Operator = BinaryOperatorType.InEquality, Right = new IdentifierExpressionBackreference("var2") }, } } }; bool CheckAutomaticEventMatch(Match m, CustomEventDeclaration ev, bool isAddAccessor) { if (!m.Success) return false; Expression fieldExpression = m.Get("field").Single(); IField eventField = fieldExpression.GetSymbol() as IField; if (eventField == null) return false; var module = eventField.ParentModule as MetadataModule; if (module == null) return false; if (!module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)eventField.MetadataToken, out _)) return false; var returnType = ev.ReturnType.GetResolveResult().Type; var eventType = m.Get("type").Single().GetResolveResult().Type; // ignore tuple element names, dynamic and nullability if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(returnType, eventType)) return false; var combineMethod = m.Get("delegateCombine").Single().Parent.GetSymbol() as IMethod; if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) return false; return combineMethod.DeclaringType.FullName == "System.Delegate"; } static readonly string[] attributeTypesToRemoveFromAutoEvents = new[] { "System.Runtime.CompilerServices.CompilerGeneratedAttribute", "System.Diagnostics.DebuggerBrowsableAttribute", "System.Runtime.CompilerServices.MethodImplAttribute" }; internal static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] { "System.Runtime.CompilerServices.CompilerGeneratedAttribute", "System.Diagnostics.DebuggerBrowsableAttribute" }; bool CheckAutomaticEventV4(CustomEventDeclaration ev) { Match addMatch = automaticEventPatternV4.Match(ev.AddAccessor); if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) return false; Match removeMatch = automaticEventPatternV4.Match(ev.RemoveAccessor); if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) return false; return true; } bool CheckAutomaticEventV4AggressivelyInlined(CustomEventDeclaration ev) { if (!context.Settings.AggressiveInlining) return false; Match addMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.AddAccessor); if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) return false; Match removeMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.RemoveAccessor); if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) return false; return true; } bool CheckAutomaticEventV2(CustomEventDeclaration ev) { Match addMatch = automaticEventPatternV2.Match(ev.AddAccessor); if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) return false; Match removeMatch = automaticEventPatternV2.Match(ev.RemoveAccessor); if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) return false; return true; } bool CheckAutomaticEventV4MCS(CustomEventDeclaration ev) { Match addMatch = automaticEventPatternV4MCS.Match(ev.AddAccessor); if (!CheckAutomaticEventMatch(addMatch, ev, true)) return false; Match removeMatch = automaticEventPatternV4MCS.Match(ev.RemoveAccessor); if (!CheckAutomaticEventMatch(removeMatch, ev, false)) return false; return true; } EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev) { if (!ev.PrivateImplementationType.IsNull) return null; const Modifiers withoutBody = Modifiers.Abstract | Modifiers.Extern; if (ev.GetSymbol() is not IEvent symbol) return null; if ((ev.Modifiers & withoutBody) == 0) { if (!CheckAutomaticEventV4AggressivelyInlined(ev) && !CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev)) return null; } RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); EventDeclaration ed = new EventDeclaration(); ev.Attributes.MoveTo(ed.Attributes); foreach (var attr in ev.AddAccessor.Attributes) { attr.AttributeTarget = "method"; ed.Attributes.Add(attr.Detach()); } ed.ReturnType = ev.ReturnType.Detach(); ed.Modifiers = ev.Modifiers; ed.Variables.Add(new VariableInitializer(ev.Name)); ed.CopyAnnotationsFrom(ev); var fieldDecl = ev.Parent?.Children.OfType() .FirstOrDefault(IsEventBackingField); if (fieldDecl != null) { fieldDecl.Remove(); CSharpDecompiler.RemoveAttribute(fieldDecl, KnownAttribute.CompilerGenerated); CSharpDecompiler.RemoveAttribute(fieldDecl, KnownAttribute.DebuggerBrowsable); foreach (var section in fieldDecl.Attributes) { section.AttributeTarget = "field"; ed.Attributes.Add(section.Detach()); } } ev.ReplaceWith(ed); return ed; bool IsEventBackingField(FieldDeclaration fd) { if (fd.Variables.Count > 1) return false; if (fd.GetSymbol() is not IField f) return false; if (f.ParentModule is not MetadataModule module) return false; return f.Accessibility == Accessibility.Private && symbol.ReturnType.Equals(f.ReturnType) && module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)f.MetadataToken, out _); } } #endregion #region Destructor static readonly BlockStatement destructorBodyPattern = new BlockStatement { new TryCatchStatement { TryBlock = new AnyNode("body"), FinallyBlock = new BlockStatement { new InvocationExpression(new MemberReferenceExpression(new BaseReferenceExpression(), "Finalize")) } } }; static readonly MethodDeclaration destructorPattern = new MethodDeclaration { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, ReturnType = new PrimitiveType("void"), Name = "Finalize", Body = destructorBodyPattern }; DestructorDeclaration TransformDestructor(MethodDeclaration methodDef) { Match m = destructorPattern.Match(methodDef); if (m.Success) { DestructorDeclaration dd = new DestructorDeclaration(); methodDef.Attributes.MoveTo(dd.Attributes); dd.CopyAnnotationsFrom(methodDef); dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); dd.Body = m.Get("body").Single().Detach(); dd.Name = currentTypeDefinition?.Name; methodDef.ReplaceWith(dd); return dd; } return null; } DestructorDeclaration TransformDestructorBody(DestructorDeclaration dtorDef) { Match m = destructorBodyPattern.Match(dtorDef.Body); if (m.Success) { dtorDef.Body = m.Get("body").Single().Detach(); return dtorDef; } return null; } #endregion #region Try-Catch-Finally static readonly TryCatchStatement tryCatchFinallyPattern = new TryCatchStatement { TryBlock = new BlockStatement { new TryCatchStatement { TryBlock = new AnyNode(), CatchClauses = { new Repeat(new AnyNode()) } } }, FinallyBlock = new AnyNode() }; /// /// Simplify nested 'try { try {} catch {} } finally {}'. /// This transformation must run after the using/lock tranformations. /// TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally) { if (tryCatchFinallyPattern.IsMatch(tryFinally)) { TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single(); tryFinally.TryBlock = tryCatch.TryBlock.Detach(); tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses); } // Since the tryFinally instance is not changed, we can continue in the visitor as usual, so return null return null; } #endregion #region Simplify cascading if-else-if statements static readonly IfElseStatement cascadingIfElsePattern = new IfElseStatement { Condition = new AnyNode(), TrueStatement = new AnyNode(), FalseStatement = new BlockStatement { Statements = { new NamedNode( "nestedIfStatement", new IfElseStatement { Condition = new AnyNode(), TrueStatement = new AnyNode(), FalseStatement = new OptionalNode(new AnyNode()) } ) } } }; AstNode SimplifyCascadingIfElseStatements(IfElseStatement node) { Match m = cascadingIfElsePattern.Match(node); if (m.Success) { IfElseStatement elseIf = m.Get("nestedIfStatement").Single(); node.FalseStatement = elseIf.Detach(); } return null; } /// /// Use associativity of logic operators to avoid parentheses. /// public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression expr) { switch (expr.Operator) { case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: // a && (b && c) ==> (a && b) && c var bAndC = expr.Right as BinaryOperatorExpression; if (bAndC != null && bAndC.Operator == expr.Operator) { // make bAndC the parent and expr the child var b = bAndC.Left.Detach(); var c = bAndC.Right.Detach(); expr.ReplaceWith(bAndC.Detach()); bAndC.Left = expr; bAndC.Right = c; expr.Right = b; return base.VisitBinaryOperatorExpression(bAndC); } break; } return base.VisitBinaryOperatorExpression(expr); } public override AstNode VisitUnaryOperatorExpression(UnaryOperatorExpression expr) { if (expr.Operator == UnaryOperatorType.Not && expr.Expression is BinaryOperatorExpression { Operator: BinaryOperatorType.Equality } binary) { binary.Operator = BinaryOperatorType.InEquality; expr.ReplaceWith(binary.Detach()); return VisitBinaryOperatorExpression(binary); } return base.VisitUnaryOperatorExpression(expr); } #endregion #region C# 7.3 pattern based fixed (for value types) // reference types are handled by DetectPinnedRegions.IsCustomRefPinPattern static readonly Expression addressOfPinnableReference = new UnaryOperatorExpression { Operator = UnaryOperatorType.AddressOf, Expression = new InvocationExpression { Target = new MemberReferenceExpression(new AnyNode("target"), "GetPinnableReference"), Arguments = { } } }; public override AstNode VisitFixedStatement(FixedStatement fixedStatement) { if (context.Settings.PatternBasedFixedStatement) { foreach (var v in fixedStatement.Variables) { var m = addressOfPinnableReference.Match(v.Initializer); if (m.Success) { Expression target = m.Get("target").Single(); if (target.GetResolveResult().Type.IsReferenceType == false) { v.Initializer = target.Detach(); } } } } return base.VisitFixedStatement(fixedStatement); } #endregion #region C# 8.0 Using variables public override AstNode VisitUsingStatement(UsingStatement usingStatement) { usingStatement = (UsingStatement)base.VisitUsingStatement(usingStatement); if (!context.Settings.UseEnhancedUsing) return usingStatement; if (usingStatement.GetNextStatement() != null || !(usingStatement.Parent is BlockStatement)) return usingStatement; if (!(usingStatement.ResourceAcquisition is VariableDeclarationStatement)) return usingStatement; usingStatement.IsEnhanced = true; return usingStatement; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs ================================================ // 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 // 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.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Simplifies "x = x op y" into "x op= y" where possible. /// /// /// Because the two "x" in "x = x op y" may refer to different ILVariables, /// this transform must run after DeclareVariables. /// /// It must also run after ReplaceMethodCallsWithOperators (so that it can work for custom operator, too); /// and after AddCheckedBlocks (because "for (;; x = unchecked(x op y))" cannot be transformed into "x += y"). /// class PrettifyAssignments : DepthFirstAstVisitor, IAstTransform { TransformContext context; public override void VisitAssignmentExpression(AssignmentExpression assignment) { base.VisitAssignmentExpression(assignment); // Combine "x = x op y" into "x op= y" // Also supports "x = (T)(x op y)" -> "x op= y", if x.GetType() == T // and y is implicitly convertible to T. Expression rhs = assignment.Right; IType expectedType = null; if (assignment.Right is CastExpression { Type: var astType } cast) { rhs = cast.Expression; expectedType = astType.GetResolveResult().Type; } if (rhs is BinaryOperatorExpression binary && assignment.Operator == AssignmentOperatorType.Assign) { if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left) && IsImplicitlyConvertible(binary.Right, expectedType)) { assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator); if (assignment.Operator != AssignmentOperatorType.Assign) { // If we found a shorter operator, get rid of the BinaryOperatorExpression: assignment.CopyAnnotationsFrom(binary); assignment.Right = binary.Right; } } } if (context.Settings.IntroduceIncrementAndDecrement && assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract) { // detect increment/decrement var rr = assignment.Right.GetResolveResult(); if (rr.IsCompileTimeConstant && rr.Type.IsCSharpPrimitiveIntegerType() && CSharpPrimitiveCast.Cast(rr.Type.GetTypeCode(), 1, false).Equals(rr.ConstantValue)) { // only if it's not a custom operator if (assignment.Annotation() == null && assignment.Annotation() == null && assignment.Annotation() == null) { UnaryOperatorType type; // When the parent is an expression statement, pre- or post-increment doesn't matter; // so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++)) if (assignment.Parent is ExpressionStatement) type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement; else type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement; assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment)); } } } bool IsImplicitlyConvertible(Expression rhs, IType expectedType) { if (expectedType == null) return true; var conversions = CSharpConversions.Get(context.TypeSystem); return conversions.ImplicitConversion(rhs.GetResolveResult(), expectedType).IsImplicit; } } public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop) { switch (bop) { case BinaryOperatorType.Add: return AssignmentOperatorType.Add; case BinaryOperatorType.Subtract: return AssignmentOperatorType.Subtract; case BinaryOperatorType.Multiply: return AssignmentOperatorType.Multiply; case BinaryOperatorType.Divide: return AssignmentOperatorType.Divide; case BinaryOperatorType.Modulus: return AssignmentOperatorType.Modulus; case BinaryOperatorType.ShiftLeft: return AssignmentOperatorType.ShiftLeft; case BinaryOperatorType.ShiftRight: return AssignmentOperatorType.ShiftRight; case BinaryOperatorType.UnsignedShiftRight: return AssignmentOperatorType.UnsignedShiftRight; case BinaryOperatorType.BitwiseAnd: return AssignmentOperatorType.BitwiseAnd; case BinaryOperatorType.BitwiseOr: return AssignmentOperatorType.BitwiseOr; case BinaryOperatorType.ExclusiveOr: return AssignmentOperatorType.ExclusiveOr; default: return AssignmentOperatorType.Assign; } } static bool CanConvertToCompoundAssignment(Expression left) { MemberReferenceExpression mre = left as MemberReferenceExpression; if (mre != null) return IsWithoutSideEffects(mre.Target); IndexerExpression ie = left as IndexerExpression; if (ie != null) return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects); UnaryOperatorExpression uoe = left as UnaryOperatorExpression; if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) return IsWithoutSideEffects(uoe.Expression); return IsWithoutSideEffects(left); } static bool IsWithoutSideEffects(Expression left) { return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression; } void IAstTransform.Run(AstNode node, TransformContext context) { this.context = context; try { node.AcceptVisitor(this); } finally { this.context = null; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/RemoveCLSCompliantAttribute.cs ================================================ // Copyright (c) 2017 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; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// This transform is used to remove CLSCompliant attributes added by the compiler. We remove them in order to get rid of many warnings. /// /// This transform is only enabled, when exporting a full assembly as project. public class RemoveCLSCompliantAttribute : IAstTransform { public void Run(AstNode rootNode, TransformContext context) { foreach (var section in rootNode.Children.OfType()) { if (section.AttributeTarget == "assembly") continue; foreach (var attribute in section.Attributes) { var trr = attribute.Type.Annotation(); if (trr != null && trr.Type.FullName == "System.CLSCompliantAttribute") attribute.Remove(); } if (section.Attributes.Count == 0) section.Remove(); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs ================================================ // 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 // 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.Linq; using System.Reflection; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Replaces method calls with the appropriate operator expressions. /// public class ReplaceMethodCallsWithOperators : DepthFirstAstVisitor, IAstTransform { static readonly MemberReferenceExpression typeHandleOnTypeOfPattern = new MemberReferenceExpression { Target = new Choice { new TypeOfExpression(new AnyNode()), new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { new AnyNode() } } }, MemberName = "TypeHandle" }; TransformContext context; public override void VisitInvocationExpression(InvocationExpression invocationExpression) { base.VisitInvocationExpression(invocationExpression); ProcessInvocationExpression(invocationExpression); } void ProcessInvocationExpression(InvocationExpression invocationExpression) { var method = invocationExpression.GetSymbol() as IMethod; if (method == null) return; var arguments = invocationExpression.Arguments.ToArray(); // Reduce "String.Concat(a, b)" to "a + b" if (IsStringConcat(method) && context.Settings.StringConcat) { if (arguments is [ArrayCreateExpression ace] && method.Parameters is [{ Type: ArrayType }]) { arguments = ace.Initializer.Elements.ToArray(); } if (!CheckArgumentsForStringConcat(arguments)) { return; } bool isInExpressionTree = invocationExpression.Ancestors.OfType().Any( lambda => lambda.Annotation()?.Kind == IL.ILFunctionKind.ExpressionTree); Expression arg0 = arguments[0].Detach(); Expression arg1 = arguments[1].Detach(); if (!isInExpressionTree) { arg1 = RemoveRedundantToStringInConcat(arg1, method, isLastArgument: arguments.Length == 2).Detach(); if (arg1.GetResolveResult().Type.IsKnownType(KnownTypeCode.String)) { arg0 = RemoveRedundantToStringInConcat(arg0, method, isLastArgument: false).Detach(); } } var expr = new BinaryOperatorExpression(arg0, BinaryOperatorType.Add, arg1); for (int i = 2; i < arguments.Length; i++) { var arg = arguments[i].Detach(); if (!isInExpressionTree) { arg = RemoveRedundantToStringInConcat(arg, method, isLastArgument: i == arguments.Length - 1).Detach(); } expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arg); } expr.CopyAnnotationsFrom(invocationExpression); invocationExpression.ReplaceWith(expr); return; } switch (method.FullName) { case "System.Type.GetTypeFromHandle": if (arguments.Length == 1) { if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) { Expression target = ((MemberReferenceExpression)arguments[0]).Target; target.CopyInstructionsFrom(invocationExpression); invocationExpression.ReplaceWith(target); return; } } break; /* case "System.Reflection.FieldInfo.GetFieldFromHandle": // TODO : This is dead code because LdTokenAnnotation is not added anywhere: if (arguments.Length == 1) { MemberReferenceExpression mre = arguments[0] as MemberReferenceExpression; if (mre != null && mre.MemberName == "FieldHandle" && mre.Target.Annotation() != null) { invocationExpression.ReplaceWith(mre.Target); return; } } else if (arguments.Length == 2) { MemberReferenceExpression mre1 = arguments[0] as MemberReferenceExpression; MemberReferenceExpression mre2 = arguments[1] as MemberReferenceExpression; if (mre1 != null && mre1.MemberName == "FieldHandle" && mre1.Target.Annotation() != null) { if (mre2 != null && mre2.MemberName == "TypeHandle" && mre2.Target is TypeOfExpression) { Expression oldArg = ((InvocationExpression)mre1.Target).Arguments.Single(); FieldReference field = oldArg.Annotation(); if (field != null) { AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach(); oldArg.ReplaceWith(new MemberReferenceExpression(new TypeReferenceExpression(declaringType), field.Name).CopyAnnotationsFrom(oldArg)); invocationExpression.ReplaceWith(mre1.Target); return; } } } } break; */ case "System.Activator.CreateInstance": if (context.Settings.UseObjectCreationOfGenericTypeParameter && arguments.Length == 0 && method.TypeArguments.Count == 1 && IsInstantiableTypeParameter(method.TypeArguments[0])) { invocationExpression.ReplaceWith(new ObjectCreateExpression(context.TypeSystemAstBuilder.ConvertType(method.TypeArguments.First()))); } break; case "System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray": if (arguments.Length == 2 && context.Settings.Ranges) { var slicing = new IndexerExpression(arguments[0].Detach(), arguments[1].Detach()); slicing.CopyAnnotationsFrom(invocationExpression); invocationExpression.ReplaceWith(slicing); } break; } bool isChecked; BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name, out isChecked, context.Settings); if (bop != null && arguments.Length == 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression if (isChecked) { invocationExpression.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); } else if (HasCheckedEquivalent(method)) { invocationExpression.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); } invocationExpression.ReplaceWith( new BinaryOperatorExpression( arguments[0].UnwrapInDirectionExpression(), bop.Value, arguments[1].UnwrapInDirectionExpression() ).CopyAnnotationsFrom(invocationExpression) ); return; } UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(method.Name, out isChecked, context.Settings); if (uop != null && arguments.Length == 1) { if (isChecked) { invocationExpression.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); } else if (HasCheckedEquivalent(method)) { invocationExpression.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); } if (uop == UnaryOperatorType.Increment || uop == UnaryOperatorType.Decrement) { // `op_Increment(a)` is not equivalent to `++a`, // because it doesn't assign the incremented value to a. if (method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { // Legacy csc optimizes "d + 1m" to "op_Increment(d)", // so reverse that optimization here: invocationExpression.ReplaceWith( new BinaryOperatorExpression( arguments[0].UnwrapInDirectionExpression().Detach(), (uop == UnaryOperatorType.Increment ? BinaryOperatorType.Add : BinaryOperatorType.Subtract), new PrimitiveExpression(1m) ).CopyAnnotationsFrom(invocationExpression) ); } } else { arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( new UnaryOperatorExpression(uop.Value, arguments[0].UnwrapInDirectionExpression()).CopyAnnotationsFrom(invocationExpression) ); } return; } if (method.Name is "op_Explicit" or "op_CheckedExplicit" && arguments.Length == 1) { arguments[0].Remove(); // detach argument if (method.Name == "op_CheckedExplicit") { invocationExpression.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); } else if (HasCheckedEquivalent(method)) { invocationExpression.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); } invocationExpression.ReplaceWith( new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0].UnwrapInDirectionExpression()) .CopyAnnotationsFrom(invocationExpression) ); return; } if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) { invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression()); return; } return; } internal static bool HasCheckedEquivalent(IMethod method) { string name = method.Name; if (name.StartsWith("op_", StringComparison.Ordinal)) name = "op_Checked" + name.Substring(3); return method.DeclaringType.GetMethods(m => m.IsOperator && m.Name == name).Any(); } bool IsInstantiableTypeParameter(IType type) { return type is ITypeParameter tp && tp.HasDefaultConstructorConstraint; } bool CheckArgumentsForStringConcat(Expression[] arguments) { if (arguments.Length < 2) return false; if (arguments.Any(arg => arg is NamedArgumentExpression)) return false; // The evaluation order when the object.ToString() calls happen is a mess: // The C# spec says the evaluation for order for each individual string + should be: // * evaluate left argument // * evaluate right argument // * call ToString() on object argument // What actually happens pre-VS2019.3: // * evaluate all arguments in chain of + operators from left to right // * call ToString() on all object arguments from left to right // What happens in VS2019.3: // * for each argument in chain of + operators fom left to right: // * evaluate argument // * call ToString() on object argument // See https://github.com/dotnet/roslyn/issues/38641 for details. // To ensure the decompiled code's behavior matches the original IL behavior, // no matter which compiler is used to recompile it, we require that all // implicit ToString() calls except for the last are free of side effects. foreach (var arg in arguments.SkipLast(1)) { if (!ToStringIsKnownEffectFree(arg.GetResolveResult().Type)) { return false; } } foreach (var arg in arguments) { var rr = arg.GetResolveResult(); if (rr is InvocationResolveResult irr && IsStringConcat(irr.Member)) { // Roslyn + mcs also flatten nested string.Concat() invocations within a operator+ use, // which causes it to use the incorrect evaluation order despite the code using an // explicit string.Concat() call. // This problem is avoided if the outer call remains string.Concat() as well. return false; } if (rr.Type.IsByRefLike) { // ref structs cannot be converted to object for use with + return false; } } // One of the first two arguments must be string, otherwise the + operator // won't resolve to a string concatenation. return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String); } private bool IsStringConcat(IParameterizedMember member) { return member is IMethod method && method.Name == "Concat" && method.DeclaringType.IsKnownType(KnownTypeCode.String); } static readonly Pattern ToStringCallPattern = new Choice { // target.ToString() new InvocationExpression(new MemberReferenceExpression(new AnyNode("target"), "ToString")).WithName("call"), // target?.ToString() new UnaryOperatorExpression( UnaryOperatorType.NullConditionalRewrap, new InvocationExpression( new MemberReferenceExpression( new UnaryOperatorExpression(UnaryOperatorType.NullConditional, new AnyNode("target")), "ToString") ).WithName("call") ).WithName("nullConditional") }; internal static Expression RemoveRedundantToStringInConcat(Expression expr, IMethod concatMethod, bool isLastArgument) { var m = ToStringCallPattern.Match(expr); if (!m.Success) return expr; if (!concatMethod.Parameters.All(IsStringParameter)) { // If we're using a string.Concat() overload involving object parameters, // string.Concat() itself already calls ToString() so the C# compiler shouldn't // generate additional ToString() calls in this case. return expr; } var toStringMethod = m.Get("call").Single().GetSymbol() as IMethod; var target = m.Get("target").Single(); var type = target.GetResolveResult().Type; if (!(isLastArgument || ToStringIsKnownEffectFree(type))) { // ToString() order of evaluation matters, see CheckArgumentsForStringConcat(). return expr; } if (type.IsReferenceType != false && !m.Has("nullConditional")) { // ToString() might throw NullReferenceException, but the builtin operator+ doesn't. return expr; } if (!ToStringIsKnownEffectFree(type) && toStringMethod != null && IL.Transforms.ILInlining.MethodRequiresCopyForReadonlyLValue(toStringMethod)) { // ToString() on a struct may mutate the struct. // For operator+ the C# compiler creates a temporary copy before implicitly calling ToString(), // whereas an explicit ToString() call would mutate the original lvalue. // So we can't remove the compiler-generated ToString() call in cases where this might make a difference. return expr; } // All checks succeeded, we can eliminate the ToString() call. // The C# compiler will generate an equivalent call if the code is recompiled. return target; bool IsStringParameter(IParameter p) { IType ty = p.Type; if (p.IsParams && ty.Kind == TypeKind.Array) ty = ((ArrayType)ty).ElementType; return ty.IsKnownType(KnownTypeCode.String); } } static bool ToStringIsKnownEffectFree(IType type) { type = NullableType.GetUnderlyingType(type); switch (type.GetDefinition()?.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.Char: case KnownTypeCode.SByte: case KnownTypeCode.Byte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: case KnownTypeCode.Int32: case KnownTypeCode.UInt32: case KnownTypeCode.Int64: case KnownTypeCode.UInt64: case KnownTypeCode.Single: case KnownTypeCode.Double: case KnownTypeCode.Decimal: case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: case KnownTypeCode.String: return true; default: return false; } } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name, out bool isChecked, DecompilerSettings settings) { isChecked = false; switch (name) { case "op_Addition": return BinaryOperatorType.Add; case "op_Subtraction": return BinaryOperatorType.Subtract; case "op_Multiply": return BinaryOperatorType.Multiply; case "op_Division": return BinaryOperatorType.Divide; case "op_CheckedAddition" when settings.CheckedOperators: isChecked = true; return BinaryOperatorType.Add; case "op_CheckedSubtraction" when settings.CheckedOperators: isChecked = true; return BinaryOperatorType.Subtract; case "op_CheckedMultiply" when settings.CheckedOperators: isChecked = true; return BinaryOperatorType.Multiply; case "op_CheckedDivision" when settings.CheckedOperators: isChecked = true; return BinaryOperatorType.Divide; case "op_Modulus": return BinaryOperatorType.Modulus; case "op_BitwiseAnd": return BinaryOperatorType.BitwiseAnd; case "op_BitwiseOr": return BinaryOperatorType.BitwiseOr; case "op_ExclusiveOr": return BinaryOperatorType.ExclusiveOr; case "op_LeftShift": return BinaryOperatorType.ShiftLeft; case "op_RightShift": return BinaryOperatorType.ShiftRight; case "op_UnsignedRightShift" when settings.UnsignedRightShift: return BinaryOperatorType.UnsignedShiftRight; case "op_Equality": return BinaryOperatorType.Equality; case "op_Inequality": return BinaryOperatorType.InEquality; case "op_LessThan": return BinaryOperatorType.LessThan; case "op_LessThanOrEqual": return BinaryOperatorType.LessThanOrEqual; case "op_GreaterThan": return BinaryOperatorType.GreaterThan; case "op_GreaterThanOrEqual": return BinaryOperatorType.GreaterThanOrEqual; default: return null; } } static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name, out bool isChecked, DecompilerSettings settings) { isChecked = false; switch (name) { case "op_LogicalNot": return UnaryOperatorType.Not; case "op_OnesComplement": return UnaryOperatorType.BitNot; case "op_UnaryNegation": return UnaryOperatorType.Minus; case "op_CheckedUnaryNegation" when settings.CheckedOperators: isChecked = true; return UnaryOperatorType.Minus; case "op_UnaryPlus": return UnaryOperatorType.Plus; case "op_Increment": return UnaryOperatorType.Increment; case "op_Decrement": return UnaryOperatorType.Decrement; case "op_CheckedIncrement" when settings.CheckedOperators: isChecked = true; return UnaryOperatorType.Increment; case "op_CheckedDecrement" when settings.CheckedOperators: isChecked = true; return UnaryOperatorType.Decrement; default: return null; } } static readonly Expression getMethodOrConstructorFromHandlePattern = new CastExpression(new Choice { new TypePattern(typeof(MethodInfo)), new TypePattern(typeof(ConstructorInfo)) }, new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(MethodBase)).ToType()), "GetMethodFromHandle"), new NamedNode("ldtokenNode", new MemberReferenceExpression(new LdTokenPattern("method").ToExpression(), "MethodHandle")), new OptionalNode(new MemberReferenceExpression(new TypeOfExpression(new AnyNode("declaringType")), "TypeHandle")) )); public override void VisitCastExpression(CastExpression castExpression) { base.VisitCastExpression(castExpression); // Handle methodof Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression); if (m.Success) { IMethod method = m.Get("method").Single().GetSymbol() as IMethod; if (m.Has("declaringType") && method != null) { Expression newNode = new MemberReferenceExpression(new TypeReferenceExpression(m.Get("declaringType").Single().Detach()), method.Name); newNode = new InvocationExpression(newNode, method.Parameters.Select(p => new TypeReferenceExpression(context.TypeSystemAstBuilder.ConvertType(p.Type)))); m.Get("method").Single().ReplaceWith(newNode); } castExpression.ReplaceWith(m.Get("ldtokenNode").Single().CopyAnnotationsFrom(castExpression)); } } void IAstTransform.Run(AstNode rootNode, TransformContext context) { try { this.context = context; rootNode.AcceptVisitor(this); } finally { this.context = null; } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Immutable; using System.Threading; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Parameters for IAstTransform. /// public class TransformContext { public readonly IDecompilerTypeSystem TypeSystem; public readonly CancellationToken CancellationToken; public readonly TypeSystemAstBuilder TypeSystemAstBuilder; public readonly DecompilerSettings Settings; internal readonly DecompileRun DecompileRun; readonly ITypeResolveContext decompilationContext; /// /// Returns the current member; or null if a whole type or module is being decompiled. /// public IMember CurrentMember => decompilationContext.CurrentMember; /// /// Returns the current type definition; or null if a module is being decompiled. /// public ITypeDefinition CurrentTypeDefinition => decompilationContext.CurrentTypeDefinition; /// /// Returns the module that is being decompiled. /// public IModule CurrentModule => decompilationContext.CurrentModule; /// /// Returns the max possible set of namespaces that will be used during decompilation. /// public IImmutableSet RequiredNamespacesSuperset => DecompileRun.Namespaces.ToImmutableHashSet(); internal TransformContext(IDecompilerTypeSystem typeSystem, DecompileRun decompileRun, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder) { this.TypeSystem = typeSystem; this.DecompileRun = decompileRun; this.decompilationContext = decompilationContext; this.TypeSystemAstBuilder = typeSystemAstBuilder; this.CancellationToken = decompileRun.CancellationToken; this.Settings = decompileRun.Settings; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs ================================================ // Copyright (c) 2025 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// This transform moves field initializers at the start of constructors to their respective field declarations /// and transforms this-/base-ctor calls in constructors to constructor initializers. /// public class TransformFieldAndConstructorInitializers : IAstTransform { /// /// Pattern for reference types: /// this..ctor(...); /// internal static readonly AstNode ThisCallClassPattern = new ExpressionStatement( new NamedNode("invocation", new InvocationExpression( new MemberReferenceExpression( new Choice { new NamedNode("target", new ThisReferenceExpression()), new NamedNode("target", new BaseReferenceExpression()), new CastExpression { Type = new AnyNode(), Expression = new Choice { new NamedNode("target", new ThisReferenceExpression()), new NamedNode("target", new BaseReferenceExpression()), } } }, ".ctor"), new Repeat(new AnyNode())) ) ); /// /// Pattern for value types: /// this = new TSelf(...); /// internal static readonly AstNode ThisCallStructPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("target", new ThisReferenceExpression()), new NamedNode("invocation", new ObjectCreateExpression(new AnyNode(), new Repeat(new AnyNode()))) ) ); internal static bool IsGeneratedPrimaryConstructorBackingField(IField field) { var name = field.Name; return name.StartsWith("<", StringComparison.Ordinal) && name.EndsWith(">P", StringComparison.Ordinal) && field.IsCompilerGenerated(); } enum InitializerKind { Static, Instance, Primary } class InitializerSequence { static readonly ExpressionStatement memberInitializerPattern = new() { Expression = new AssignmentExpression( new Choice { new NamedNode("fieldAccess", new MemberReferenceExpression { Target = new ThisReferenceExpression(), MemberName = Pattern.AnyString }), new NamedNode("fieldAccess", new IdentifierExpression(Pattern.AnyString)) }, new AnyNode("initializer") ) }; [AllowNull] public List<(Statement Statement, IMember Member, Expression Initializer, bool DependsOnConstructorBody)> Statements; public Dictionary>? StatementToOtherCtorsMap; public bool HasDuplicateAssignments { get; private set; } public bool IsUnsafe { get; private set; } public bool CoversFullBody { get; private set; } public static InitializerSequence? Analyze(ConstructorInitializerAnalyzer context, ConstructorDeclaration ctor, IMethod ctorMethod) { var sequence = new InitializerSequence() { Statements = [], IsUnsafe = ctor.HasModifier(Modifiers.Unsafe) }; var initializedMembers = new HashSet(); var function = ctor.Annotation(); var isStruct = ctorMethod.DeclaringType.Kind == TypeKind.Struct; bool onlyMoveConstants = !context.context.Settings.AlwaysMoveInitializer && !context.IsBeforeFieldInit && ctorMethod.IsStatic; bool skippedStmts = false; Statement? stmt; for (stmt = ctor.Body.Statements.FirstOrDefault(); stmt != null; stmt = stmt.GetNextStatement()) { var m = memberInitializerPattern.Match(stmt); if (!m.Success) break; var fieldAccess = m.Get("fieldAccess").Single(); var initializer = m.Get("initializer").Single(); var member = (fieldAccess.GetSymbol() as IMember)?.MemberDefinition; if (member == null || !CanHaveInitializer(member, context)) break; if (onlyMoveConstants && member is not IField { IsConst: true }) { skippedStmts = true; continue; } if (!initializedMembers.Add(member)) sequence.HasDuplicateAssignments = true; bool dependsOnBody = false; foreach (var instruction in initializer.Annotations.OfType()) { foreach (var inst in instruction.Descendants) { if (inst is IInstructionWithVariableOperand { Variable: var v } && v.Function == function && v.Kind == VariableKind.Parameter) { dependsOnBody = true; } } } sequence.Statements.Add((stmt, member, initializer, dependsOnBody)); } if (!skippedStmts) { if (stmt == null) { sequence.CoversFullBody = true; } else { var m = isStruct ? ThisCallStructPattern.Match(stmt) : ThisCallClassPattern.Match(stmt); if (m.Success) { sequence.CoversFullBody = stmt.GetNextStatement() == null; } } } return sequence; } private static bool CanHaveInitializer(IMember member, ConstructorInitializerAnalyzer context) { if (context.MemberToDeclaringSyntaxNodeMap == null) return false; if (!context.MemberToDeclaringSyntaxNodeMap.TryGetValue(member, out var declaringSyntaxNode)) return true; return declaringSyntaxNode is FieldDeclaration or PropertyDeclaration { IsAutomaticProperty: true } or EventDeclaration; } public bool IsMatch(ConstructorDeclaration ctor) { var stmts = ctor.Body.Statements; var otherStmt = stmts.FirstOrDefault(); foreach (var (stmt, member, initializer, _) in Statements) { var m = memberInitializerPattern.Match(otherStmt); if (!m.Success) return false; Debug.Assert(otherStmt != null); // because m.Success var fieldAccess = m.Get("fieldAccess").Single(); var otherMember = (fieldAccess.GetSymbol() as IMember)?.MemberDefinition; if (!member.Equals(otherMember)) return false; var otherInitializer = m.Get("initializer").Single(); if (!initializer.IsMatch(otherInitializer)) return false; StatementToOtherCtorsMap ??= []; if (!StatementToOtherCtorsMap.TryGetValue(stmt, out var list)) { list = []; StatementToOtherCtorsMap[stmt] = list; } list.Add(otherStmt); otherStmt = otherStmt.GetNextStatement(); } return true; } } class ConstructorInitializerAnalyzer { internal readonly TransformContext context; public readonly ITypeDefinition TypeDefinition; public readonly TypeDeclaration? TypeDeclaration; public readonly RecordDecompiler? RecordDecompiler; [AllowNull] public Dictionary MemberToDeclaringSyntaxNodeMap; public Dictionary? PrimaryConstructorParameterToBackingStoreMap; public Dictionary? BackingFieldToPrimaryConstructorParameterVariableMap; public InitializerSequence? InstanceInitializers; public InitializerSequence? StaticInitializers; public InitializerSequence? PrimaryConstructorInitializers; public IMethod? StaticConstructor; public ConstructorDeclaration? StaticConstructorDecl; public IMethod? RecordCopyConstructor; public ConstructorDeclaration? RecordCopyConstructorDecl; public IMethod? PrimaryConstructor; public ConstructorDeclaration? PrimaryConstructorDecl; [AllowNull] public ConstructorDeclaration[] InstanceConstructors; public bool IsBeforeFieldInit { get { if (TypeDefinition?.MetadataToken.IsNil != false) return false; var metadata = context.TypeSystem.MainModule.MetadataFile.Metadata; var td = metadata.GetTypeDefinition((SRM.TypeDefinitionHandle)TypeDefinition.MetadataToken); return td.HasFlag(TypeAttributes.BeforeFieldInit); } } public ConstructorInitializerAnalyzer(TransformContext context, ITypeDefinition typeDefinition, TypeDeclaration? typeDeclaration) { this.context = context; TypeDefinition = typeDefinition; TypeDeclaration = typeDeclaration; RecordDecompiler = context.DecompileRun.RecordDecompilers.TryGetValue(typeDefinition, out var record) ? record : null; } public bool Analyze(IEnumerable members) { MemberToDeclaringSyntaxNodeMap = members .Select(m => (symbol: m.GetSymbol(), entity: (EntityDeclaration)m)) .Where(_ => _.symbol is IMember) .ToDictionary(_ => (IMember)_.symbol, _ => _.entity); List constructorsNotChainedWithThis = []; List allCtors = []; // we use the row number of the first method in the metadata table as a heuristic // to determine the primary constructor in non-record structs. // This is not completely reliable, but works. var metadata = context.TypeSystem.MainModule.metadata; var typeDef = metadata.GetTypeDefinition((SRM.TypeDefinitionHandle)TypeDefinition.MetadataToken); var firstMethodRowNumber = MetadataTokens.GetRowNumber(typeDef.GetMethods().FirstOrDefault()); foreach (var ctor in members.OfType()) { var ctorMethod = (IMethod)ctor.GetSymbol(); Debug.Assert(ctorMethod.IsConstructor); Debug.Assert(ctorMethod.MetadataToken.IsNil == false); int rowNumber = MetadataTokens.GetRowNumber(ctorMethod.MetadataToken); Debug.Assert(rowNumber > 0); if (ctorMethod.Equals(RecordDecompiler?.PrimaryConstructor)) { Debug.Assert(PrimaryConstructorDecl == null); PrimaryConstructor = ctorMethod; PrimaryConstructorDecl = ctor; PrimaryConstructorParameterToBackingStoreMap = RecordDecompiler.GetParameterToBackingStoreMap(); constructorsNotChainedWithThis.Add(ctor); } else if (ctorMethod.IsStatic) { Debug.Assert(StaticConstructorDecl == null); StaticConstructor = ctorMethod; StaticConstructorDecl = ctor; } else if (RecordDecompiler != null && RecordDecompiler.IsCopyConstructor(ctorMethod)) { Debug.Assert(RecordCopyConstructorDecl == null); RecordCopyConstructor = ctorMethod; RecordCopyConstructorDecl = ctor; } else { // find this-ctor call var stmt = ctor.Body.Statements.FirstOrDefault(); var m = ctorMethod.DeclaringType.Kind == TypeKind.Struct ? ThisCallStructPattern.Match(stmt) : ThisCallClassPattern.Match(stmt); allCtors.Add(ctor); if (m.Success && m.Get("target").Single() is ThisReferenceExpression) continue; constructorsNotChainedWithThis.Add(ctor); } } if (context.Settings.UsePrimaryConstructorSyntaxForNonRecordTypes && RecordDecompiler == null && constructorsNotChainedWithThis.Count == 1) { Debug.Assert(PrimaryConstructor == null); // this constructor could be converted to a primary constructor var ctor = constructorsNotChainedWithThis[0]; var ctorMethod = (IMethod)constructorsNotChainedWithThis[0].GetSymbol(); var initializer = InitializerSequence.Analyze(this, ctor, ctorMethod); if (initializer is { CoversFullBody: true, HasDuplicateAssignments: false, Statements.Count: > 0 }) { bool transformToPrimaryConstructor = MetadataTokens.GetRowNumber(ctorMethod.MetadataToken) == firstMethodRowNumber; if (ctorMethod.Parameters.Count == 0) { transformToPrimaryConstructor = false; } foreach (var (stmt, member, expr, dependsOnBody) in initializer.Statements) { if (member is IField f && IsGeneratedPrimaryConstructorBackingField(f)) { var variable = expr.Annotation()?.Variable; if (variable is { Kind: VariableKind.Parameter, Index: >= 0 and int index }) { var p = ctorMethod.Parameters[index]; PrimaryConstructorParameterToBackingStoreMap ??= []; BackingFieldToPrimaryConstructorParameterVariableMap ??= []; PrimaryConstructorParameterToBackingStoreMap[p] = (null, f); BackingFieldToPrimaryConstructorParameterVariableMap[f] = variable; transformToPrimaryConstructor = true; } } } if (context.Settings.ShowXmlDocumentation && context.DecompileRun.DocumentationProvider is { } provider) { var classDoc = provider.GetDocumentation(ctorMethod.DeclaringTypeDefinition); var ctorDoc = provider.GetDocumentation(ctorMethod); if (ctorDoc != null && ctorDoc != classDoc) { transformToPrimaryConstructor = false; } } if (transformToPrimaryConstructor) { PrimaryConstructorParameterToBackingStoreMap ??= []; PrimaryConstructorDecl = ctor; PrimaryConstructor = ctorMethod; PrimaryConstructorInitializers = initializer; } } } if (StaticConstructor != null) { StaticInitializers = InitializerSequence.Analyze(this, StaticConstructorDecl!, StaticConstructor); } if (PrimaryConstructor != null) { Debug.Assert(PrimaryConstructorDecl != null); // if there exists a primary constructor, all other constructors must call it Debug.Assert(constructorsNotChainedWithThis.Count == 1); PrimaryConstructorInitializers ??= InitializerSequence.Analyze(this, PrimaryConstructorDecl, PrimaryConstructor); } if (constructorsNotChainedWithThis.Count > 0) { if (TypeDefinition?.Kind != TypeKind.Struct || (context.Settings.StructDefaultConstructorsAndFieldInitializers && !TypeDefinition.IsRecord)) { bool isPrimaryCtor = constructorsNotChainedWithThis[0] == PrimaryConstructorDecl; var sequence = isPrimaryCtor ? PrimaryConstructorInitializers : InitializerSequence.Analyze(this, constructorsNotChainedWithThis[0], (IMethod)constructorsNotChainedWithThis[0].GetSymbol()); if (sequence == null) return false; for (int i = 1; i < constructorsNotChainedWithThis.Count; i++) { if (!sequence.IsMatch(constructorsNotChainedWithThis[i])) return false; } if (!isPrimaryCtor) { if (!sequence.Statements.Any(s => s.DependsOnConstructorBody)) InstanceInitializers = sequence; } } } InstanceConstructors = allCtors.ToArray(); return true; } public bool MoveConstructorInitializer(ConstructorDeclaration constructorDeclaration, IMethod ctorMethod) { Statement stmt = constructorDeclaration.Body.Statements.FirstOrDefault()!; var isValueType = ctorMethod.DeclaringType.Kind == TypeKind.Struct; // value types may omit the constructor initializer completely if (stmt == null && isValueType) { return true; } var m = isValueType ? ThisCallStructPattern.Match(stmt) : ThisCallClassPattern.Match(stmt); if (!m.Success) return isValueType; Debug.Assert(stmt != null); // because m.Success AstNode invocation = m.Get("invocation").Single(); if (invocation.GetSymbol() is not IMethod { IsConstructor: true } ctor) return false; ConstructorInitializerType type = ctor.DeclaringTypeDefinition == ctorMethod.DeclaringTypeDefinition ? ConstructorInitializerType.This : ConstructorInitializerType.Base; var ci = new ConstructorInitializer { ConstructorInitializerType = type }; // Move arguments from invocation to initializer: invocation.GetChildrenByRole(Roles.Argument).MoveTo(ci.Arguments); // Add the initializer: (unless it is the default 'base()') if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(invocation); // Remove the statement stmt.Remove(); return true; } public bool MoveFieldInitializersToDeclarations(InitializerSequence sequence, InitializerKind kind) { foreach (var (stmt, member, initializer, dependsOnBody) in sequence.Statements) { Debug.Assert(!dependsOnBody || kind is InitializerKind.Primary); if (!MemberToDeclaringSyntaxNodeMap.TryGetValue(member, out var declaringSyntaxNode)) { Debug.Assert(kind is InitializerKind.Primary); stmt.Remove(); continue; } VariableInitializer v; switch (declaringSyntaxNode) { case FieldDeclaration fd: v = fd.Variables.Single(); if (v.Initializer.IsNull) { v.Initializer = initializer.Detach(); } else if (kind == InitializerKind.Static) { // decimal constants already have an initializer in the AST at this point, // because it was added in CSharpDecompiler.DoDecompile(IField, ...) var constant = v.Initializer.GetResolveResult(); var expression = initializer.GetResolveResult(); if (constant.IsCompileTimeConstant && TryEvaluateDecimalConstant(expression, out decimal value)) { // decimal values do not match, skip transformation? if (!value.Equals(constant.ConstantValue)) continue; } else { // already has an initializer - do not modify Debug.Fail("Field already has an initializer"); } } else { // already has an initializer - do not modify Debug.Fail("Field already has an initializer"); } break; case PropertyDeclaration pd: Debug.Assert(pd.IsAutomaticProperty); if (pd.Initializer.IsNull) { pd.Initializer = initializer.Detach(); } else { // already has an initializer - do not modify Debug.Fail("Property already has an initializer"); } break; case EventDeclaration ev: v = ev.Variables.Single(); if (v.Initializer.IsNull) { v.Initializer = initializer.Detach(); } else { // already has an initializer - do not modify Debug.Fail("Event already has an initializer"); } break; default: // cannot move initializer continue; } // Remove the statement from all constructors stmt.Remove(); if (sequence.StatementToOtherCtorsMap != null && sequence.StatementToOtherCtorsMap.TryGetValue(stmt, out var otherStmts)) { foreach (var otherStmt in otherStmts) { otherStmt.Remove(); } } if (sequence.IsUnsafe && IntroduceUnsafeModifier.IsUnsafe(initializer)) { declaringSyntaxNode.Modifiers |= Modifiers.Unsafe; } } return true; } public void RemoveImplicitConstructor() { Debug.Assert(MemberToDeclaringSyntaxNodeMap != null); Debug.Assert(TypeDefinition != null); // We do not want to hide the constructor if the user explicitly selected it in the tree view. if (TypeDeclaration == null) return; // Remove primary constructor body if (PrimaryConstructor != null) { Debug.Assert(PrimaryConstructorDecl != null); this.TypeDeclaration.HasPrimaryConstructor = PrimaryConstructor.Parameters.Any() || !PrimaryConstructorDecl.Initializer.IsNull || TypeDefinition.Kind == TypeKind.Struct; // HACK: because our current AST model doesn't allow specifying an explicit order of roles, // we have to explicitly insert the primary constructor parameters, // MoveTo would just append the parameters to the list of children if (PrimaryConstructorDecl.Parameters.Count > 0) { var insertionPoint = (AstNode?)this.TypeDeclaration.TypeParameters.LastOrDefault() ?? this.TypeDeclaration.NameToken; foreach (var param in PrimaryConstructorDecl.Parameters) { param.Remove(); this.TypeDeclaration.InsertChildAfter(insertionPoint, param, Roles.Parameter); insertionPoint = param; } } Debug.Assert(PrimaryConstructorParameterToBackingStoreMap != null); foreach (var pd in this.TypeDeclaration.PrimaryConstructorParameters) { var v = pd.Annotation()?.Variable; Debug.Assert(v?.Index >= 0); var p = PrimaryConstructor.Parameters[v.Index.Value]; if (!PrimaryConstructorParameterToBackingStoreMap.TryGetValue(p, out var backingStore)) { // no backing store, constructor parameter was left unassigned or // assigned to a different member in a sub-expression: // private int someField = param + param2; continue; } var (prop, field) = backingStore; if (prop != null) { var attributes = prop?.GetAttributes().Select(attr => context.TypeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); if (attributes?.Length > 0) { var section = new AttributeSection { AttributeTarget = "property" }; section.Attributes.AddRange(attributes); pd.Attributes.Add(section); } } if (field != null) { var attributes = field.GetAttributes() .Where(a => !PatternStatementTransform.attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName)) .Select(attr => context.TypeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); if (attributes.Length > 0) { var section = new AttributeSection { AttributeTarget = "field" }; section.Attributes.AddRange(attributes); pd.Attributes.Add(section); } } } if (PrimaryConstructorDecl.HasModifier(Modifiers.Unsafe)) { this.TypeDeclaration.Modifiers |= Modifiers.Unsafe; } if (!PrimaryConstructorDecl.Initializer.IsNull && TypeDeclaration is { BaseTypes.Count: > 0 }) { Debug.Assert(PrimaryConstructorDecl.Initializer is { ConstructorInitializerType: ConstructorInitializerType.Base }); var baseType = TypeDeclaration.BaseTypes.First(); var newBaseType = new InvocationAstType(); baseType.ReplaceWith(newBaseType); newBaseType.BaseType = baseType; PrimaryConstructorDecl.Initializer.GetChildrenByRole(Roles.Argument).MoveTo(newBaseType.Arguments); } PrimaryConstructorDecl.Remove(); } // Remove static constructor if (StaticConstructor != null) { Debug.Assert(StaticConstructorDecl != null); if (IsBeforeFieldInit && StaticConstructorDecl.Body.Statements.Count == 0) { StaticConstructorDecl.Remove(); } } // More than one constructor - do not remove anything if (InstanceConstructors.Length != 1) return; var ctor = InstanceConstructors[0]; var ctorMethod = (IMethod)ctor.GetSymbol(); if (TypeDefinition.Kind == TypeKind.Struct && ctorMethod.Parameters.Count == 0 && InstanceInitializers != null) { // struct constructor with initializers is not optional return; } // dynamically create a pattern of an empty ctor ConstructorDeclaration emptyCtorPattern = new ConstructorDeclaration(); emptyCtorPattern.Modifiers = TypeDefinition.IsAbstract ? Modifiers.Protected : Modifiers.Public; if (ctor.HasModifier(Modifiers.Unsafe)) emptyCtorPattern.Modifiers |= Modifiers.Unsafe; emptyCtorPattern.Body = new BlockStatement(); if (emptyCtorPattern.IsMatch(ctor)) { bool retainBecauseOfDocumentation = context.Settings.ShowXmlDocumentation && context.DecompileRun.DocumentationProvider?.GetDocumentation(ctorMethod) != null; if (!retainBecauseOfDocumentation) ctor.Remove(); } } /// /// Evaluates a call to the decimal-ctor. /// private static bool TryEvaluateDecimalConstant(Semantics.ResolveResult expression, out decimal value) { value = 0; if (!expression.Type.IsKnownType(KnownTypeCode.Decimal)) { return false; } switch (expression) { case CSharpInvocationResolveResult rr: if (!(rr.GetSymbol() is IMethod { SymbolKind: SymbolKind.Constructor } ctor)) return false; var args = rr.GetArgumentsForCall(); if (args.Count == 1) { switch (args[0].ConstantValue) { case double d: value = new decimal(d); return true; case float f: value = new decimal(f); return true; case long l: value = new decimal(l); return true; case int i: value = new decimal(i); return true; case ulong ul: value = new decimal(ul); return true; case uint ui: value = new decimal(ui); return true; case int[] bits when bits.Length == 4 && (bits[3] & 0x7F00FFFF) == 0 && (bits[3] & 0xFF000000) <= 0x1C000000: value = new decimal(bits); return true; default: return false; } } else if (args.Count == 5 && args[0].ConstantValue is int lo && args[1].ConstantValue is int mid && args[2].ConstantValue is int hi && args[3].ConstantValue is bool isNegative && args[4].ConstantValue is byte scale) { value = new decimal(lo, mid, hi, isNegative, scale); return true; } return false; default: if (expression.ConstantValue is decimal v) { value = v; return true; } return false; } } } [AllowNull] TransformContext context; public void Run(AstNode node, TransformContext context) { this.context = context; if (context.CurrentTypeDefinition != null) { TransformDeclaration(context.CurrentTypeDefinition, node, node.Children.OfType()); } foreach (var typeDeclaration in node.Descendants.OfType()) { var currentTypeDefinition = (ITypeDefinition)typeDeclaration.GetSymbol(); TransformDeclaration(currentTypeDefinition, typeDeclaration, typeDeclaration.Members); } } private bool TransformDeclaration(ITypeDefinition currentTypeDefinition, AstNode node, IEnumerable members) { var analyzer = new ConstructorInitializerAnalyzer(context, currentTypeDefinition, node as TypeDeclaration); if (!analyzer.Analyze(members)) return false; if (analyzer.PrimaryConstructorInitializers is { HasDuplicateAssignments: false }) { analyzer.MoveFieldInitializersToDeclarations(analyzer.PrimaryConstructorInitializers, InitializerKind.Primary); } else if (analyzer.InstanceInitializers is { HasDuplicateAssignments: false }) { analyzer.MoveFieldInitializersToDeclarations(analyzer.InstanceInitializers, InitializerKind.Instance); } if (analyzer.StaticInitializers is { HasDuplicateAssignments: false }) { analyzer.MoveFieldInitializersToDeclarations(analyzer.StaticInitializers, InitializerKind.Static); } foreach (var constructorDeclaration in members.OfType()) { analyzer.MoveConstructorInitializer(constructorDeclaration, (IMethod)constructorDeclaration.GetSymbol()); } analyzer.RemoveImplicitConstructor(); if (analyzer.BackingFieldToPrimaryConstructorParameterVariableMap == null) { return false; } foreach (Identifier identifier in node.Descendants.OfType()) { if (identifier.Parent?.GetSymbol() is not IField field) { continue; } if (!analyzer.BackingFieldToPrimaryConstructorParameterVariableMap.TryGetValue((IField)field.MemberDefinition, out var v)) { continue; } identifier.Parent.RemoveAnnotations(); identifier.Parent.AddAnnotation(new ILVariableResolveResult(v)); identifier.ReplaceWith(Identifier.Create(v.Name)); } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { /// /// Helper struct so that the compiler can ensure we don't forget both the ILInstruction annotation and the ResolveResult annotation. /// Use '.WithILInstruction(...)' or '.WithoutILInstruction()' to create an instance of this struct. /// struct ExpressionWithILInstruction { public readonly Expression Expression; public IEnumerable ILInstructions { get { return Expression.Annotations.OfType(); } } internal ExpressionWithILInstruction(Expression expression) { Debug.Assert(expression != null); this.Expression = expression; } public static implicit operator Expression(ExpressionWithILInstruction expression) { return expression.Expression; } } /// /// Helper struct so that the compiler can ensure we don't forget both the ILInstruction annotation and the ResolveResult annotation. /// Use '.WithRR(...)'. /// struct ExpressionWithResolveResult { public readonly Expression Expression; // Because ResolveResult is frequently accessed within the ExpressionBuilder, we put it directly // in this struct instead of accessing it through the list of annotations. public readonly ResolveResult ResolveResult; public IType Type { get { return ResolveResult.Type; } } internal ExpressionWithResolveResult(Expression expression) { Debug.Assert(expression != null); this.Expression = expression; this.ResolveResult = expression.Annotation() ?? ErrorResolveResult.UnknownError; } internal ExpressionWithResolveResult(Expression expression, ResolveResult resolveResult) { Debug.Assert(expression != null && resolveResult != null); Debug.Assert(expression.Annotation() == resolveResult); this.Expression = expression; this.ResolveResult = resolveResult; } public static implicit operator Expression(ExpressionWithResolveResult expression) { return expression.Expression; } } /// /// Output of C# ExpressionBuilder -- a decompiled C# expression that has both a resolve result and ILInstruction annotation. /// /// /// The resolve result is also always available as annotation on the expression, but having /// TranslatedExpression as a separate type is still useful to ensure that no case in the expression builder /// forgets to add the annotation. /// [DebuggerDisplay("{Expression} : {ResolveResult}")] struct TranslatedExpression { public readonly Expression Expression; // Because ResolveResult is frequently accessed within the ExpressionBuilder, we put it directly // in this struct instead of accessing it through the list of annotations. public readonly ResolveResult ResolveResult; public IEnumerable ILInstructions { get { return Expression.Annotations.OfType(); } } public IType Type { get { return ResolveResult.Type; } } internal TranslatedExpression(Expression expression) { Debug.Assert(expression != null); this.Expression = expression; this.ResolveResult = expression.Annotation() ?? ErrorResolveResult.UnknownError; } internal TranslatedExpression(Expression expression, ResolveResult resolveResult) { Debug.Assert(expression != null && resolveResult != null); Debug.Assert(expression.Annotation() == resolveResult); this.ResolveResult = resolveResult; this.Expression = expression; } public static implicit operator Expression(TranslatedExpression expression) { return expression.Expression; } public static implicit operator ExpressionWithResolveResult(TranslatedExpression expression) { return new ExpressionWithResolveResult(expression.Expression, expression.ResolveResult); } public static implicit operator ExpressionWithILInstruction(TranslatedExpression expression) { return new ExpressionWithILInstruction(expression.Expression); } /// /// Returns a new TranslatedExpression that represents the specified descendant expression. /// All ILInstruction annotations from the current expression are copied to the descendant expression. /// The descendant expression is detached from the AST. /// public TranslatedExpression UnwrapChild(Expression descendant) { if (descendant == Expression) return this; for (AstNode parent = descendant.Parent; parent != null; parent = parent.Parent) { foreach (var inst in parent.Annotations.OfType()) descendant.AddAnnotation(inst); if (parent == Expression) return new TranslatedExpression(descendant.Detach()); } throw new ArgumentException("descendant must be a descendant of the current node"); } /// /// Adds casts (if necessary) to convert this expression to the specified target type. /// /// /// If the target type is narrower than the source type, the value is truncated. /// If the target type is wider than the source type, the value is sign- or zero-extended based on the /// sign of the source type. /// This fits with the ExpressionBuilder's post-condition, so e.g. an assignment can simply /// call Translate(stloc.Value).ConvertTo(stloc.Variable.Type) and have the overall C# semantics match the IL semantics. /// /// From the caller's perspective, IntPtr/UIntPtr behave like normal C# integers except that they have native int size. /// All the special cases necessary to make IntPtr/UIntPtr behave sanely are handled internally in ConvertTo(). /// /// Post-condition: /// The "expected evaluation result" is the value computed by this.Expression, /// converted to targetType via an IL conv instruction. /// /// ConvertTo(targetType, allowImplicitConversion=false).Type must be equal to targetType (modulo identity conversions). /// The value computed by the converted expression must match the "expected evaluation result". /// /// ConvertTo(targetType, allowImplicitConversion=true) must produce an expression that, /// when evaluated in a context where it will be implicitly converted to targetType, /// evaluates to the "expected evaluation result". /// public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false) { var type = this.Type; if (NormalizeTypeVisitor.IgnoreNullabilityAndTuples.EquivalentTypes(type, targetType)) { // Make explicit conversion implicit, if possible if (allowImplicitConversion) { switch (ResolveResult) { case ConversionResolveResult conversion: { if (Expression is CastExpression cast && CastCanBeMadeImplicit( Resolver.CSharpConversions.Get(expressionBuilder.compilation), conversion.Conversion, conversion.Input.Type, type, targetType )) { var result = this.UnwrapChild(cast.Expression); if (conversion.Conversion.IsUserDefined) { result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion)); } return result; } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) { return this.UnwrapChild(oce.Arguments.Single()); } break; } case InvocationResolveResult invocation: { if (Expression is ObjectCreateExpression oce && oce.Arguments.Count == 1 && invocation.Type.IsKnownType(KnownTypeCode.NullableOfT)) { return this.UnwrapChild(oce.Arguments.Single()); } break; } } } return this; } if (targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } else if (targetType.Kind == TypeKind.Unknown) { // don't attempt cast to '?', or casts between an unknown type and a known type with same name if (targetType.Name == "?" || targetType.ReflectionName == type.ReflectionName) { return this; } // However we still want explicit casts to types that are merely unresolved } var convAnnotation = this.Expression.Annotation(); if (convAnnotation != null) { // If an implicit user-defined conversion was stripped from this expression; // it needs to be re-introduced before we can apply other casts to this expression. // This happens when the CallBuilder discovers that the conversion is necessary in // order to choose the correct overload. this.Expression.RemoveAnnotations(); return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression) .WithoutILInstruction() .WithRR(convAnnotation.ConversionResolveResult) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } if (Expression is ThrowExpression && allowImplicitConversion) { return this; // Throw expressions have no type and are implicitly convertible to any type } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) { // Conversion of a tuple literal: convert element-wise var newTupleExpr = new TupleExpression(); var newElementRRs = new List(); // element names: discard existing names and use targetTupleType instead var newElementNames = targetTupleType.ElementNames; foreach (var (index, elementExpr, elementTargetType) in tupleExpr.Elements.ZipWithIndex(targetTupleType.ElementTypes)) { var newElementExpr = new TranslatedExpression((elementExpr is NamedArgumentExpression nae ? nae.Expression : elementExpr).Detach()) .ConvertTo(elementTargetType, expressionBuilder, checkForOverflow, allowImplicitConversion); if (newElementNames.IsDefaultOrEmpty || newElementNames.ElementAtOrDefault(index) is not string { Length: > 0 } name) { newTupleExpr.Elements.Add(newElementExpr.Expression); } else { newTupleExpr.Elements.Add(new NamedArgumentExpression(name, newElementExpr.Expression)); } newElementRRs.Add(newElementExpr.ResolveResult); } return newTupleExpr.WithILInstruction(this.ILInstructions) .WithRR(new TupleResolveResult( expressionBuilder.compilation, newElementRRs.ToImmutableArray(), valueTupleAssembly: targetTupleType.GetDefinition()?.ParentModule )); } var compilation = expressionBuilder.compilation; var conversions = Resolver.CSharpConversions.Get(compilation); if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 && !conv.Conversion.IsUserDefined && CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) { var unwrapped = this.UnwrapChild(cast2.Expression); if (allowImplicitConversion) return unwrapped; return unwrapped.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } if (Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && targetType.IsReferenceType == true) { // "(T)(x?).AccessChain" is invalid, but "((T)x)?.AccessChain" is valid and equivalent return new UnaryOperatorExpression( UnaryOperatorType.NullConditional, UnwrapChild(uoe.Expression).ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion) ).WithRR(new ResolveResult(targetType)).WithoutILInstruction(); } IType utype = NullableType.GetUnderlyingType(type); IType targetUType = NullableType.GetUnderlyingType(targetType); if (type.IsKnownType(KnownTypeCode.Boolean) && !targetUType.IsKnownType(KnownTypeCode.Boolean) && targetUType.GetStackType().IsIntegerType()) { // convert from boolean to integer (or enum) return new ConditionalExpression( this.Expression, LdcI4(compilation, 1).ConvertTo(targetType, expressionBuilder, checkForOverflow), LdcI4(compilation, 0).ConvertTo(targetType, expressionBuilder, checkForOverflow) ).WithoutILInstruction().WithRR(new ResolveResult(targetType)); } if (targetType.IsKnownType(KnownTypeCode.Boolean)) { // convert to boolean through byte, to simulate the truncation to 8 bits return this.ConvertTo(compilation.FindType(KnownTypeCode.Byte), expressionBuilder, checkForOverflow) .ConvertToBoolean(expressionBuilder); } // Special-case IntPtr and UIntPtr: they behave extremely weird, see IntPtr.txt for details. if (type.IsKnownType(KnownTypeCode.IntPtr)) { // Conversion from IntPtr // Direct cast only works correctly for IntPtr -> long. // IntPtr -> int works correctly only in checked context. // Everything else can be worked around by casting via long. if (!(targetType.IsKnownType(KnownTypeCode.Int64) || targetType.Kind == TypeKind.NInt || (checkForOverflow && targetType.IsKnownType(KnownTypeCode.Int32)) || targetType.Kind.IsAnyPointer() || targetType.Kind == TypeKind.ByReference)) { var convertVia = expressionBuilder.settings.NativeIntegers ? SpecialType.NInt : compilation.FindType(KnownTypeCode.Int64); return this.ConvertTo(convertVia, expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } } else if (type.IsKnownType(KnownTypeCode.UIntPtr)) { // Conversion from UIntPtr // Direct cast only works correctly for UIntPtr -> ulong. // UIntPtr -> uint works correctly only in checked context. // Everything else can be worked around by casting via ulong. if (!(targetType.IsKnownType(KnownTypeCode.UInt64) || targetType.Kind == TypeKind.NUInt || (checkForOverflow && targetType.IsKnownType(KnownTypeCode.UInt32)) || targetType.Kind.IsAnyPointer() || targetType.Kind == TypeKind.ByReference)) { var convertVia = expressionBuilder.settings.NativeIntegers ? SpecialType.NUInt : compilation.FindType(KnownTypeCode.UInt64); return this.ConvertTo(convertVia, expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } } if (targetUType.IsKnownType(KnownTypeCode.IntPtr) && utype.GetStackType().IsIntegerType()) { // Conversion to IntPtr if (type.IsKnownType(KnownTypeCode.Int32) || type.Kind == TypeKind.NInt) { // normal casts work for int/nint (both in checked and unchecked context) // note that pointers only allow normal casts in unchecked contexts } else if (expressionBuilder.settings.NativeIntegers) { // if native integer types are available, prefer using those return this.ConvertTo(SpecialType.NInt, expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } else if (checkForOverflow) { // if overflow-checking is enabled, we can simply cast via long: // (and long itself works directly in checked context) if (!type.IsKnownType(KnownTypeCode.Int64)) { return this.ConvertTo(compilation.FindType(KnownTypeCode.Int64), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } } else if (!type.Kind.IsAnyPointer()) { // If overflow-checking is disabled, the only way to truncate to native size // without throwing an exception in 32-bit mode is to use a pointer type. return this.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } } else if (targetUType.IsKnownType(KnownTypeCode.UIntPtr) && utype.GetStackType().IsIntegerType()) { // Conversion to UIntPtr if (type.IsKnownType(KnownTypeCode.UInt32) || type.Kind.IsAnyPointer() || type.Kind == TypeKind.NUInt) { // normal casts work for uint/nuint and pointers (both in checked and unchecked context) } else if (expressionBuilder.settings.NativeIntegers) { // if native integer types are available, prefer using those return this.ConvertTo(SpecialType.NUInt, expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } else if (checkForOverflow) { // if overflow-checking is enabled, we can simply cast via ulong: // (and ulong itself works directly in checked context) if (!type.IsKnownType(KnownTypeCode.UInt64)) { return this.ConvertTo(compilation.FindType(KnownTypeCode.UInt64), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } } else { // If overflow-checking is disabled, the only way to truncate to native size // without throwing an exception in 32-bit mode is to use a pointer type. return this.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } } if (targetType.Kind.IsAnyPointer() && type.Kind == TypeKind.Enum) { // enum to pointer: C# doesn't allow such casts // -> convert via underlying type return this.ConvertTo(type.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } else if (targetUType.Kind == TypeKind.Enum && type.Kind.IsAnyPointer()) { // pointer to enum: C# doesn't allow such casts // -> convert via underlying type return this.ConvertTo(targetUType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } if (targetType.Kind.IsAnyPointer() && type.IsKnownType(KnownTypeCode.Char) || targetUType.IsKnownType(KnownTypeCode.Char) && type.Kind.IsAnyPointer()) { // char <-> pointer: C# doesn't allow such casts // -> convert via ushort return this.ConvertTo(compilation.FindType(KnownTypeCode.UInt16), expressionBuilder, checkForOverflow) .ConvertTo(targetType, expressionBuilder, checkForOverflow); } if (targetType.Kind == TypeKind.Pointer && type.Kind == TypeKind.ByReference && Expression is DirectionExpression) { // convert from reference to pointer Expression arg = ((DirectionExpression)Expression).Expression.Detach(); var pointerType = new PointerType(((ByReferenceType)type).ElementType); if (arg is UnaryOperatorExpression argUOE && argUOE.Operator == UnaryOperatorType.Dereference) { // &*ptr -> ptr return new TranslatedExpression(argUOE).UnwrapChild(argUOE.Expression) .ConvertTo(targetType, expressionBuilder); } var pointerExpr = new UnaryOperatorExpression(UnaryOperatorType.AddressOf, arg) .WithILInstruction(this.ILInstructions) .WithRR(new ResolveResult(pointerType)); // perform remaining pointer cast, if necessary return pointerExpr.ConvertTo(targetType, expressionBuilder); } Expression expr; if (targetType.Kind == TypeKind.ByReference) { if (NormalizeTypeVisitor.TypeErasure.EquivalentTypes(targetType, this.Type)) { return this; } var elementType = ((ByReferenceType)targetType).ElementType; if (this.Expression is DirectionExpression thisDir && this.ILInstructions.Any(i => i.OpCode == OpCode.AddressOf) && thisDir.Expression.GetResolveResult()?.Type.GetStackType() == elementType.GetStackType()) { // When converting a reference to a temporary to a different type, // apply the cast to the temporary instead. var convertedTemp = this.UnwrapChild(thisDir.Expression).ConvertTo(elementType, expressionBuilder, checkForOverflow); return new DirectionExpression(FieldDirection.Ref, convertedTemp) .WithILInstruction(this.ILInstructions) .WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, ReferenceKind.Ref)); } if (this.Type.Kind == TypeKind.ByReference && !IsFixedVariable()) { // Convert between managed reference types. // We can't do this by going through a pointer type because that would temporarily stop GC tracking. // Instead, emit `ref Unsafe.As(ref expr)` return expressionBuilder.CallUnsafeIntrinsic("As", new[] { this.Expression }, typeArguments: new IType[] { ((ByReferenceType)this.Type).ElementType, elementType }, returnType: targetType); } // Convert from integer/pointer to reference. // First, convert to the corresponding pointer type: var arg = this.ConvertTo(new PointerType(elementType), expressionBuilder, checkForOverflow); ResolveResult elementRR; if (arg.Expression is UnaryOperatorExpression unary && unary.Operator == UnaryOperatorType.AddressOf) { // If we already have an address -> unwrap expr = arg.UnwrapChild(unary.Expression); elementRR = expr.GetResolveResult(); } else { // Otherwise dereference the pointer: expr = new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg.Expression); elementRR = new ResolveResult(elementType); expr.AddAnnotation(elementRR); } // And then take a reference: return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction() .WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); } if (this.ResolveResult.IsCompileTimeConstant && this.ResolveResult.ConstantValue != null && NullableType.IsNullable(targetType) && !utype.Equals(targetUType) && targetUType.GetStackType().IsIntegerType()) { // Casts like `(uint?)-1` are only valid in an explicitly unchecked context, but we // don't have logic to ensure such a context (usually we emit into an implicitly unchecked context). // This only applies with constants as input (int->uint? is fine in implicitly unchecked context). // We use an intermediate cast to the nullable's underlying type, which results // in a constant conversion, so the final output will be something like `(uint?)uint.MaxValue` return ConvertTo(targetUType, expressionBuilder, checkForOverflow, allowImplicitConversion: false) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult); if (rr.IsCompileTimeConstant && !rr.IsError) { var convertedResult = expressionBuilder.ConvertConstantValue(rr, allowImplicitConversion) .WithILInstruction(this.ILInstructions); if (convertedResult.Expression is PrimitiveExpression outputLiteral && this.Expression is PrimitiveExpression inputLiteral) { outputLiteral.Format = inputLiteral.Format; } return convertedResult; } else if (rr.IsError && targetType.IsReferenceType == true && type.IsReferenceType == true) { // Conversion between two reference types, but no direct cast allowed? cast via object // Just make sure we avoid infinite recursion even if the resolver falsely claims we can't cast directly: if (!(targetType.IsKnownType(KnownTypeCode.Object) || type.IsKnownType(KnownTypeCode.Object))) { return this.ConvertTo(compilation.FindType(KnownTypeCode.Object), expressionBuilder) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } } else if (type.Kind == TypeKind.Dynamic && targetType.IsReferenceType == true && !targetType.IsKnownType(KnownTypeCode.Object)) { // "static" conversion between dynamic and a reference type requires us to add a cast to object, // otherwise recompilation would produce a dynamic cast. // (T)dynamicExpression is a "dynamic" cast // (T)(object)dynamicExpression is a "static" cast // as "dynamic" casts are handled differently by ExpressionBuilder.VisitDynamicConvertInstruction // we can always insert the cast to object, if we encounter a conversion from any reference type to dynamic. return this.ConvertTo(compilation.FindType(KnownTypeCode.Object), expressionBuilder) .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); } if (targetType.Kind.IsAnyPointer() && (0.Equals(ResolveResult.ConstantValue) || 0u.Equals(ResolveResult.ConstantValue))) { if (allowImplicitConversion) { return new NullReferenceExpression() .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(SpecialType.NullType, null)); } return new CastExpression(expressionBuilder.ConvertType(targetType), new NullReferenceExpression()) .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(targetType, null)); } if (allowImplicitConversion) { if (conversions.ImplicitConversion(ResolveResult, targetType).IsValid) { return this; } } else { if (NormalizeTypeVisitor.IgnoreNullabilityAndTuples.EquivalentTypes(type, targetType)) { // avoid an explicit cast when types differ only in nullability of reference types return this; } } // BaseReferenceExpression must not be used with CastExpressions expr = Expression is BaseReferenceExpression ? new ThisReferenceExpression().WithILInstruction(this.ILInstructions) : Expression; var castExpr = new CastExpression(expressionBuilder.ConvertType(targetType), expr); bool needsCheckAnnotation = targetUType.GetStackType().IsIntegerType(); if (needsCheckAnnotation) { if (checkForOverflow) { castExpr.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); } else if (ResolveResult.IsCompileTimeConstant && targetUType.IsCSharpNativeIntegerType()) { // unchecked potentially-overflowing cast of constant to n(u)int: // Placement in implicitly unchecked context is not good enough when applied to compile-time constant, // the constant must be placed into an explicit unchecked block. // (note that non-potentially-overflowing casts will be handled by constant folding and won't get here) castExpr.AddAnnotation(AddCheckedBlocks.ExplicitUncheckedAnnotation); } else { castExpr.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); } } return castExpr.WithoutILInstruction().WithRR(rr); } bool IsFixedVariable() { if (this.Expression is DirectionExpression dirExpr) { var inst = dirExpr.Expression.Annotation(); return inst != null && PointerArithmeticOffset.IsFixedVariable(inst); } else { return false; } } /// /// Gets whether an implicit conversion from 'inputType' to 'newTargetType' /// would have the same semantics as the existing cast from 'inputType' to 'oldTargetType'. /// The existing cast is classified in 'conversion'. /// bool CastCanBeMadeImplicit(Resolver.CSharpConversions conversions, Conversion conversion, IType inputType, IType oldTargetType, IType newTargetType) { if (!conversion.IsImplicit) { // If the cast was required for the old conversion, avoid making it implicit. return false; } if (oldTargetType.Kind == TypeKind.NInt || oldTargetType.Kind == TypeKind.NUInt || newTargetType.Kind == TypeKind.NInt || newTargetType.Kind == TypeKind.NUInt) { // nint has identity conversion with IntPtr, but the two have different implicit conversions return false; } if (conversion.IsBoxingConversion) { return conversions.IsBoxingConversionOrInvolvingTypeParameter(inputType, newTargetType); } if (conversion.IsInterpolatedStringConversion) { return newTargetType.IsKnownType(KnownTypeCode.FormattableString) || newTargetType.IsKnownType(KnownTypeCode.IFormattable); } return conversions.IdentityConversion(oldTargetType, newTargetType); } TranslatedExpression LdcI4(ICompilation compilation, int val) { return new PrimitiveExpression(val) .WithoutILInstruction() .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), val)); } /// /// In conditional contexts, remove the bool-cast emitted when converting /// an "implicit operator bool" invocation. /// public TranslatedExpression UnwrapImplicitBoolConversion(Func typeFilter = null) { if (!this.Type.IsKnownType(KnownTypeCode.Boolean)) return this; if (!(this.ResolveResult is ConversionResolveResult rr)) return this; if (!(rr.Conversion.IsUserDefined && rr.Conversion.IsImplicit)) return this; if (typeFilter != null && !typeFilter(rr.Input.Type)) return this; if (this.Expression is CastExpression cast) { return this.UnwrapChild(cast.Expression); } return this; } /// /// Converts this expression to a boolean expression. /// /// Expects that the input expression is an integer expression; produces an expression /// that returns true iff the integer value is not 0. /// /// If negate is true, instead produces an expression that returns true iff the integer value is 0. /// public TranslatedExpression ConvertToBoolean(ExpressionBuilder expressionBuilder, bool negate = false) { if (Type.IsKnownType(KnownTypeCode.Boolean) || Type.Kind == TypeKind.Unknown) { if (negate) { return expressionBuilder.LogicNot(this).WithoutILInstruction(); } else { return this; } } Debug.Assert(Type.GetStackType().IsIntegerType()); IType boolType = expressionBuilder.compilation.FindType(KnownTypeCode.Boolean); if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is int) { bool val = (int)ResolveResult.ConstantValue != 0; val ^= negate; return new PrimitiveExpression(val) .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(boolType, val)); } else if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is byte) { bool val = (byte)ResolveResult.ConstantValue != 0; val ^= negate; return new PrimitiveExpression(val) .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(boolType, val)); } else if (Type.Kind == TypeKind.Pointer) { var nullRef = new NullReferenceExpression() .WithoutILInstruction() .WithRR(new ConstantResolveResult(SpecialType.NullType, null)); var op = negate ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality; return new BinaryOperatorExpression(Expression, op, nullRef.Expression) .WithoutILInstruction() .WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual, this.ResolveResult, nullRef.ResolveResult)); } else if (Type.Kind == TypeKind.Enum && Type.GetDefinition() is { } typeDef && typeDef.Fields.Any(f => f.GetConstantValue() is { } val && (ulong)CSharpPrimitiveCast.Cast(TypeCode.UInt64, val, false) == 0L)) { var zero = expressionBuilder .ConvertConstantValue(new ConstantResolveResult(Type, 0), allowImplicitConversion: true); var op = negate ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality; return new BinaryOperatorExpression(Expression, op, zero.Expression) .WithoutILInstruction() .WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual, this.ResolveResult, zero.ResolveResult)); } else { var zero = new PrimitiveExpression(0) .WithoutILInstruction() .WithRR(new ConstantResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.Int32), 0)); var op = negate ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality; return new BinaryOperatorExpression(Expression, op, zero.Expression) .WithoutILInstruction() .WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual, this.ResolveResult, zero.ResolveResult)); } } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/TranslatedStatement.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; namespace ICSharpCode.Decompiler.CSharp { [DebuggerDisplay("{Statement}")] struct TranslatedStatement { public readonly Statement Statement; public IEnumerable ILInstructions { get { return Statement.Annotations.OfType(); } } internal TranslatedStatement(Statement statement) { Debug.Assert(statement != null); this.Statement = statement; } public static implicit operator Statement(TranslatedStatement statement) { return statement.Statement; } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/TranslationContext.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp { /// /// Context struct passed in to ExpressionBuilder.Visit() methods. /// public struct TranslationContext { /// /// The expected type during ILAst->C# translation; or SpecialType.Unknown /// if no specific type is expected. /// public IType TypeHint; } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/TypeSystem/CSharpTypeResolveContext.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.TypeSystem { public sealed class CSharpTypeResolveContext : ITypeResolveContext { readonly IModule module; readonly UsingScope currentUsingScope; readonly ITypeDefinition currentTypeDefinition; readonly IMember currentMember; readonly string[] methodTypeParameterNames; public CSharpTypeResolveContext(IModule module, UsingScope usingScope = null, ITypeDefinition typeDefinition = null, IMember member = null) { if (module == null) throw new ArgumentNullException(nameof(module)); this.module = module; this.currentUsingScope = usingScope; this.currentTypeDefinition = typeDefinition; this.currentMember = member; } private CSharpTypeResolveContext(IModule module, UsingScope usingScope, ITypeDefinition typeDefinition, IMember member, string[] methodTypeParameterNames) { this.module = module; this.currentUsingScope = usingScope; this.currentTypeDefinition = typeDefinition; this.currentMember = member; this.methodTypeParameterNames = methodTypeParameterNames; } public UsingScope CurrentUsingScope { get { return currentUsingScope; } } public ICompilation Compilation { get { return module.Compilation; } } public IModule CurrentModule { get { return module; } } public ITypeDefinition CurrentTypeDefinition { get { return currentTypeDefinition; } } public IMember CurrentMember { get { return currentMember; } } public CSharpTypeResolveContext WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { return new CSharpTypeResolveContext(module, currentUsingScope, typeDefinition, currentMember, methodTypeParameterNames); } ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { return WithCurrentTypeDefinition(typeDefinition); } public CSharpTypeResolveContext WithCurrentMember(IMember member) { return new CSharpTypeResolveContext(module, currentUsingScope, currentTypeDefinition, member, methodTypeParameterNames); } ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member) { return WithCurrentMember(member); } public CSharpTypeResolveContext WithUsingScope(UsingScope usingScope) { return new CSharpTypeResolveContext(module, usingScope, currentTypeDefinition, currentMember, methodTypeParameterNames); } } } ================================================ FILE: ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; #nullable enable namespace ICSharpCode.Decompiler.CSharp.TypeSystem { /// /// Represents a scope that contains "using" statements. /// This is either the mo itself, or a namespace declaration. /// public class UsingScope { readonly CSharpTypeResolveContext parentContext; internal readonly ConcurrentDictionary ResolveCache = new ConcurrentDictionary(); internal List>? AllExtensionMethods; public UsingScope(CSharpTypeResolveContext context, INamespace @namespace, ImmutableArray usings) { this.parentContext = context ?? throw new ArgumentNullException(nameof(context)); this.Usings = usings; this.Namespace = @namespace ?? throw new ArgumentNullException(nameof(@namespace)); } public INamespace Namespace { get; } public UsingScope Parent { get { return parentContext.CurrentUsingScope; } } public ImmutableArray Usings { get; } public IReadOnlyList> UsingAliases => []; public IReadOnlyList ExternAliases => []; /// /// Gets whether this using scope has an alias (either using or extern) /// with the specified name. /// public bool HasAlias(string identifier) => false; internal UsingScope WithNestedNamespace(string simpleName) { var ns = Namespace.GetChildNamespace(simpleName) ?? new DummyNamespace(Namespace, simpleName); return new UsingScope( parentContext.WithUsingScope(this), ns, []); } sealed class DummyNamespace : INamespace { readonly INamespace parentNamespace; readonly string name; public DummyNamespace(INamespace parentNamespace, string name) { this.parentNamespace = parentNamespace; this.name = name; } string INamespace.ExternAlias => ""; string INamespace.FullName { get { return NamespaceDeclaration.BuildQualifiedName(parentNamespace.FullName, name); } } public string Name { get { return name; } } SymbolKind ISymbol.SymbolKind { get { return SymbolKind.Namespace; } } INamespace INamespace.ParentNamespace { get { return parentNamespace; } } IEnumerable INamespace.ChildNamespaces { get { return EmptyList.Instance; } } IEnumerable INamespace.Types { get { return EmptyList.Instance; } } IEnumerable INamespace.ContributingModules { get { return EmptyList.Instance; } } ICompilation ICompilationProvider.Compilation { get { return parentNamespace.Compilation; } } INamespace? INamespace.GetChildNamespace(string name) { return null; } ITypeDefinition? INamespace.GetTypeDefinition(string name, int typeParameterCount) { return null; } } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs ================================================ using System; using System.Collections.Immutable; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; namespace ICSharpCode.Decompiler.DebugInfo { public readonly struct AsyncDebugInfo { public readonly int CatchHandlerOffset; public readonly ImmutableArray Awaits; public AsyncDebugInfo(int catchHandlerOffset, ImmutableArray awaits) { this.CatchHandlerOffset = catchHandlerOffset; this.Awaits = awaits; } public readonly struct Await { public readonly int YieldOffset; public readonly int ResumeOffset; public Await(int yieldOffset, int resumeOffset) { this.YieldOffset = yieldOffset; this.ResumeOffset = resumeOffset; } } public BlobBuilder BuildBlob(MethodDefinitionHandle moveNext) { BlobBuilder blob = new BlobBuilder(); blob.WriteUInt32((uint)CatchHandlerOffset); foreach (var await in Awaits) { blob.WriteUInt32((uint)await.YieldOffset); blob.WriteUInt32((uint)await.ResumeOffset); blob.WriteCompressedInteger(MetadataTokens.GetRowNumber(moveNext)); } return blob; } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs ================================================ // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.DebugInfo { /// /// Visitor that generates debug information. /// /// The intended usage is to create a new instance for each source file, /// and call syntaxTree.AcceptVisitor(debugInfoGenerator) to fill the internal debug info tables. /// This can happen concurrently for multiple source files. /// Then the main thread calls Generate() to write out the results into the PDB. /// class DebugInfoGenerator : DepthFirstAstVisitor { static readonly KeyComparer ILVariableKeyComparer = new KeyComparer(l => l.Index.Value, Comparer.Default, EqualityComparer.Default); IDecompilerTypeSystem typeSystem; readonly ImportScopeInfo globalImportScope = new ImportScopeInfo(); ImportScopeInfo currentImportScope; List importScopes = new List(); internal List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)> LocalScopes { get; } = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)>(); List functions = new List(); /// /// Gets all functions with bodies that were seen by the visitor so far. /// public IReadOnlyList Functions { get => functions; } public DebugInfoGenerator(IDecompilerTypeSystem typeSystem) { this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.currentImportScope = globalImportScope; } public void GenerateImportScopes(MetadataBuilder metadata, ImportScopeHandle globalImportScope) { foreach (var scope in importScopes) { var blob = EncodeImports(metadata, scope); scope.Handle = metadata.AddImportScope(scope.Parent == null ? globalImportScope : scope.Parent.Handle, blob); } } static BlobHandle EncodeImports(MetadataBuilder metadata, ImportScopeInfo scope) { var writer = new BlobBuilder(); foreach (var import in scope.Imports) { writer.WriteByte((byte)ImportDefinitionKind.ImportNamespace); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import))); } return metadata.GetOrAddBlob(writer); } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { var parentImportScope = currentImportScope; currentImportScope = new ImportScopeInfo(parentImportScope); importScopes.Add(currentImportScope); base.VisitNamespaceDeclaration(namespaceDeclaration); currentImportScope = parentImportScope; } public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { currentImportScope.Imports.Add(usingDeclaration.Namespace); } public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { HandleMethod(methodDeclaration); } public override void VisitAccessor(Accessor accessor) { HandleMethod(accessor); } public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { HandleMethod(constructorDeclaration); } public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { HandleMethod(destructorDeclaration); } public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { HandleMethod(operatorDeclaration); } public override void VisitLambdaExpression(LambdaExpression lambdaExpression) { HandleMethod(lambdaExpression); } public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { HandleMethod(anonymousMethodExpression); } public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (!propertyDeclaration.ExpressionBody.IsNull) { HandleMethod(propertyDeclaration.ExpressionBody, propertyDeclaration.Annotation()); } else { base.VisitPropertyDeclaration(propertyDeclaration); } } public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { if (!indexerDeclaration.ExpressionBody.IsNull) { HandleMethod(indexerDeclaration.ExpressionBody, indexerDeclaration.Annotation()); } else { base.VisitIndexerDeclaration(indexerDeclaration); } } public override void VisitQueryFromClause(QueryFromClause queryFromClause) { if (queryFromClause.Parent.FirstChild != queryFromClause) { HandleMethod(queryFromClause); } else { base.VisitQueryFromClause(queryFromClause); } } public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { var annotation = queryGroupClause.Annotation(); if (annotation == null) { base.VisitQueryGroupClause(queryGroupClause); return; } HandleMethod(queryGroupClause.Projection, annotation.ProjectionLambda); HandleMethod(queryGroupClause.Key, annotation.KeyLambda); } public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { var annotation = queryJoinClause.Annotation(); if (annotation == null) { base.VisitQueryJoinClause(queryJoinClause); return; } HandleMethod(queryJoinClause.OnExpression, annotation.OnLambda); HandleMethod(queryJoinClause.EqualsExpression, annotation.EqualsLambda); } public override void VisitQueryLetClause(QueryLetClause queryLetClause) { HandleMethod(queryLetClause); } public override void VisitQueryOrdering(QueryOrdering queryOrdering) { HandleMethod(queryOrdering); } public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) { HandleMethod(querySelectClause); } public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { HandleMethod(queryWhereClause); } void HandleMethod(AstNode node) { HandleMethod(node, node.Annotation()); } void HandleMethod(AstNode node, ILFunction function) { // Look into method body, e.g. in order to find lambdas VisitChildren(node); if (function == null || function.Method == null || function.Method.MetadataToken.IsNil) return; this.functions.Add(function); var method = function.MoveNextMethod ?? function.Method; MethodDefinitionHandle handle = (MethodDefinitionHandle)method.MetadataToken; var file = typeSystem.MainModule.MetadataFile; MethodDefinition md = file.Metadata.GetMethodDefinition(handle); if (md.HasBody()) { HandleMethodBody(function, file.GetMethodBody(md.RelativeVirtualAddress)); } } void HandleMethodBody(ILFunction function, MethodBodyBlock methodBody) { var method = function.MoveNextMethod ?? function.Method; var localVariables = new HashSet(ILVariableKeyComparer); if (!methodBody.LocalSignature.IsNil) { #if DEBUG var types = typeSystem.MainModule.DecodeLocalSignature(methodBody.LocalSignature, new TypeSystem.GenericContext(method)); #endif foreach (var v in function.Variables) { if (v.Index != null && v.Kind.IsLocal()) { #if DEBUG Debug.Assert(v.Index < types.Length && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(v.Type, types[v.Index.Value])); #endif localVariables.Add(v); } } } LocalScopes.Add(((MethodDefinitionHandle)method.MetadataToken, currentImportScope, 0, methodBody.GetCodeSize(), localVariables)); } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs ================================================ using System; using System.Collections.Generic; using System.Reflection.Metadata; using System.Text; namespace ICSharpCode.Decompiler.DebugInfo { public struct Variable { public Variable(int index, string name) { Index = index; Name = name; } public int Index { get; } public string Name { get; } } public struct PdbExtraTypeInfo { public string[] TupleElementNames; public bool[] DynamicFlags; } public interface IDebugInfoProvider { string Description { get; } IList GetSequencePoints(MethodDefinitionHandle method); IList GetVariables(MethodDefinitionHandle method); bool TryGetName(MethodDefinitionHandle method, int index, out string name); bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo); string SourceFileName { get; } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/ImportScopeInfo.cs ================================================ // 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.Collections.Generic; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.DebugInfo { class ImportScopeInfo { public readonly ImportScopeInfo Parent; public ImportScopeHandle Handle; public readonly HashSet Imports = new HashSet(); public ImportScopeInfo() { Parent = null; } public ImportScopeInfo(ImportScopeInfo parent) { Parent = parent; } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/KnownGuids.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace ICSharpCode.Decompiler.DebugInfo { public static class KnownGuids { public static readonly Guid CSharpLanguageGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1"); public static readonly Guid VBLanguageGuid = new Guid("3a12d0b8-c26c-11d0-b442-00a0244a1dd2"); public static readonly Guid FSharpLanguageGuid = new Guid("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3"); // https://github.com/dotnet/roslyn/blob/main/src/Dependencies/CodeAnalysis.Debugging/PortableCustomDebugInfoKinds.cs public static readonly Guid StateMachineHoistedLocalScopes = new Guid("6DA9A61E-F8C7-4874-BE62-68BC5630DF71"); public static readonly Guid DynamicLocalVariables = new Guid("83C563C4-B4F3-47D5-B824-BA5441477EA8"); public static readonly Guid DefaultNamespaces = new Guid("58b2eab6-209f-4e4e-a22c-b2d0f910c782"); public static readonly Guid EditAndContinueLocalSlotMap = new Guid("755F52A8-91C5-45BE-B4B8-209571E552BD"); public static readonly Guid EditAndContinueLambdaAndClosureMap = new Guid("A643004C-0240-496F-A783-30D64F4979DE"); public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3"); public static readonly Guid EmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe"); public static readonly Guid SourceLink = new Guid("CC110556-A091-4D38-9FEC-25AB9A351A6A"); public static readonly Guid MethodSteppingInformation = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8"); public static readonly Guid CompilationOptions = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8"); public static readonly Guid CompilationMetadataReferences = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D"); public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710"); public static readonly Guid TypeDefinitionDocuments = new Guid("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"); public static readonly Guid HashAlgorithmSHA1 = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460"); public static readonly Guid HashAlgorithmSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16"); } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs ================================================ // 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.Collections.Immutable; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Security.Cryptography; using System.Text; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.DebugInfo { public class PortablePdbWriter { const string decompilerVersion = DecompilerVersionInfo.Version; public static bool HasCodeViewDebugDirectoryEntry(PEFile file) { return file != null && file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); } private static bool IncludeTypeWhenGeneratingPdb(PEFile module, TypeDefinitionHandle type, DecompilerSettings settings) { var metadata = module.Metadata; var typeDef = metadata.GetTypeDefinition(type); string name = metadata.GetString(typeDef.Name); string ns = metadata.GetString(typeDef.Namespace); if (name == "" || CSharpDecompiler.MemberIsHidden(module, type, settings)) return false; if (ns == "XamlGeneratedNamespace" && name == "GeneratedInternalTypeHelper") return false; if (!typeDef.IsNested && RemoveEmbeddedAttributes.attributeNames.Contains(ns + "." + name)) return false; return true; } public static void WritePdb( PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream, bool noLogo = false, BlobContentId? pdbId = null, IProgress progress = null, string currentProgressTitle = "Generating portable PDB...") { MetadataBuilder metadata = new MetadataBuilder(); MetadataReader reader = file.Metadata; var entrypointHandle = MetadataTokens.MethodDefinitionHandle(file.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); var sequencePointBlobs = new Dictionary(); var emptyList = new List(); var localScopes = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)>(); var stateMachineMethods = new List<(MethodDefinitionHandle MoveNextMethod, MethodDefinitionHandle KickoffMethod)>(); var customDebugInfo = new List<(EntityHandle Parent, GuidHandle Guid, BlobHandle Blob)>(); var customMethodDebugInfo = new List<(MethodDefinitionHandle Parent, GuidHandle Guid, BlobHandle Blob)>(); var globalImportScope = metadata.AddImportScope(default, default); string BuildFileNameFromTypeName(TypeDefinitionHandle handle) { var typeName = handle.GetFullTypeName(reader).TopLevelTypeName; string ns = settings.UseNestedDirectoriesForNamespaces ? WholeProjectDecompiler.CleanUpPath(typeName.Namespace) : WholeProjectDecompiler.CleanUpDirectoryName(typeName.Namespace); return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name, ".cs")); } var sourceFiles = reader.GetTopLevelTypeDefinitions().Where(t => IncludeTypeWhenGeneratingPdb(file, t, settings)).GroupBy(BuildFileNameFromTypeName).ToList(); DecompilationProgress currentProgress = new() { TotalUnits = sourceFiles.Count, UnitsCompleted = 0, Title = currentProgressTitle }; foreach (var sourceFile in sourceFiles) { // Generate syntax tree var syntaxTree = decompiler.DecompileTypes(sourceFile); if (progress != null) { currentProgress.UnitsCompleted++; progress.Report(currentProgress); } if (!syntaxTree.HasChildren) continue; // Generate source and checksum if (!noLogo) syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion), Roles.Comment); var sourceText = SyntaxTreeToString(syntaxTree, settings); // Generate sequence points for the syntax tree var sequencePoints = decompiler.CreateSequencePoints(syntaxTree); // Generate other debug information var debugInfoGen = new DebugInfoGenerator(decompiler.TypeSystem); syntaxTree.AcceptVisitor(debugInfoGen); lock (metadata) { var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum); var name = metadata.GetOrAddDocumentName(sourceFile.Key); // Create Document(Handle) var document = metadata.AddDocument(name, hashAlgorithm: metadata.GetOrAddGuid(KnownGuids.HashAlgorithmSHA256), hash: metadata.GetOrAddBlob(sourceCheckSum), language: metadata.GetOrAddGuid(KnownGuids.CSharpLanguageGuid)); // Add embedded source to the PDB customDebugInfo.Add((document, metadata.GetOrAddGuid(KnownGuids.EmbeddedSource), sourceBlob)); debugInfoGen.GenerateImportScopes(metadata, globalImportScope); localScopes.AddRange(debugInfoGen.LocalScopes); foreach (var function in debugInfoGen.Functions) { var method = function.MoveNextMethod ?? function.Method; var methodHandle = (MethodDefinitionHandle)method.MetadataToken; sequencePoints.TryGetValue(function, out var points); ProcessMethod(methodHandle, document, points, syntaxTree); if (function.MoveNextMethod != null) { stateMachineMethods.Add(( (MethodDefinitionHandle)function.MoveNextMethod.MetadataToken, (MethodDefinitionHandle)function.Method.MetadataToken )); customDebugInfo.Add(( function.MoveNextMethod.MetadataToken, metadata.GetOrAddGuid(KnownGuids.StateMachineHoistedLocalScopes), metadata.GetOrAddBlob(BuildStateMachineHoistedLocalScopes(function)) )); } if (function.IsAsync) { customMethodDebugInfo.Add((methodHandle, metadata.GetOrAddGuid(KnownGuids.MethodSteppingInformation), metadata.GetOrAddBlob(function.AsyncDebugInfo.BuildBlob(methodHandle)))); } } } } foreach (var method in reader.MethodDefinitions) { var md = reader.GetMethodDefinition(method); if (sequencePointBlobs.TryGetValue(method, out var info)) { metadata.AddMethodDebugInformation(info.Document, info.SequencePoints); } else { metadata.AddMethodDebugInformation(default, default); } } localScopes.Sort((x, y) => { if (x.Method != y.Method) { return MetadataTokens.GetRowNumber(x.Method) - MetadataTokens.GetRowNumber(y.Method); } if (x.Offset != y.Offset) { return x.Offset - y.Offset; } return y.Length - x.Length; }); foreach (var localScope in localScopes) { int nextRow = metadata.GetRowCount(TableIndex.LocalVariable) + 1; var firstLocalVariable = MetadataTokens.LocalVariableHandle(nextRow); foreach (var local in localScope.Locals.OrderBy(l => l.Index)) { var localVarName = local.Name != null ? metadata.GetOrAddString(local.Name) : default; metadata.AddLocalVariable(LocalVariableAttributes.None, local.Index.Value, localVarName); } metadata.AddLocalScope(localScope.Method, localScope.Import.Handle, firstLocalVariable, default, localScope.Offset, localScope.Length); } stateMachineMethods.SortBy(row => MetadataTokens.GetRowNumber(row.MoveNextMethod)); foreach (var row in stateMachineMethods) { metadata.AddStateMachineMethod(row.MoveNextMethod, row.KickoffMethod); } customMethodDebugInfo.SortBy(row => MetadataTokens.GetRowNumber(row.Parent)); foreach (var row in customMethodDebugInfo) { metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob); } customDebugInfo.SortBy(row => MetadataTokens.GetRowNumber(row.Parent)); foreach (var row in customDebugInfo) { metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob); } if (pdbId == null) { var debugDir = file.Reader.ReadDebugDirectory().LastOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView); var portable = file.Reader.ReadCodeViewDebugDirectoryData(debugDir); Debug.Assert(!portable.Path.EndsWith(".ni.pdb")); pdbId = new BlobContentId(portable.Guid, debugDir.Stamp); } PortablePdbBuilder serializer = new PortablePdbBuilder(metadata, GetRowCounts(reader), entrypointHandle, blobs => pdbId.Value); BlobBuilder blobBuilder = new BlobBuilder(); serializer.Serialize(blobBuilder); blobBuilder.WriteContentTo(targetStream); void ProcessMethod(MethodDefinitionHandle method, DocumentHandle document, List sequencePoints, SyntaxTree syntaxTree) { var methodDef = reader.GetMethodDefinition(method); int localSignatureRowId; MethodBodyBlock methodBody; if (methodDef.RelativeVirtualAddress != 0) { methodBody = file.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature); } else { methodBody = null; localSignatureRowId = 0; } // Check if sequence points were already processed - ILFunction gets defined in C# twice: // This may happen if a compiler-generated function gets transformed into a lambda expression, // but its method definition is not removed from the syntax tree. if (!sequencePointBlobs.ContainsKey(method)) { if (sequencePoints?.Count > 0) sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, sequencePoints))); else sequencePointBlobs.Add(method, (default, default)); } else { Debug.Assert(false, "Duplicate sequence point definition detected: " + MetadataTokens.GetToken(method).ToString("X8")); } } } static BlobBuilder BuildStateMachineHoistedLocalScopes(ILFunction function) { var builder = new BlobBuilder(); foreach (var variable in function.Variables.Where(v => v.StateMachineField != null).OrderBy(v => MetadataTokens.GetRowNumber(v.StateMachineField.MetadataToken))) { builder.WriteUInt32(0); builder.WriteUInt32((uint)function.CodeSize); } return builder; } static BlobHandle WriteSourceToBlob(MetadataBuilder metadata, string sourceText, out byte[] sourceCheckSum) { var builder = new BlobBuilder(); using (var memory = new MemoryStream()) { var deflate = new DeflateStream(memory, CompressionLevel.Optimal, leaveOpen: true); byte[] bytes = Encoding.UTF8.GetBytes(sourceText); deflate.Write(bytes, 0, bytes.Length); deflate.Close(); byte[] buffer = memory.ToArray(); builder.WriteInt32(bytes.Length); // compressed builder.WriteBytes(buffer); using (var hasher = SHA256.Create()) { sourceCheckSum = hasher.ComputeHash(bytes); } } return metadata.GetOrAddBlob(builder); } static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List sequencePoints) { if (sequencePoints.Count == 0) return default; var writer = new BlobBuilder(); // header: writer.WriteCompressedInteger(localSignatureRowId); int previousOffset = -1; int previousStartLine = -1; int previousStartColumn = -1; for (int i = 0; i < sequencePoints.Count; i++) { var sequencePoint = sequencePoints[i]; // delta IL offset: if (i > 0) writer.WriteCompressedInteger(sequencePoint.Offset - previousOffset); else writer.WriteCompressedInteger(sequencePoint.Offset); previousOffset = sequencePoint.Offset; if (sequencePoint.IsHidden) { writer.WriteInt16(0); continue; } int lineDelta = sequencePoint.EndLine - sequencePoint.StartLine; int columnDelta = sequencePoint.EndColumn - sequencePoint.StartColumn; writer.WriteCompressedInteger(lineDelta); if (lineDelta == 0) { writer.WriteCompressedInteger(columnDelta); } else { writer.WriteCompressedSignedInteger(columnDelta); } if (previousStartLine < 0) { writer.WriteCompressedInteger(sequencePoint.StartLine); writer.WriteCompressedInteger(sequencePoint.StartColumn); } else { writer.WriteCompressedSignedInteger(sequencePoint.StartLine - previousStartLine); writer.WriteCompressedSignedInteger(sequencePoint.StartColumn - previousStartColumn); } previousStartLine = sequencePoint.StartLine; previousStartColumn = sequencePoint.StartColumn; } return metadata.GetOrAddBlob(writer); } static ImmutableArray GetRowCounts(MetadataReader reader) { var builder = ImmutableArray.CreateBuilder(MetadataTokens.TableCount); for (int i = 0; i < MetadataTokens.TableCount; i++) { builder.Add(reader.GetTableRowCount((TableIndex)i)); } return builder.MoveToImmutable(); } static string SyntaxTreeToString(SyntaxTree syntaxTree, DecompilerSettings settings) { StringWriter w = new StringWriter(); TokenWriter tokenWriter = new TextWriterTokenWriter(w); tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); return w.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.DebugInfo { /// /// A sequence point read from a PDB file or produced by the decompiler. /// [DebuggerDisplay("SequencePoint IL_{Offset,h}-IL_{EndOffset,h}, {StartLine}:{StartColumn}-{EndLine}:{EndColumn}, IsHidden={IsHidden}")] public class SequencePoint { /// /// IL start offset. /// public int Offset { get; set; } /// /// IL end offset. /// /// /// This does not get stored in debug information; /// it is used internally to create hidden sequence points /// for the IL fragments not covered by any sequence point. /// public int EndOffset { get; set; } public int StartLine { get; set; } public int StartColumn { get; set; } public int EndLine { get; set; } public int EndColumn { get; set; } public bool IsHidden { get { return StartLine == 0xfeefee && StartLine == EndLine; } } public string DocumentUrl { get; set; } internal void SetHidden() { StartLine = EndLine = 0xfeefee; } } } ================================================ FILE: ICSharpCode.Decompiler/DecompilationProgress.cs ================================================ // Copyright (c) 2022 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 // 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. #nullable enable namespace ICSharpCode.Decompiler { /// /// Information used for (optional) progress reporting by the decompiler. /// public struct DecompilationProgress { /// /// The total number of units to process. If set to a value <= 0, an indeterminate progress bar is displayed. /// public int TotalUnits; /// /// The number of units currently completed. Should be a positive number. /// public int UnitsCompleted; /// /// Optional information displayed alongside the progress bar. /// public string? Status; /// /// Optional custom title for the operation. /// public string? Title; } } ================================================ FILE: ICSharpCode.Decompiler/DecompileRun.cs ================================================ // 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.Threading; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { internal class DecompileRun { public HashSet DefinedSymbols { get; } = new HashSet(); public HashSet Namespaces { get; set; } public CancellationToken CancellationToken { get; set; } public DecompilerSettings Settings { get; } public IDocumentationProvider DocumentationProvider { get; set; } public Dictionary RecordDecompilers { get; } = new Dictionary(); public Dictionary TypeHierarchyIsKnown { get; } = new(); public CSharp.TypeSystem.UsingScope UsingScope { get; } public DecompileRun(DecompilerSettings settings, CSharp.TypeSystem.UsingScope usingScope) { this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); this.UsingScope = usingScope ?? throw new ArgumentNullException(nameof(usingScope)); } } enum EnumValueDisplayMode { None, All, AllHex, FirstOnly } } ================================================ FILE: ICSharpCode.Decompiler/DecompilerException.cs ================================================ // 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 // 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.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Text; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { /// /// Description of DecompilerException. /// public class DecompilerException : Exception, ISerializable { public string AssemblyName => File.Name; public string FileName => File.FileName; public IEntity DecompiledEntity { get; } public IModule Module { get; } public MetadataFile File { get; } public DecompilerException(MetadataModule module, IEntity decompiledEntity, Exception innerException, string message = null) : base(message ?? GetDefaultMessage(decompiledEntity), innerException) { this.File = module.MetadataFile; this.Module = module; this.DecompiledEntity = decompiledEntity; } public DecompilerException(MetadataFile file, string message, Exception innerException) : base(message, innerException) { this.File = file; } static string GetDefaultMessage(IEntity entity) { if (entity == null) return "Error decompiling"; return $"Error decompiling @{MetadataTokens.GetToken(entity.MetadataToken):X8} {entity.FullName}"; } // This constructor is needed for serialization. protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context) { } public override string StackTrace => GetStackTrace(this); public override string ToString() => ToString(this); string ToString(Exception exception) { if (exception == null) throw new ArgumentNullException(nameof(exception)); string exceptionType = GetTypeName(exception); string stacktrace = GetStackTrace(exception); while (exception.InnerException != null) { exception = exception.InnerException; stacktrace = GetStackTrace(exception) + Environment.NewLine + "-- continuing with outer exception (" + exceptionType + ") --" + Environment.NewLine + stacktrace; exceptionType = GetTypeName(exception); } return this.Message + Environment.NewLine + $"in assembly \"{this.FileName}\"" + Environment.NewLine + " ---> " + exceptionType + ": " + exception.Message + Environment.NewLine + stacktrace; } static string GetTypeName(Exception exception) { string type = exception.GetType().FullName; if (exception is ExternalException || exception is IOException) return type + " (" + Marshal.GetHRForException(exception).ToString("x8") + ")"; else return type; } static string GetStackTrace(Exception exception) { // Output stacktrace in custom format (very similar to Exception.StackTrace // property on English systems). // Include filenames where available, but no paths. StackTrace stackTrace = new StackTrace(exception, true); StringBuilder b = new StringBuilder(); for (int i = 0; i < stackTrace.FrameCount; i++) { StackFrame frame = stackTrace.GetFrame(i); MethodBase method = frame.GetMethod(); if (method == null) continue; if (b.Length > 0) b.AppendLine(); b.Append(" at "); Type declaringType = method.DeclaringType; if (declaringType != null) { b.Append(declaringType.FullName.Replace('+', '.')); b.Append('.'); } b.Append(method.Name); // output type parameters, if any if ((method is MethodInfo) && ((MethodInfo)method).IsGenericMethod) { Type[] genericArguments = ((MethodInfo)method).GetGenericArguments(); b.Append('['); for (int j = 0; j < genericArguments.Length; j++) { if (j > 0) b.Append(','); b.Append(genericArguments[j].Name); } b.Append(']'); } // output parameters, if any b.Append('('); ParameterInfo[] parameters = method.GetParameters(); for (int j = 0; j < parameters.Length; j++) { if (j > 0) b.Append(", "); if (parameters[j].ParameterType != null) { b.Append(parameters[j].ParameterType.Name); } else { b.Append('?'); } if (!string.IsNullOrEmpty(parameters[j].Name)) { b.Append(' '); b.Append(parameters[j].Name); } } b.Append(')'); // source location if (frame.GetILOffset() >= 0) { string filename = null; try { string fullpath = frame.GetFileName(); if (fullpath != null) filename = Path.GetFileName(fullpath); } catch (SecurityException) { // StackFrame.GetFileName requires PathDiscovery permission } catch (ArgumentException) { // Path.GetFileName might throw on paths with invalid chars } b.Append(" in "); if (filename != null) { b.Append(filename); b.Append(":line "); b.Append(frame.GetFileLineNumber()); } else { b.Append("offset "); b.Append(frame.GetILOffset()); } } } return b.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/DecompilerSettings.cs ================================================ // 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 // 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.ComponentModel; using System.Runtime.CompilerServices; using ICSharpCode.Decompiler.CSharp.OutputVisitor; namespace ICSharpCode.Decompiler { /// /// Settings for the decompiler. /// public class DecompilerSettings : INotifyPropertyChanged { /// /// Equivalent to new DecompilerSettings(LanguageVersion.Latest) /// public DecompilerSettings() { } /// /// Creates a new DecompilerSettings instance with initial settings /// appropriate for the specified language version. /// /// /// This does not imply that the resulting code strictly uses only language features from /// that version. Language constructs like generics or ref locals cannot be removed from /// the compiled code. /// public DecompilerSettings(CSharp.LanguageVersion languageVersion) { SetLanguageVersion(languageVersion); } /// /// Deactivates all language features from versions newer than . /// public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) { // By default, all decompiler features are enabled. // Disable some of them based on language version: if (languageVersion < CSharp.LanguageVersion.CSharp2) { anonymousMethods = false; liftNullables = false; yieldReturn = false; useImplicitMethodGroupConversion = false; useObjectCreationOfGenericTypeParameter = false; } if (languageVersion < CSharp.LanguageVersion.CSharp3) { anonymousTypes = false; useLambdaSyntax = false; objectCollectionInitializers = false; automaticProperties = false; extensionMethods = false; queryExpressions = false; expressionTrees = false; } if (languageVersion < CSharp.LanguageVersion.CSharp4) { dynamic = false; namedArguments = false; optionalArguments = false; } if (languageVersion < CSharp.LanguageVersion.CSharp5) { asyncAwait = false; } if (languageVersion < CSharp.LanguageVersion.CSharp6) { awaitInCatchFinally = false; useExpressionBodyForCalculatedGetterOnlyProperties = false; nullPropagation = false; stringInterpolation = false; dictionaryInitializers = false; extensionMethodsInCollectionInitializers = false; getterOnlyAutomaticProperties = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7) { outVariables = false; throwExpressions = false; tupleTypes = false; tupleConversions = false; discards = false; localFunctions = false; deconstruction = false; patternMatching = false; useRefLocalsForAccurateOrderOfEvaluation = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7_2) { introduceReadonlyAndInModifiers = false; introduceRefModifiersOnStructs = false; nonTrailingNamedArguments = false; refExtensionMethods = false; introducePrivateProtectedAccessibilty = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7_3) { introduceUnmanagedConstraint = false; stackAllocInitializers = false; tupleComparisons = false; patternBasedFixedStatement = false; } if (languageVersion < CSharp.LanguageVersion.CSharp8_0) { nullableReferenceTypes = false; readOnlyMethods = false; asyncUsingAndForEachStatement = false; asyncEnumerator = false; useEnhancedUsing = false; staticLocalFunctions = false; ranges = false; switchExpressions = false; recursivePatternMatching = false; } if (languageVersion < CSharp.LanguageVersion.CSharp9_0) { nativeIntegers = false; initAccessors = false; functionPointers = false; forEachWithGetEnumeratorExtension = false; recordClasses = false; withExpressions = false; usePrimaryConstructorSyntax = false; covariantReturns = false; relationalPatterns = false; patternCombinators = false; } if (languageVersion < CSharp.LanguageVersion.CSharp10_0) { fileScopedNamespaces = false; recordStructs = false; structDefaultConstructorsAndFieldInitializers = false; } if (languageVersion < CSharp.LanguageVersion.CSharp11_0) { scopedRef = false; requiredMembers = false; numericIntPtr = false; utf8StringLiterals = false; unsignedRightShift = false; checkedOperators = false; } if (languageVersion < CSharp.LanguageVersion.CSharp12_0) { refReadOnlyParameters = false; usePrimaryConstructorSyntaxForNonRecordTypes = false; inlineArrays = false; } if (languageVersion < CSharp.LanguageVersion.CSharp13_0) { paramsCollections = false; } if (languageVersion < CSharp.LanguageVersion.CSharp14_0) { extensionMembers = false; firstClassSpanTypes = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { if (extensionMembers || firstClassSpanTypes) return CSharp.LanguageVersion.CSharp14_0; if (paramsCollections) return CSharp.LanguageVersion.CSharp13_0; if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes || inlineArrays) return CSharp.LanguageVersion.CSharp12_0; if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs || structDefaultConstructorsAndFieldInitializers) return CSharp.LanguageVersion.CSharp10_0; if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension || recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns || relationalPatterns || patternCombinators) return CSharp.LanguageVersion.CSharp9_0; if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges || switchExpressions || recursivePatternMatching) return CSharp.LanguageVersion.CSharp8_0; if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement) return CSharp.LanguageVersion.CSharp7_3; if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods || introducePrivateProtectedAccessibilty) return CSharp.LanguageVersion.CSharp7_2; // C# 7.1 missing if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions || deconstruction || patternMatching || useRefLocalsForAccurateOrderOfEvaluation) return CSharp.LanguageVersion.CSharp7; if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers || getterOnlyAutomaticProperties) return CSharp.LanguageVersion.CSharp6; if (asyncAwait) return CSharp.LanguageVersion.CSharp5; if (dynamic || namedArguments || optionalArguments) return CSharp.LanguageVersion.CSharp4; if (anonymousTypes || objectCollectionInitializers || automaticProperties || queryExpressions || expressionTrees) return CSharp.LanguageVersion.CSharp3; if (anonymousMethods || liftNullables || yieldReturn || useImplicitMethodGroupConversion || useObjectCreationOfGenericTypeParameter) return CSharp.LanguageVersion.CSharp2; return CSharp.LanguageVersion.CSharp1; } bool nativeIntegers = true; /// /// Use C# 9 nint/nuint types. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.NativeIntegers")] public bool NativeIntegers { get { return nativeIntegers; } set { if (nativeIntegers != value) { nativeIntegers = value; OnPropertyChanged(); } } } bool numericIntPtr = true; /// /// Treat IntPtr/UIntPtr as nint/nuint. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.NumericIntPtr")] public bool NumericIntPtr { get { return numericIntPtr; } set { if (numericIntPtr != value) { numericIntPtr = value; OnPropertyChanged(); } } } bool covariantReturns = true; /// /// Decompile C# 9 covariant return types. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.CovariantReturns")] public bool CovariantReturns { get { return covariantReturns; } set { if (covariantReturns != value) { covariantReturns = value; OnPropertyChanged(); } } } bool initAccessors = true; /// /// Use C# 9 init; property accessors. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.InitAccessors")] public bool InitAccessors { get { return initAccessors; } set { if (initAccessors != value) { initAccessors = value; OnPropertyChanged(); } } } bool recordClasses = true; /// /// Use C# 9 record classes. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.RecordClasses")] public bool RecordClasses { get { return recordClasses; } set { if (recordClasses != value) { recordClasses = value; OnPropertyChanged(); } } } bool recordStructs = true; /// /// Use C# 10 record structs. /// [Category("C# 10.0 / VS 2022")] [Description("DecompilerSettings.RecordStructs")] public bool RecordStructs { get { return recordStructs; } set { if (recordStructs != value) { recordStructs = value; OnPropertyChanged(); } } } bool structDefaultConstructorsAndFieldInitializers = true; /// /// Use field initializers in structs. /// [Category("C# 10.0 / VS 2022")] [Description("DecompilerSettings.StructDefaultConstructorsAndFieldInitializers")] public bool StructDefaultConstructorsAndFieldInitializers { get { return structDefaultConstructorsAndFieldInitializers; } set { if (structDefaultConstructorsAndFieldInitializers != value) { structDefaultConstructorsAndFieldInitializers = value; OnPropertyChanged(); } } } bool withExpressions = true; /// /// Use C# 9 with initializer expressions. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.WithExpressions")] public bool WithExpressions { get { return withExpressions; } set { if (withExpressions != value) { withExpressions = value; OnPropertyChanged(); } } } bool usePrimaryConstructorSyntax = true; /// /// Use primary constructor syntax with records. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.UsePrimaryConstructorSyntax")] public bool UsePrimaryConstructorSyntax { get { return usePrimaryConstructorSyntax; } set { if (usePrimaryConstructorSyntax != value) { usePrimaryConstructorSyntax = value; OnPropertyChanged(); } } } bool functionPointers = true; /// /// Use C# 9 delegate* unmanaged types. /// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.FunctionPointers")] public bool FunctionPointers { get { return functionPointers; } set { if (functionPointers != value) { functionPointers = value; OnPropertyChanged(); } } } bool scopedRef = true; /// /// Use C# 11 scoped modifier. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.ScopedRef")] public bool ScopedRef { get { return scopedRef; } set { if (scopedRef != value) { scopedRef = value; OnPropertyChanged(); } } } [Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")] [Browsable(false)] public bool LifetimeAnnotations { get { return ScopedRef; } set { ScopedRef = value; } } bool requiredMembers = true; /// /// Use C# 11 required modifier. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.RequiredMembers")] public bool RequiredMembers { get { return requiredMembers; } set { if (requiredMembers != value) { requiredMembers = value; OnPropertyChanged(); } } } bool switchExpressions = true; /// /// Use C# 8 switch expressions. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.SwitchExpressions")] public bool SwitchExpressions { get { return switchExpressions; } set { if (switchExpressions != value) { switchExpressions = value; OnPropertyChanged(); } } } bool fileScopedNamespaces = true; /// /// Use C# 10 file-scoped namespaces. /// [Category("C# 10.0 / VS 2022")] [Description("DecompilerSettings.FileScopedNamespaces")] public bool FileScopedNamespaces { get { return fileScopedNamespaces; } set { if (fileScopedNamespaces != value) { fileScopedNamespaces = value; OnPropertyChanged(); } } } bool anonymousMethods = true; /// /// Decompile anonymous methods/lambdas. /// [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.DecompileAnonymousMethodsLambdas")] public bool AnonymousMethods { get { return anonymousMethods; } set { if (anonymousMethods != value) { anonymousMethods = value; OnPropertyChanged(); } } } bool anonymousTypes = true; /// /// Decompile anonymous types. /// [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileAnonymousTypes")] public bool AnonymousTypes { get { return anonymousTypes; } set { if (anonymousTypes != value) { anonymousTypes = value; OnPropertyChanged(); } } } bool useLambdaSyntax = true; /// /// Use C# 3 lambda syntax if possible. /// [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseLambdaSyntaxIfPossible")] public bool UseLambdaSyntax { get { return useLambdaSyntax; } set { if (useLambdaSyntax != value) { useLambdaSyntax = value; OnPropertyChanged(); } } } bool expressionTrees = true; /// /// Decompile expression trees. /// [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileExpressionTrees")] public bool ExpressionTrees { get { return expressionTrees; } set { if (expressionTrees != value) { expressionTrees = value; OnPropertyChanged(); } } } bool yieldReturn = true; /// /// Decompile enumerators. /// [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.DecompileEnumeratorsYieldReturn")] public bool YieldReturn { get { return yieldReturn; } set { if (yieldReturn != value) { yieldReturn = value; OnPropertyChanged(); } } } bool dynamic = true; /// /// Decompile use of the 'dynamic' type. /// [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.DecompileUseOfTheDynamicType")] public bool Dynamic { get { return dynamic; } set { if (dynamic != value) { dynamic = value; OnPropertyChanged(); } } } bool asyncAwait = true; /// /// Decompile async methods. /// [Category("C# 5.0 / VS 2012")] [Description("DecompilerSettings.DecompileAsyncMethods")] public bool AsyncAwait { get { return asyncAwait; } set { if (asyncAwait != value) { asyncAwait = value; OnPropertyChanged(); } } } bool awaitInCatchFinally = true; /// /// Decompile await in catch/finally blocks. /// Only has an effect if is enabled. /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.DecompileAwaitInCatchFinallyBlocks")] public bool AwaitInCatchFinally { get { return awaitInCatchFinally; } set { if (awaitInCatchFinally != value) { awaitInCatchFinally = value; OnPropertyChanged(); } } } bool asyncEnumerator = true; /// /// Decompile IAsyncEnumerator/IAsyncEnumerable. /// Only has an effect if is enabled. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.AsyncEnumerator")] public bool AsyncEnumerator { get { return asyncEnumerator; } set { if (asyncEnumerator != value) { asyncEnumerator = value; OnPropertyChanged(); } } } bool decimalConstants = true; /// /// Decompile [DecimalConstant(...)] as simple literal values. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileDecimalConstantAsSimpleLiteralValues")] public bool DecimalConstants { get { return decimalConstants; } set { if (decimalConstants != value) { decimalConstants = value; OnPropertyChanged(); } } } bool fixedBuffers = true; /// /// Decompile C# 1.0 'public unsafe fixed int arr[10];' members. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileC10PublicUnsafeFixedIntArr10Members")] public bool FixedBuffers { get { return fixedBuffers; } set { if (fixedBuffers != value) { fixedBuffers = value; OnPropertyChanged(); } } } bool stringConcat = true; /// /// Decompile 'string.Concat(a, b)' calls into 'a + b'. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.StringConcat")] public bool StringConcat { get { return stringConcat; } set { if (stringConcat != value) { stringConcat = value; OnPropertyChanged(); } } } bool liftNullables = true; /// /// Use lifted operators for nullables. /// [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseLiftedOperatorsForNullables")] public bool LiftNullables { get { return liftNullables; } set { if (liftNullables != value) { liftNullables = value; OnPropertyChanged(); } } } bool nullPropagation = true; /// /// Decompile C# 6 ?. and ?[] operators. /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.NullPropagation")] public bool NullPropagation { get { return nullPropagation; } set { if (nullPropagation != value) { nullPropagation = value; OnPropertyChanged(); } } } bool automaticProperties = true; /// /// Decompile automatic properties /// [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileAutomaticProperties")] public bool AutomaticProperties { get { return automaticProperties; } set { if (automaticProperties != value) { automaticProperties = value; OnPropertyChanged(); } } } bool getterOnlyAutomaticProperties = true; /// /// Decompile getter-only automatic properties /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.GetterOnlyAutomaticProperties")] public bool GetterOnlyAutomaticProperties { get { return getterOnlyAutomaticProperties; } set { if (getterOnlyAutomaticProperties != value) { getterOnlyAutomaticProperties = value; OnPropertyChanged(); } } } bool automaticEvents = true; /// /// Decompile automatic events /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileAutomaticEvents")] public bool AutomaticEvents { get { return automaticEvents; } set { if (automaticEvents != value) { automaticEvents = value; OnPropertyChanged(); } } } bool usingStatement = true; /// /// Decompile using statements. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectUsingStatements")] public bool UsingStatement { get { return usingStatement; } set { if (usingStatement != value) { usingStatement = value; OnPropertyChanged(); } } } bool useEnhancedUsing = true; /// /// Use enhanced using statements. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.UseEnhancedUsing")] public bool UseEnhancedUsing { get { return useEnhancedUsing; } set { if (useEnhancedUsing != value) { useEnhancedUsing = value; OnPropertyChanged(); } } } bool alwaysUseBraces = true; /// /// Gets/Sets whether to use braces for single-statement-blocks. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysUseBraces")] public bool AlwaysUseBraces { get { return alwaysUseBraces; } set { if (alwaysUseBraces != value) { alwaysUseBraces = value; OnPropertyChanged(); } } } bool forEachStatement = true; /// /// Decompile foreach statements. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectForeachStatements")] public bool ForEachStatement { get { return forEachStatement; } set { if (forEachStatement != value) { forEachStatement = value; OnPropertyChanged(); } } } bool forEachWithGetEnumeratorExtension = true; /// /// Support GetEnumerator extension methods in foreach. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.DecompileForEachWithGetEnumeratorExtension")] public bool ForEachWithGetEnumeratorExtension { get { return forEachWithGetEnumeratorExtension; } set { if (forEachWithGetEnumeratorExtension != value) { forEachWithGetEnumeratorExtension = value; OnPropertyChanged(); } } } bool paramsCollections = true; /// /// Support params collections. /// [Category("C# 13.0 / VS 2022.12")] [Description("DecompilerSettings.DecompileParamsCollections")] public bool ParamsCollections { get { return paramsCollections; } set { if (paramsCollections != value) { paramsCollections = value; OnPropertyChanged(); } } } bool lockStatement = true; /// /// Decompile lock statements. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectLockStatements")] public bool LockStatement { get { return lockStatement; } set { if (lockStatement != value) { lockStatement = value; OnPropertyChanged(); } } } bool switchStatementOnString = true; [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectSwitchOnString")] public bool SwitchStatementOnString { get { return switchStatementOnString; } set { if (switchStatementOnString != value) { switchStatementOnString = value; OnPropertyChanged(); } } } bool sparseIntegerSwitch = true; [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.SparseIntegerSwitch")] public bool SparseIntegerSwitch { get { return sparseIntegerSwitch; } set { if (sparseIntegerSwitch != value) { sparseIntegerSwitch = value; OnPropertyChanged(); } } } bool usingDeclarations = true; [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.InsertUsingDeclarations")] public bool UsingDeclarations { get { return usingDeclarations; } set { if (usingDeclarations != value) { usingDeclarations = value; OnPropertyChanged(); } } } bool extensionMethods = true; [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseExtensionMethodSyntax")] public bool ExtensionMethods { get { return extensionMethods; } set { if (extensionMethods != value) { extensionMethods = value; OnPropertyChanged(); } } } bool queryExpressions = true; [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseLINQExpressionSyntax")] public bool QueryExpressions { get { return queryExpressions; } set { if (queryExpressions != value) { queryExpressions = value; OnPropertyChanged(); } } } bool useImplicitMethodGroupConversion = true; /// /// Gets/Sets whether to use C# 2.0 method group conversions. /// true: EventHandler h = this.OnClick; /// false: EventHandler h = new EventHandler(this.OnClick); /// [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseImplicitMethodGroupConversions")] public bool UseImplicitMethodGroupConversion { get { return useImplicitMethodGroupConversion; } set { if (useImplicitMethodGroupConversion != value) { useImplicitMethodGroupConversion = value; OnPropertyChanged(); } } } bool useObjectCreationOfGenericTypeParameter = true; /// /// Gets/Sets whether to use object creation expressions for generic types with new() constraint. /// true: T t = new T(); /// false: T t = Activator.CreateInstance<T>() /// [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseObjectCreationOfGenericTypeParameter")] public bool UseObjectCreationOfGenericTypeParameter { get { return useObjectCreationOfGenericTypeParameter; } set { if (useObjectCreationOfGenericTypeParameter != value) { useObjectCreationOfGenericTypeParameter = value; OnPropertyChanged(); } } } bool alwaysCastTargetsOfExplicitInterfaceImplementationCalls = false; /// /// Gets/Sets whether to always cast targets to explicitly implemented methods. /// true: ((ISupportInitialize)pictureBox1).BeginInit(); /// false: pictureBox1.BeginInit(); /// default: false /// [Category("Other")] [Description("DecompilerSettings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls")] public bool AlwaysCastTargetsOfExplicitInterfaceImplementationCalls { get { return alwaysCastTargetsOfExplicitInterfaceImplementationCalls; } set { if (alwaysCastTargetsOfExplicitInterfaceImplementationCalls != value) { alwaysCastTargetsOfExplicitInterfaceImplementationCalls = value; OnPropertyChanged(); } } } bool alwaysQualifyMemberReferences = false; /// /// Gets/Sets whether to always qualify member references. /// true: this.DoSomething(); /// false: DoSomething(); /// default: false /// [Category("Other")] [Description("DecompilerSettings.AlwaysQualifyMemberReferences")] public bool AlwaysQualifyMemberReferences { get { return alwaysQualifyMemberReferences; } set { if (alwaysQualifyMemberReferences != value) { alwaysQualifyMemberReferences = value; OnPropertyChanged(); } } } bool alwaysShowEnumMemberValues = false; /// /// Gets/Sets whether to always show enum member values. /// true: enum Kind { A = 0, B = 1, C = 5 } /// false: enum Kind { A, B, C = 5 } /// default: false /// [Category("Other")] [Description("DecompilerSettings.AlwaysShowEnumMemberValues")] public bool AlwaysShowEnumMemberValues { get { return alwaysShowEnumMemberValues; } set { if (alwaysShowEnumMemberValues != value) { alwaysShowEnumMemberValues = value; OnPropertyChanged(); } } } bool useDebugSymbols = true; /// /// Gets/Sets whether to use variable names from debug symbols, if available. /// [Category("Other")] [Description("DecompilerSettings.UseVariableNamesFromDebugSymbolsIfAvailable")] public bool UseDebugSymbols { get { return useDebugSymbols; } set { if (useDebugSymbols != value) { useDebugSymbols = value; OnPropertyChanged(); } } } bool arrayInitializers = true; /// /// Gets/Sets whether to use array initializers. /// If set to false, might produce non-compilable code. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.ArrayInitializerExpressions")] public bool ArrayInitializers { get { return arrayInitializers; } set { if (arrayInitializers != value) { arrayInitializers = value; OnPropertyChanged(); } } } bool objectCollectionInitializers = true; /// /// Gets/Sets whether to use C# 3.0 object/collection initializers. /// [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.ObjectCollectionInitializerExpressions")] public bool ObjectOrCollectionInitializers { get { return objectCollectionInitializers; } set { if (objectCollectionInitializers != value) { objectCollectionInitializers = value; OnPropertyChanged(); } } } bool dictionaryInitializers = true; /// /// Gets/Sets whether to use C# 6.0 dictionary initializers. /// Only has an effect if ObjectOrCollectionInitializers is enabled. /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.DictionaryInitializerExpressions")] public bool DictionaryInitializers { get { return dictionaryInitializers; } set { if (dictionaryInitializers != value) { dictionaryInitializers = value; OnPropertyChanged(); } } } bool extensionMethodsInCollectionInitializers = true; /// /// Gets/Sets whether to use C# 6.0 Extension Add methods in collection initializers. /// Only has an effect if ObjectOrCollectionInitializers is enabled. /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.AllowExtensionAddMethodsInCollectionInitializerExpressions")] public bool ExtensionMethodsInCollectionInitializers { get { return extensionMethodsInCollectionInitializers; } set { if (extensionMethodsInCollectionInitializers != value) { extensionMethodsInCollectionInitializers = value; OnPropertyChanged(); } } } bool useRefLocalsForAccurateOrderOfEvaluation = true; /// /// Gets/Sets whether to use local ref variables in cases where this is necessary /// for re-compilation with a modern C# compiler to reproduce the same behavior /// as the original assembly produced with an old C# compiler that used an incorrect /// order of evaluation. /// See https://github.com/icsharpcode/ILSpy/issues/2050 /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseRefLocalsForAccurateOrderOfEvaluation")] public bool UseRefLocalsForAccurateOrderOfEvaluation { get { return useRefLocalsForAccurateOrderOfEvaluation; } set { if (useRefLocalsForAccurateOrderOfEvaluation != value) { useRefLocalsForAccurateOrderOfEvaluation = value; OnPropertyChanged(); } } } bool refExtensionMethods = true; /// /// Gets/Sets whether to use C# 7.2 'ref' extension methods. /// [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.AllowExtensionMethodSyntaxOnRef")] public bool RefExtensionMethods { get { return refExtensionMethods; } set { if (refExtensionMethods != value) { refExtensionMethods = value; OnPropertyChanged(); } } } bool stringInterpolation = true; /// /// Gets/Sets whether to use C# 6.0 string interpolation /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.UseStringInterpolation")] public bool StringInterpolation { get { return stringInterpolation; } set { if (stringInterpolation != value) { stringInterpolation = value; OnPropertyChanged(); } } } bool utf8StringLiterals = true; /// /// Gets/Sets whether to use C# 11.0 UTF-8 string literals /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.Utf8StringLiterals")] public bool Utf8StringLiterals { get { return utf8StringLiterals; } set { if (utf8StringLiterals != value) { utf8StringLiterals = value; OnPropertyChanged(); } } } bool switchOnReadOnlySpanChar = true; /// /// Gets/Sets whether to use C# 11.0 switch on (ReadOnly)Span<char> /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.SwitchOnReadOnlySpanChar")] public bool SwitchOnReadOnlySpanChar { get { return switchOnReadOnlySpanChar; } set { if (switchOnReadOnlySpanChar != value) { switchOnReadOnlySpanChar = value; OnPropertyChanged(); } } } bool unsignedRightShift = true; /// /// Gets/Sets whether to use C# 11.0 unsigned right shift operator. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.UnsignedRightShift")] public bool UnsignedRightShift { get { return unsignedRightShift; } set { if (unsignedRightShift != value) { unsignedRightShift = value; OnPropertyChanged(); } } } bool checkedOperators = true; /// /// Gets/Sets whether to use C# 11.0 user-defined checked operators. /// [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.CheckedOperators")] public bool CheckedOperators { get { return checkedOperators; } set { if (checkedOperators != value) { checkedOperators = value; OnPropertyChanged(); } } } bool showXmlDocumentation = true; /// /// Gets/Sets whether to include XML documentation comments in the decompiled code. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.IncludeXMLDocumentationCommentsInTheDecompiledCode")] public bool ShowXmlDocumentation { get { return showXmlDocumentation; } set { if (showXmlDocumentation != value) { showXmlDocumentation = value; OnPropertyChanged(); } } } bool foldBraces = false; [Browsable(false)] public bool FoldBraces { get { return foldBraces; } set { if (foldBraces != value) { foldBraces = value; OnPropertyChanged(); } } } bool expandMemberDefinitions = false; [Browsable(false)] public bool ExpandMemberDefinitions { get { return expandMemberDefinitions; } set { if (expandMemberDefinitions != value) { expandMemberDefinitions = value; OnPropertyChanged(); } } } bool expandUsingDeclarations = false; [Browsable(false)] public bool ExpandUsingDeclarations { get { return expandUsingDeclarations; } set { if (expandUsingDeclarations != value) { expandUsingDeclarations = value; OnPropertyChanged(); } } } bool decompileMemberBodies = true; /// /// Gets/Sets whether member bodies should be decompiled. /// [Category("DecompilerSettings.Other")] [Browsable(false)] public bool DecompileMemberBodies { get { return decompileMemberBodies; } set { if (decompileMemberBodies != value) { decompileMemberBodies = value; OnPropertyChanged(); } } } bool useExpressionBodyForCalculatedGetterOnlyProperties = true; /// /// Gets/Sets whether simple calculated getter-only property declarations /// should use expression body syntax. /// [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.UseExpressionBodiedMemberSyntaxForGetOnlyProperties")] public bool UseExpressionBodyForCalculatedGetterOnlyProperties { get { return useExpressionBodyForCalculatedGetterOnlyProperties; } set { if (useExpressionBodyForCalculatedGetterOnlyProperties != value) { useExpressionBodyForCalculatedGetterOnlyProperties = value; OnPropertyChanged(); } } } bool outVariables = true; /// /// Gets/Sets whether out variable declarations should be used when possible. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseOutVariableDeclarations")] public bool OutVariables { get { return outVariables; } set { if (outVariables != value) { outVariables = value; OnPropertyChanged(); } } } bool discards = true; /// /// Gets/Sets whether discards should be used when possible. /// Only has an effect if is enabled. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseDiscards")] public bool Discards { get { return discards; } set { if (discards != value) { discards = value; OnPropertyChanged(); } } } bool introduceRefModifiersOnStructs = true; /// /// Gets/Sets whether IsByRefLikeAttribute should be replaced with 'ref' modifiers on structs. /// [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.IsByRefLikeAttributeShouldBeReplacedWithRefModifiersOnStructs")] public bool IntroduceRefModifiersOnStructs { get { return introduceRefModifiersOnStructs; } set { if (introduceRefModifiersOnStructs != value) { introduceRefModifiersOnStructs = value; OnPropertyChanged(); } } } bool introduceReadonlyAndInModifiers = true; /// /// Gets/Sets whether IsReadOnlyAttribute should be replaced with 'readonly' modifiers on structs /// and with the 'in' modifier on parameters. /// [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings." + "IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters")] public bool IntroduceReadonlyAndInModifiers { get { return introduceReadonlyAndInModifiers; } set { if (introduceReadonlyAndInModifiers != value) { introduceReadonlyAndInModifiers = value; OnPropertyChanged(); } } } bool introducePrivateProtectedAccessibilty = true; /// /// Gets/Sets whether "private protected" should be used. /// [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.IntroducePrivateProtectedAccessibility")] public bool IntroducePrivateProtectedAccessibility { get { return introducePrivateProtectedAccessibilty; } set { if (introducePrivateProtectedAccessibilty != value) { introducePrivateProtectedAccessibilty = value; OnPropertyChanged(); } } } bool readOnlyMethods = true; [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.ReadOnlyMethods")] public bool ReadOnlyMethods { get { return readOnlyMethods; } set { if (readOnlyMethods != value) { readOnlyMethods = value; OnPropertyChanged(); } } } bool asyncUsingAndForEachStatement = true; [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.DetectAsyncUsingAndForeachStatements")] public bool AsyncUsingAndForEachStatement { get { return asyncUsingAndForEachStatement; } set { if (asyncUsingAndForEachStatement != value) { asyncUsingAndForEachStatement = value; OnPropertyChanged(); } } } bool introduceUnmanagedConstraint = true; /// /// If this option is active, [IsUnmanagedAttribute] on type parameters /// is replaced with "T : unmanaged" constraints. /// [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings." + "IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints")] public bool IntroduceUnmanagedConstraint { get { return introduceUnmanagedConstraint; } set { if (introduceUnmanagedConstraint != value) { introduceUnmanagedConstraint = value; OnPropertyChanged(); } } } bool stackAllocInitializers = true; /// /// Gets/Sets whether C# 7.3 stackalloc initializers should be used. /// [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.UseStackallocInitializerSyntax")] public bool StackAllocInitializers { get { return stackAllocInitializers; } set { if (stackAllocInitializers != value) { stackAllocInitializers = value; OnPropertyChanged(); } } } bool patternBasedFixedStatement = true; /// /// Gets/Sets whether C# 7.3 pattern based fixed statement should be used. /// [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.UsePatternBasedFixedStatement")] public bool PatternBasedFixedStatement { get { return patternBasedFixedStatement; } set { if (patternBasedFixedStatement != value) { patternBasedFixedStatement = value; OnPropertyChanged(); } } } bool tupleTypes = true; /// /// Gets/Sets whether tuple type syntax (int, string) /// should be used for System.ValueTuple. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseTupleTypeSyntax")] public bool TupleTypes { get { return tupleTypes; } set { if (tupleTypes != value) { tupleTypes = value; OnPropertyChanged(); } } } bool throwExpressions = true; /// /// Gets/Sets whether throw expressions should be used. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseThrowExpressions")] public bool ThrowExpressions { get { return throwExpressions; } set { if (throwExpressions != value) { throwExpressions = value; OnPropertyChanged(); } } } bool tupleConversions = true; /// /// Gets/Sets whether implicit conversions between tuples /// should be used in the decompiled output. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseImplicitConversionsBetweenTupleTypes")] public bool TupleConversions { get { return tupleConversions; } set { if (tupleConversions != value) { tupleConversions = value; OnPropertyChanged(); } } } bool tupleComparisons = true; /// /// Gets/Sets whether tuple comparisons should be detected. /// [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.DetectTupleComparisons")] public bool TupleComparisons { get { return tupleComparisons; } set { if (tupleComparisons != value) { tupleComparisons = value; OnPropertyChanged(); } } } bool namedArguments = true; /// /// Gets/Sets whether named arguments should be used. /// [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.UseNamedArguments")] public bool NamedArguments { get { return namedArguments; } set { if (namedArguments != value) { namedArguments = value; OnPropertyChanged(); } } } bool nonTrailingNamedArguments = true; /// /// Gets/Sets whether C# 7.2 non-trailing named arguments should be used. /// [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.UseNonTrailingNamedArguments")] public bool NonTrailingNamedArguments { get { return nonTrailingNamedArguments; } set { if (nonTrailingNamedArguments != value) { nonTrailingNamedArguments = value; OnPropertyChanged(); } } } bool optionalArguments = true; /// /// Gets/Sets whether optional arguments should be removed, if possible. /// [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.RemoveOptionalArgumentsIfPossible")] public bool OptionalArguments { get { return optionalArguments; } set { if (optionalArguments != value) { optionalArguments = value; OnPropertyChanged(); } } } bool expandParamsArguments = true; /// /// Gets/Sets whether to expand params arguments by replacing explicit array creation /// with individual values in method calls. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.ExpandParamsArguments")] public bool ExpandParamsArguments { get { return expandParamsArguments; } set { if (expandParamsArguments != value) { expandParamsArguments = value; OnPropertyChanged(); } } } bool localFunctions = true; /// /// Gets/Sets whether C# 7.0 local functions should be transformed. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.IntroduceLocalFunctions")] public bool LocalFunctions { get { return localFunctions; } set { if (localFunctions != value) { localFunctions = value; OnPropertyChanged(); } } } bool deconstruction = true; /// /// Gets/Sets whether C# 7.0 deconstruction should be detected. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.Deconstruction")] public bool Deconstruction { get { return deconstruction; } set { if (deconstruction != value) { deconstruction = value; OnPropertyChanged(); } } } bool patternMatching = true; /// /// Gets/Sets whether C# 7.0 pattern matching should be detected. /// [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.PatternMatching")] public bool PatternMatching { get { return patternMatching; } set { if (patternMatching != value) { patternMatching = value; OnPropertyChanged(); } } } bool recursivePatternMatching = true; /// /// Gets/Sets whether C# 8.0 recursive patterns should be detected. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.RecursivePatternMatching")] public bool RecursivePatternMatching { get { return recursivePatternMatching; } set { if (recursivePatternMatching != value) { recursivePatternMatching = value; OnPropertyChanged(); } } } bool patternCombinators = true; /// /// Gets/Sets whether C# 9.0 and, or, not patterns should be detected. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.PatternCombinators")] public bool PatternCombinators { get { return patternCombinators; } set { if (patternCombinators != value) { patternCombinators = value; OnPropertyChanged(); } } } bool relationalPatterns = true; /// /// Gets/Sets whether C# 9.0 relational patterns should be detected. /// [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.RelationalPatterns")] public bool RelationalPatterns { get { return relationalPatterns; } set { if (relationalPatterns != value) { relationalPatterns = value; OnPropertyChanged(); } } } bool staticLocalFunctions = true; /// /// Gets/Sets whether C# 8.0 static local functions should be transformed. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.IntroduceStaticLocalFunctions")] public bool StaticLocalFunctions { get { return staticLocalFunctions; } set { if (staticLocalFunctions != value) { staticLocalFunctions = value; OnPropertyChanged(); } } } bool ranges = true; /// /// Gets/Sets whether C# 8.0 index and range syntax should be used. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.Ranges")] public bool Ranges { get { return ranges; } set { if (ranges != value) { ranges = value; OnPropertyChanged(); } } } bool nullableReferenceTypes = true; /// /// Gets/Sets whether C# 8.0 nullable reference types are enabled. /// [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.NullableReferenceTypes")] public bool NullableReferenceTypes { get { return nullableReferenceTypes; } set { if (nullableReferenceTypes != value) { nullableReferenceTypes = value; OnPropertyChanged(); } } } bool showDebugInfo; [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.ShowInfoFromDebugSymbolsIfAvailable")] [Browsable(false)] public bool ShowDebugInfo { get { return showDebugInfo; } set { if (showDebugInfo != value) { showDebugInfo = value; OnPropertyChanged(); } } } #region Options to aid VB decompilation bool assumeArrayLengthFitsIntoInt32 = true; /// /// Gets/Sets whether the decompiler can assume that 'ldlen; conv.i4.ovf' /// does not throw an overflow exception. /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] public bool AssumeArrayLengthFitsIntoInt32 { get { return assumeArrayLengthFitsIntoInt32; } set { if (assumeArrayLengthFitsIntoInt32 != value) { assumeArrayLengthFitsIntoInt32 = value; OnPropertyChanged(); } } } bool introduceIncrementAndDecrement = true; /// /// Gets/Sets whether to use increment and decrement operators /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] public bool IntroduceIncrementAndDecrement { get { return introduceIncrementAndDecrement; } set { if (introduceIncrementAndDecrement != value) { introduceIncrementAndDecrement = value; OnPropertyChanged(); } } } bool makeAssignmentExpressions = true; /// /// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ; /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] public bool MakeAssignmentExpressions { get { return makeAssignmentExpressions; } set { if (makeAssignmentExpressions != value) { makeAssignmentExpressions = value; OnPropertyChanged(); } } } #endregion #region Options to aid F# decompilation bool removeDeadCode = false; [Category("DecompilerSettings.FSpecificOptions")] [Description("DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution")] public bool RemoveDeadCode { get { return removeDeadCode; } set { if (removeDeadCode != value) { removeDeadCode = value; OnPropertyChanged(); } } } bool removeDeadStores = false; [Category("DecompilerSettings.FSpecificOptions")] [Description("DecompilerSettings.RemoveDeadStores")] public bool RemoveDeadStores { get { return removeDeadStores; } set { if (removeDeadStores != value) { removeDeadStores = value; OnPropertyChanged(); } } } #endregion #region Assembly Load and Resolve options bool loadInMemory = false; [Browsable(false)] public bool LoadInMemory { get { return loadInMemory; } set { if (loadInMemory != value) { loadInMemory = value; OnPropertyChanged(); } } } bool throwOnAssemblyResolveErrors = true; [Browsable(false)] public bool ThrowOnAssemblyResolveErrors { get { return throwOnAssemblyResolveErrors; } set { if (throwOnAssemblyResolveErrors != value) { throwOnAssemblyResolveErrors = value; OnPropertyChanged(); } } } bool applyWindowsRuntimeProjections = true; [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.ApplyWindowsRuntimeProjectionsOnLoadedAssemblies")] public bool ApplyWindowsRuntimeProjections { get { return applyWindowsRuntimeProjections; } set { if (applyWindowsRuntimeProjections != value) { applyWindowsRuntimeProjections = value; OnPropertyChanged(); } } } bool autoLoadAssemblyReferences = true; [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AutoLoadAssemblyReferences")] public bool AutoLoadAssemblyReferences { get { return autoLoadAssemblyReferences; } set { if (autoLoadAssemblyReferences != value) { autoLoadAssemblyReferences = value; OnPropertyChanged(); } } } #endregion bool forStatement = true; /// /// Gets/sets whether the decompiler should produce for loops. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.ForStatement")] public bool ForStatement { get { return forStatement; } set { if (forStatement != value) { forStatement = value; OnPropertyChanged(); } } } bool doWhileStatement = true; /// /// Gets/sets whether the decompiler should produce do-while loops. /// [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DoWhileStatement")] public bool DoWhileStatement { get { return doWhileStatement; } set { if (doWhileStatement != value) { doWhileStatement = value; OnPropertyChanged(); } } } bool refReadOnlyParameters = true; /// /// Gets/sets whether RequiresLocationAttribute on parameters should be replaced with 'ref readonly' modifiers. /// [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.RefReadOnlyParameters")] public bool RefReadOnlyParameters { get { return refReadOnlyParameters; } set { if (refReadOnlyParameters != value) { refReadOnlyParameters = value; OnPropertyChanged(); } } } bool usePrimaryConstructorSyntaxForNonRecordTypes = true; /// /// Use primary constructor syntax with classes and structs. /// [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.UsePrimaryConstructorSyntaxForNonRecordTypes")] public bool UsePrimaryConstructorSyntaxForNonRecordTypes { get { return usePrimaryConstructorSyntaxForNonRecordTypes; } set { if (usePrimaryConstructorSyntaxForNonRecordTypes != value) { usePrimaryConstructorSyntaxForNonRecordTypes = value; OnPropertyChanged(); } } } bool inlineArrays = true; /// /// Gets/Sets whether C# 12.0 inline array uses should be transformed. /// [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.InlineArrays")] public bool InlineArrays { get { return inlineArrays; } set { if (inlineArrays != value) { inlineArrays = value; OnPropertyChanged(); } } } bool extensionMembers = true; /// /// Gets/Sets whether C# 14.0 extension members should be transformed. /// [Category("C# 14.0 / VS 202x.yy")] [Description("DecompilerSettings.ExtensionMembers")] public bool ExtensionMembers { get { return extensionMembers; } set { if (extensionMembers != value) { extensionMembers = value; OnPropertyChanged(); } } } bool firstClassSpanTypes = true; /// /// Gets/Sets whether (ReadOnly)Span<T> should be treated like built-in types. /// [Category("C# 14.0 / VS 202x.yy")] [Description("DecompilerSettings.FirstClassSpanTypes")] public bool FirstClassSpanTypes { get { return firstClassSpanTypes; } set { if (firstClassSpanTypes != value) { firstClassSpanTypes = value; OnPropertyChanged(); } } } bool separateLocalVariableDeclarations = false; /// /// Gets/sets whether the decompiler should separate local variable declarations /// from their initialization. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.SeparateLocalVariableDeclarations")] public bool SeparateLocalVariableDeclarations { get { return separateLocalVariableDeclarations; } set { if (separateLocalVariableDeclarations != value) { separateLocalVariableDeclarations = value; OnPropertyChanged(); } } } bool useSdkStyleProjectFormat = true; /// /// Gets or sets a value indicating whether the new SDK style format /// shall be used for the generated project files. /// [Category("DecompilerSettings.ProjectExport")] [Description("DecompilerSettings.UseSdkStyleProjectFormat")] public bool UseSdkStyleProjectFormat { get { return useSdkStyleProjectFormat; } set { if (useSdkStyleProjectFormat != value) { useSdkStyleProjectFormat = value; OnPropertyChanged(); } } } bool useNestedDirectoriesForNamespaces; /// /// Gets/sets whether namespaces and namespace-like identifiers should be split at '.' /// and each part should produce a new level of nesting in the output directory structure. /// [Category("DecompilerSettings.ProjectExport")] [Description("DecompilerSettings.UseNestedDirectoriesForNamespaces")] public bool UseNestedDirectoriesForNamespaces { get { return useNestedDirectoriesForNamespaces; } set { if (useNestedDirectoriesForNamespaces != value) { useNestedDirectoriesForNamespaces = value; OnPropertyChanged(); } } } bool aggressiveScalarReplacementOfAggregates = false; [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AggressiveScalarReplacementOfAggregates")] // TODO : Remove once https://github.com/icsharpcode/ILSpy/issues/2032 is fixed. #if !DEBUG [Browsable(false)] #endif public bool AggressiveScalarReplacementOfAggregates { get { return aggressiveScalarReplacementOfAggregates; } set { if (aggressiveScalarReplacementOfAggregates != value) { aggressiveScalarReplacementOfAggregates = value; OnPropertyChanged(); } } } bool aggressiveInlining = false; /// /// If set to false (the default), the decompiler will inline local variables only when they occur /// in a context where the C# compiler is known to emit compiler-generated locals. /// If set to true, the decompiler will inline local variables whenever possible. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AggressiveInlining")] public bool AggressiveInlining { get { return aggressiveInlining; } set { if (aggressiveInlining != value) { aggressiveInlining = value; OnPropertyChanged(); } } } bool alwaysUseGlobal = false; /// /// Always fully qualify namespaces using the "global::" prefix. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysUseGlobal")] public bool AlwaysUseGlobal { get { return alwaysUseGlobal; } set { if (alwaysUseGlobal != value) { alwaysUseGlobal = value; OnPropertyChanged(); } } } bool alwaysMoveInitializer = false; /// /// If set to false (the default), the decompiler will move field initializers at the start of constructors /// to their respective field declarations (TransformFieldAndConstructorInitializers) only when the declaring /// type has BeforeFieldInit or the member IsConst. /// If set true, the decompiler will always move them regardless of the flags. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysMoveInitializer")] public bool AlwaysMoveInitializer { get { return alwaysMoveInitializer; } set { if (alwaysMoveInitializer != value) { alwaysMoveInitializer = value; OnPropertyChanged(); } } } bool sortCustomAttributes = false; /// /// Sort custom attributes. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.SortCustomAttributes")] public bool SortCustomAttributes { get { return sortCustomAttributes; } set { if (sortCustomAttributes != value) { sortCustomAttributes = value; OnPropertyChanged(); } } } bool checkForOverflowUnderflow = false; /// /// Check for overflow and underflow in operators. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.CheckForOverflowUnderflow")] public bool CheckForOverflowUnderflow { get { return checkForOverflowUnderflow; } set { if (checkForOverflowUnderflow != value) { checkForOverflowUnderflow = value; OnPropertyChanged(); } } } CSharpFormattingOptions csharpFormattingOptions; [Browsable(false)] public CSharpFormattingOptions CSharpFormattingOptions { get { if (csharpFormattingOptions == null) { csharpFormattingOptions = FormattingOptionsFactory.CreateAllman(); csharpFormattingOptions.IndentSwitchBody = false; csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapIfTooLong; csharpFormattingOptions.AutoPropertyFormatting = PropertyFormatting.SingleLine; } return csharpFormattingOptions; } set { if (value == null) throw new ArgumentNullException(); if (csharpFormattingOptions != value) { csharpFormattingOptions = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public virtual DecompilerSettings Clone() { DecompilerSettings settings = (DecompilerSettings)MemberwiseClone(); if (csharpFormattingOptions != null) settings.csharpFormattingOptions = csharpFormattingOptions.Clone(); settings.PropertyChanged = null; return settings; } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs ================================================ // 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 // 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.Linq; using System.Reflection.Metadata; using System.Text; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Disassembler { public enum ILNameSyntax { /// /// class/valuetype + TypeName (built-in types use keyword syntax) /// Signature, /// /// Like signature, but always refers to type parameters using their position /// SignatureNoNamedTypeParameters, /// /// [assembly]Full.Type.Name (even for built-in types) /// TypeName, /// /// Name (but built-in types use keyword syntax) /// ShortTypeName } public static class DisassemblerHelpers { static readonly char[] _validNonLetterIdentifierCharacter = new char[] { '_', '$', '@', '?', '`', '.' }; public static string OffsetToString(int offset) { return string.Format("IL_{0:x4}", offset); } public static string OffsetToString(long offset) { return string.Format("IL_{0:x4}", offset); } public static void WriteOffsetReference(ITextOutput writer, int? offset) { if (offset == null) writer.Write("null"); else writer.WriteLocalReference(OffsetToString(offset.Value), offset); } public static void WriteTo(this ExceptionRegion exceptionHandler, MetadataFile module, MetadataGenericContext context, ITextOutput writer) { writer.Write(".try "); WriteOffsetReference(writer, exceptionHandler.TryOffset); writer.Write('-'); WriteOffsetReference(writer, exceptionHandler.TryOffset + exceptionHandler.TryLength); writer.Write(' '); writer.Write(exceptionHandler.Kind.ToString().ToLowerInvariant()); if (exceptionHandler.FilterOffset != -1) { writer.Write(' '); WriteOffsetReference(writer, exceptionHandler.FilterOffset); writer.Write(" handler "); } if (!exceptionHandler.CatchType.IsNil) { writer.Write(' '); exceptionHandler.CatchType.WriteTo(module, writer, context); } writer.Write(' '); WriteOffsetReference(writer, exceptionHandler.HandlerOffset); writer.Write('-'); WriteOffsetReference(writer, exceptionHandler.HandlerOffset + exceptionHandler.HandlerLength); } static string ToInvariantCultureString(object value) { IConvertible convertible = value as IConvertible; return (null != convertible) ? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture) : value.ToString(); } static bool IsValidIdentifierCharacter(char c) => char.IsLetterOrDigit(c) || _validNonLetterIdentifierCharacter.IndexOf(c) >= 0; static bool IsValidIdentifier(string identifier) { if (string.IsNullOrEmpty(identifier)) return false; if (char.IsDigit(identifier[0])) return false; // As a special case, .ctor and .cctor are valid despite starting with a dot if (identifier[0] == '.') return identifier == ".ctor" || identifier == ".cctor"; if (identifier.Contains("..")) return false; if (Metadata.ILOpCodeExtensions.ILKeywords.Contains(identifier)) return false; return identifier.All(IsValidIdentifierCharacter); } public static string Escape(string identifier) { if (IsValidIdentifier(identifier)) { return identifier; } // The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence, // but we follow Microsoft's ILDasm and use \'. return $"'{EscapeString(identifier).Replace("'", "\\'")}'"; } public static void WriteParameterReference(ITextOutput writer, MetadataReader metadata, MethodDefinitionHandle handle, int index) { string name = GetParameterName(index); if (name == null) { writer.WriteLocalReference(index.ToString(), "param_" + index); } else { writer.WriteLocalReference(name, "param_" + index); } string GetParameterName(int parameterNumber) { var methodDefinition = metadata.GetMethodDefinition(handle); if ((methodDefinition.Attributes & System.Reflection.MethodAttributes.Static) != 0) { parameterNumber++; } foreach (var p in methodDefinition.GetParameters()) { var param = metadata.GetParameter(p); if (param.SequenceNumber < parameterNumber) { continue; } else if (param.SequenceNumber == parameterNumber) { if (param.Name.IsNil) return null; return Escape(metadata.GetString(param.Name)); } else { break; } } return null; } } public static void WriteVariableReference(ITextOutput writer, MetadataReader metadata, MethodDefinitionHandle handle, int index) { writer.WriteLocalReference(index.ToString(), "loc_" + index); } public static void WriteOperand(ITextOutput writer, object operand) { if (operand == null) throw new ArgumentNullException(nameof(operand)); string s = operand as string; if (s != null) { WriteOperand(writer, s); } else if (operand is char) { writer.Write(((int)(char)operand).ToString()); } else if (operand is float) { WriteOperand(writer, (float)operand); } else if (operand is double) { WriteOperand(writer, (double)operand); } else if (operand is bool) { writer.Write((bool)operand ? "true" : "false"); } else { s = ToInvariantCultureString(operand); writer.Write(s); } } public static void WriteOperand(ITextOutput writer, long val) { writer.Write(ToInvariantCultureString(val)); } public static void WriteOperand(ITextOutput writer, float val) { if (val == 0) { if (1 / val == float.NegativeInfinity) { // negative zero is a special case writer.Write('-'); } writer.Write("0.0"); } else if (float.IsInfinity(val) || float.IsNaN(val)) { byte[] data = BitConverter.GetBytes(val); writer.Write('('); for (int i = 0; i < data.Length; i++) { if (i > 0) writer.Write(' '); writer.Write(data[i].ToString("X2")); } writer.Write(')'); } else { writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } } public static void WriteOperand(ITextOutput writer, double val) { if (val == 0) { if (1 / val == double.NegativeInfinity) { // negative zero is a special case writer.Write('-'); } writer.Write("0.0"); } else if (double.IsInfinity(val) || double.IsNaN(val)) { byte[] data = BitConverter.GetBytes(val); writer.Write('('); for (int i = 0; i < data.Length; i++) { if (i > 0) writer.Write(' '); writer.Write(data[i].ToString("X2")); } writer.Write(')'); } else { writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } } public static void WriteOperand(ITextOutput writer, string operand) { writer.Write('"'); writer.Write(EscapeString(operand)); writer.Write('"'); } public static string EscapeString(string str) { var sb = new StringBuilder(); foreach (char ch in str) { switch (ch) { case '"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\0': sb.Append("\\0"); break; case '\a': sb.Append("\\a"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\v': sb.Append("\\v"); break; default: // print control characters and uncommon white spaces as numbers if (char.IsControl(ch) || char.IsSurrogate(ch) || (char.IsWhiteSpace(ch) && ch != ' ')) { sb.AppendFormat("\\u{0:x4}", (int)ch); } else { sb.Append(ch); } break; } } return sb.ToString(); } public static string PrimitiveTypeName(string fullName) { switch (fullName) { case "System.SByte": return "int8"; case "System.Int16": return "int16"; case "System.Int32": return "int32"; case "System.Int64": return "int64"; case "System.Byte": return "uint8"; case "System.UInt16": return "uint16"; case "System.UInt32": return "uint32"; case "System.UInt64": return "uint64"; case "System.Single": return "float32"; case "System.Double": return "float64"; case "System.Void": return "void"; case "System.Boolean": return "bool"; case "System.String": return "string"; case "System.Char": return "char"; case "System.Object": return "object"; case "System.IntPtr": return "native int"; default: return null; } } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/DisassemblerSignatureTypeProvider.cs ================================================ // 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 // 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.Immutable; using System.Reflection.Metadata; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Disassembler { public class DisassemblerSignatureTypeProvider : ISignatureTypeProvider, MetadataGenericContext> { readonly MetadataFile module; readonly MetadataReader metadata; readonly ITextOutput output; public DisassemblerSignatureTypeProvider(MetadataFile module, ITextOutput output) { this.module = module ?? throw new ArgumentNullException(nameof(module)); this.output = output ?? throw new ArgumentNullException(nameof(output)); this.metadata = module.Metadata; } public Action GetArrayType(Action elementType, ArrayShape shape) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; elementType(syntaxForElementTypes); output.Write('['); for (int i = 0; i < shape.Rank; i++) { if (i > 0) output.Write(", "); if (i < shape.LowerBounds.Length || i < shape.Sizes.Length) { int lower = 0; if (i < shape.LowerBounds.Length) { lower = shape.LowerBounds[i]; output.Write(lower.ToString()); } output.Write("..."); if (i < shape.Sizes.Length) output.Write((lower + shape.Sizes[i] - 1).ToString()); } } output.Write(']'); }; } public Action GetByReferenceType(Action elementType) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; elementType(syntaxForElementTypes); output.Write('&'); }; } public Action GetFunctionPointerType(MethodSignature> signature) { return syntax => { output.Write("method "); signature.Header.WriteTo(output); signature.ReturnType(syntax); output.Write(" *("); for (int i = 0; i < signature.ParameterTypes.Length; i++) { if (i > 0) output.Write(", "); signature.ParameterTypes[i](syntax); } output.Write(')'); }; } public Action GetGenericInstantiation(Action genericType, ImmutableArray> typeArguments) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; genericType(syntaxForElementTypes); output.Write('<'); for (int i = 0; i < typeArguments.Length; i++) { if (i > 0) output.Write(", "); typeArguments[i](syntaxForElementTypes); } output.Write('>'); }; } public Action GetGenericMethodParameter(MetadataGenericContext genericContext, int index) { return syntax => { output.Write("!!"); WriteTypeParameter(genericContext.GetGenericMethodTypeParameterHandleOrNull(index), index, syntax); }; } public Action GetGenericTypeParameter(MetadataGenericContext genericContext, int index) { return syntax => { output.Write("!"); WriteTypeParameter(genericContext.GetGenericTypeParameterHandleOrNull(index), index, syntax); }; } void WriteTypeParameter(GenericParameterHandle paramRef, int index, ILNameSyntax syntax) { if (paramRef.IsNil || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) output.Write(index.ToString()); else { var param = metadata.GetGenericParameter(paramRef); if (param.Name.IsNil) output.Write(param.Index.ToString()); else output.Write(DisassemblerHelpers.Escape(metadata.GetString(param.Name))); } } public Action GetModifiedType(Action modifier, Action unmodifiedType, bool isRequired) { return syntax => { unmodifiedType(syntax); if (isRequired) output.Write(" modreq"); else output.Write(" modopt"); output.Write('('); modifier(ILNameSyntax.TypeName); output.Write(')'); }; } public Action GetPinnedType(Action elementType) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; elementType(syntaxForElementTypes); output.Write(" pinned"); }; } public Action GetPointerType(Action elementType) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; elementType(syntaxForElementTypes); output.Write('*'); }; } public Action GetPrimitiveType(PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.SByte: return syntax => output.Write("int8"); case PrimitiveTypeCode.Int16: return syntax => output.Write("int16"); case PrimitiveTypeCode.Int32: return syntax => output.Write("int32"); case PrimitiveTypeCode.Int64: return syntax => output.Write("int64"); case PrimitiveTypeCode.Byte: return syntax => output.Write("uint8"); case PrimitiveTypeCode.UInt16: return syntax => output.Write("uint16"); case PrimitiveTypeCode.UInt32: return syntax => output.Write("uint32"); case PrimitiveTypeCode.UInt64: return syntax => output.Write("uint64"); case PrimitiveTypeCode.Single: return syntax => output.Write("float32"); case PrimitiveTypeCode.Double: return syntax => output.Write("float64"); case PrimitiveTypeCode.Void: return syntax => output.Write("void"); case PrimitiveTypeCode.Boolean: return syntax => output.Write("bool"); case PrimitiveTypeCode.String: return syntax => output.Write("string"); case PrimitiveTypeCode.Char: return syntax => output.Write("char"); case PrimitiveTypeCode.Object: return syntax => output.Write("object"); case PrimitiveTypeCode.IntPtr: return syntax => output.Write("native int"); case PrimitiveTypeCode.UIntPtr: return syntax => output.Write("native uint"); case PrimitiveTypeCode.TypedReference: return syntax => output.Write("typedref"); default: throw new ArgumentOutOfRangeException(); } } public Action GetSZArrayType(Action elementType) { return syntax => { var syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; elementType(syntaxForElementTypes); output.Write('['); output.Write(']'); }; } public Action GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { return syntax => { switch (rawTypeKind) { case 0x00: break; case 0x11: output.Write("valuetype "); break; case 0x12: output.Write("class "); break; default: throw new BadImageFormatException($"Unexpected rawTypeKind: {rawTypeKind} (0x{rawTypeKind:x})"); } ((EntityHandle)handle).WriteTo(module, output, default); }; } public Action GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { return syntax => { switch (rawTypeKind) { case 0x00: break; case 0x11: output.Write("valuetype "); break; case 0x12: output.Write("class "); break; default: throw new BadImageFormatException($"Unexpected rawTypeKind: {rawTypeKind} (0x{rawTypeKind:x})"); } ((EntityHandle)handle).WriteTo(module, output, default); }; } public Action GetTypeFromSpecification(MetadataReader reader, MetadataGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/IEntityProcessor.cs ================================================ // Copyright (c) 2022 Tom Englert // // 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. #nullable enable using System.Collections.Generic; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Disassembler { public interface IEntityProcessor { IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items); } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/ILParser.cs ================================================ // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Disassembler { public static class ILParser { public static ILOpCode DecodeOpCode(this ref BlobReader blob) { byte opCodeByte = blob.ReadByte(); if (opCodeByte == 0xFE && blob.RemainingBytes >= 1) { return (ILOpCode)(0xFE00 + blob.ReadByte()); } else { return (ILOpCode)opCodeByte; } } internal static int OperandSize(this OperandType opType) { switch (opType) { // 64-bit case OperandType.I8: case OperandType.R: return 8; // 32-bit case OperandType.BrTarget: case OperandType.Field: case OperandType.Method: case OperandType.I: case OperandType.Sig: case OperandType.String: case OperandType.Tok: case OperandType.Type: case OperandType.ShortR: return 4; // (n + 1) * 32-bit case OperandType.Switch: return 4; // minimum 4, usually more case OperandType.Variable: // 16-bit return 2; // 8-bit case OperandType.ShortVariable: case OperandType.ShortBrTarget: case OperandType.ShortI: return 1; default: return 0; } } public static void SkipOperand(this ref BlobReader blob, ILOpCode opCode) { var opType = opCode.GetOperandType(); int operandSize; if (opType == OperandType.Switch) { uint n = blob.RemainingBytes >= 4 ? blob.ReadUInt32() : uint.MaxValue; if (n < int.MaxValue / 4) { operandSize = (int)(n * 4); } else { operandSize = int.MaxValue; } } else { operandSize = opType.OperandSize(); } if (operandSize <= blob.RemainingBytes) { blob.Offset += operandSize; } else { // ignore missing/partial operand at end of body blob.Offset = blob.Length; } } public static int DecodeBranchTarget(this ref BlobReader blob, ILOpCode opCode) { int opSize = opCode.GetBranchOperandSize(); if (opSize <= blob.RemainingBytes) { int relOffset = opSize == 4 ? blob.ReadInt32() : blob.ReadSByte(); return unchecked(relOffset + blob.Offset); } else { return int.MinValue; } } public static int[] DecodeSwitchTargets(this ref BlobReader blob) { if (blob.RemainingBytes < 4) { blob.Offset += blob.RemainingBytes; return new int[0]; } uint numTargets = blob.ReadUInt32(); bool numTargetOverflow = false; if (numTargets > blob.RemainingBytes / 4) { numTargets = (uint)(blob.RemainingBytes / 4); numTargetOverflow = true; } int[] targets = new int[numTargets]; int offset = blob.Offset + 4 * targets.Length; for (int i = 0; i < targets.Length; i++) { targets[i] = unchecked(blob.ReadInt32() + offset); } if (numTargetOverflow) { blob.Offset += blob.RemainingBytes; } return targets; } public static string DecodeUserString(this ref BlobReader blob, MetadataReader metadata) { return metadata.GetUserString(MetadataTokens.UserStringHandle(blob.ReadInt32())); } public static int DecodeIndex(this ref BlobReader blob, ILOpCode opCode) { switch (opCode.GetOperandType()) { case OperandType.ShortVariable: return blob.ReadByte(); case OperandType.Variable: return blob.ReadUInt16(); default: throw new ArgumentException($"{opCode} not supported!"); } } public static bool IsReturn(this ILOpCode opCode) { return opCode == ILOpCode.Ret || opCode == ILOpCode.Endfilter || opCode == ILOpCode.Endfinally; } public static int GetHeaderSize(BlobReader bodyBlockReader) { byte header = bodyBlockReader.ReadByte(); if ((header & 3) == 3) { // fat ushort largeHeader = (ushort)((bodyBlockReader.ReadByte() << 8) | header); return (byte)(largeHeader >> 12) * 4; } else { // tiny return 1; } } public static void SetBranchTargets(ref BlobReader blob, BitSet branchTargets) { while (blob.RemainingBytes > 0) { var opCode = DecodeOpCode(ref blob); if (opCode == ILOpCode.Switch) { foreach (var target in DecodeSwitchTargets(ref blob)) { if (target >= 0 && target < blob.Length) branchTargets.Set(target); } } else if (opCode.IsBranch()) { int target = DecodeBranchTarget(ref blob, opCode); if (target >= 0 && target < blob.Length) branchTargets.Set(target); } else { SkipOperand(ref blob, opCode); } } } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/ILStructure.cs ================================================ // 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 // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Disassembler { /// /// Specifies the type of an IL structure. /// public enum ILStructureType { /// /// The root block of the method /// Root, /// /// A nested control structure representing a loop. /// Loop, /// /// A nested control structure representing a try block. /// Try, /// /// A nested control structure representing a catch, finally, or fault block. /// Handler, /// /// A nested control structure representing an exception filter block. /// Filter } /// /// An IL structure. /// public class ILStructure { public readonly MetadataFile Module; public readonly MethodDefinitionHandle MethodHandle; public readonly MetadataGenericContext GenericContext; public readonly ILStructureType Type; /// /// Start position of the structure. /// public readonly int StartOffset; /// /// End position of the structure. (exclusive) /// public readonly int EndOffset; /// /// The exception handler associated with the Try, Filter or Handler block. /// public readonly ExceptionRegion ExceptionHandler; /// /// The loop's entry point. /// public readonly int LoopEntryPointOffset; /// /// The list of child structures. /// public readonly List Children = new List(); public ILStructure(MetadataFile module, MethodDefinitionHandle handle, MetadataGenericContext genericContext, MethodBodyBlock body) : this(module, handle, genericContext, ILStructureType.Root, 0, body.GetILReader().Length) { // Build the tree of exception structures: for (int i = 0; i < body.ExceptionRegions.Length; i++) { ExceptionRegion eh = body.ExceptionRegions[i]; if (!body.ExceptionRegions.Take(i).Any(oldEh => oldEh.TryOffset == eh.TryOffset && oldEh.TryLength == eh.TryLength)) AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Try, eh.TryOffset, eh.TryOffset + eh.TryLength, eh)); if (eh.Kind == ExceptionRegionKind.Filter) AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Filter, eh.FilterOffset, eh.HandlerOffset, eh)); AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Handler, eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength, eh)); } // Very simple loop detection: look for backward branches (var allBranches, var isAfterUnconditionalBranch) = FindAllBranches(body.GetILReader()); // We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;") for (int i = allBranches.Count - 1; i >= 0; i--) { int loopEnd = allBranches[i].Source.End; int loopStart = allBranches[i].Target; if (loopStart < loopEnd) { // We found a backward branch. This is a potential loop. // Check that is has only one entry point: int entryPoint = -1; // entry point is first instruction in loop if prev inst isn't an unconditional branch if (loopStart > 0 && !isAfterUnconditionalBranch[loopStart]) entryPoint = allBranches[i].Target; bool multipleEntryPoints = false; foreach (var branch in allBranches) { if (branch.Source.Start < loopStart || branch.Source.Start >= loopEnd) { if (loopStart <= branch.Target && branch.Target < loopEnd) { // jump from outside the loop into the loop if (entryPoint < 0) entryPoint = branch.Target; else if (branch.Target != entryPoint) multipleEntryPoints = true; } } } if (!multipleEntryPoints) { AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Loop, loopStart, loopEnd, entryPoint)); } } } SortChildren(); } public ILStructure(MetadataFile module, MethodDefinitionHandle handle, MetadataGenericContext genericContext, ILStructureType type, int startOffset, int endOffset, ExceptionRegion handler = default) { Debug.Assert(startOffset < endOffset); this.Module = module; this.MethodHandle = handle; this.GenericContext = genericContext; this.Type = type; this.StartOffset = startOffset; this.EndOffset = endOffset; this.ExceptionHandler = handler; } public ILStructure(MetadataFile module, MethodDefinitionHandle handle, MetadataGenericContext genericContext, ILStructureType type, int startOffset, int endOffset, int loopEntryPoint) { Debug.Assert(startOffset < endOffset); this.Module = module; this.MethodHandle = handle; this.GenericContext = genericContext; this.Type = type; this.StartOffset = startOffset; this.EndOffset = endOffset; this.LoopEntryPointOffset = loopEntryPoint; } bool AddNestedStructure(ILStructure newStructure) { // special case: don't consider the loop-like structure of "continue;" statements to be nested loops if (this.Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == this.StartOffset) return false; if (newStructure.StartOffset < 0) return false; // use <= for end-offset comparisons because both end and EndOffset are exclusive Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset); foreach (ILStructure child in this.Children) { if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) { return child.AddNestedStructure(newStructure); } else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) { // child and newStructure overlap if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) { // Invalid nesting, can't build a tree. -> Don't add the new structure. return false; } } } // Move existing structures into the new structure: for (int i = 0; i < this.Children.Count; i++) { ILStructure child = this.Children[i]; if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) { this.Children.RemoveAt(i--); newStructure.Children.Add(child); } } // Add the structure here: this.Children.Add(newStructure); return true; } struct Branch { public Interval Source; public int Target; public Branch(int start, int end, int target) { this.Source = new Interval(start, end); this.Target = target; } public override string ToString() { return $"[Branch Source={Source}, Target={Target}]"; } } /// /// Finds all branches. Returns list of source offset->target offset mapping. /// Multiple entries for the same source offset are possible (switch statements). /// The result is sorted by source offset. /// (List Branches, BitSet IsAfterUnconditionalBranch) FindAllBranches(BlobReader body) { var result = new List(); var bitset = new BitSet(body.Length + 1); body.Reset(); int target; while (body.RemainingBytes > 0) { var offset = body.Offset; int endOffset; var thisOpCode = body.DecodeOpCode(); switch (thisOpCode.GetOperandType()) { case OperandType.BrTarget: case OperandType.ShortBrTarget: target = ILParser.DecodeBranchTarget(ref body, thisOpCode); endOffset = body.Offset; result.Add(new Branch(offset, endOffset, target)); bitset[endOffset] = IsUnconditionalBranch(thisOpCode); break; case OperandType.Switch: var targets = ILParser.DecodeSwitchTargets(ref body); foreach (int t in targets) result.Add(new Branch(offset, body.Offset, t)); break; default: ILParser.SkipOperand(ref body, thisOpCode); bitset[body.Offset] = IsUnconditionalBranch(thisOpCode); break; } } return (result, bitset); } static bool IsUnconditionalBranch(ILOpCode opCode) { switch (opCode) { case ILOpCode.Br: case ILOpCode.Br_s: case ILOpCode.Ret: case ILOpCode.Endfilter: case ILOpCode.Endfinally: case ILOpCode.Throw: case ILOpCode.Rethrow: case ILOpCode.Leave: case ILOpCode.Leave_s: return true; default: return false; } } void SortChildren() { Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset)); foreach (ILStructure child in Children) child.SortChildren(); } /// /// Gets the innermost structure containing the specified offset. /// public ILStructure GetInnermost(int offset) { Debug.Assert(StartOffset <= offset && offset < EndOffset); foreach (ILStructure child in this.Children) { if (child.StartOffset <= offset && offset < child.EndOffset) return child.GetInnermost(offset); } return this; } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs ================================================ // 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 // 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.Collections.Immutable; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Disassembler { /// /// Disassembles a method body. /// public class MethodBodyDisassembler { readonly ITextOutput output; readonly CancellationToken cancellationToken; /// /// Show .try/finally as blocks in IL code; indent loops. /// public bool DetectControlStructure { get; set; } = true; /// /// Show sequence points if debug information is loaded in Cecil. /// public bool ShowSequencePoints { get; set; } /// /// Show metadata tokens for instructions with token operands. /// public bool ShowMetadataTokens { get; set; } /// /// Show metadata tokens for instructions with token operands in base 10. /// public bool ShowMetadataTokensInBase10 { get; set; } /// /// Show raw RVA offset and bytes before each instruction. /// public bool ShowRawRVAOffsetAndBytes { get; set; } /// /// Optional provider for sequence points. /// public IDebugInfoProvider DebugInfo { get; set; } IList sequencePoints; int nextSequencePointIndex; // cache info MetadataFile module; MetadataReader metadata; MetadataGenericContext genericContext; DisassemblerSignatureTypeProvider signatureDecoder; public MethodBodyDisassembler(ITextOutput output, CancellationToken cancellationToken) { this.output = output ?? throw new ArgumentNullException(nameof(output)); this.cancellationToken = cancellationToken; } public virtual void Disassemble(MetadataFile module, MethodDefinitionHandle handle) { this.module = module ?? throw new ArgumentNullException(nameof(module)); metadata = module.Metadata; genericContext = new MetadataGenericContext(handle, module); signatureDecoder = new DisassemblerSignatureTypeProvider(module, output); var methodDefinition = metadata.GetMethodDefinition(handle); // start writing IL code output.WriteLine("// Method begins at RVA 0x{0:x4}", methodDefinition.RelativeVirtualAddress); if (methodDefinition.RelativeVirtualAddress == 0) { output.WriteLine("// Header size: {0}", 0); output.WriteLine("// Code size: {0} (0x{0:x})", 0); output.WriteLine(".maxstack {0}", 0); output.WriteLine(); return; } MethodBodyBlock body; BlobReader bodyBlockReader; try { body = module.GetMethodBody(methodDefinition.RelativeVirtualAddress); bodyBlockReader = module.GetSectionData(methodDefinition.RelativeVirtualAddress).GetReader(); } catch (BadImageFormatException ex) { output.WriteLine("// {0}", ex.Message); return; } var blob = body.GetILReader(); int headerSize = ILParser.GetHeaderSize(bodyBlockReader); output.WriteLine("// Header size: {0}", headerSize); output.WriteLine("// Code size: {0} (0x{0:x})", blob.Length); output.WriteLine(".maxstack {0}", body.MaxStack); var entrypointHandle = MetadataTokens.MethodDefinitionHandle(module.CorHeader?.EntryPointTokenOrRelativeVirtualAddress ?? 0); if (handle == entrypointHandle) output.WriteLine(".entrypoint"); DisassembleLocalsBlock(handle, body); output.WriteLine(); sequencePoints = DebugInfo?.GetSequencePoints(handle) ?? EmptyList.Instance; nextSequencePointIndex = 0; if (DetectControlStructure && blob.Length > 0) { blob.Reset(); BitSet branchTargets = new(blob.Length); ILParser.SetBranchTargets(ref blob, branchTargets); blob.Reset(); WriteStructureBody(new ILStructure(module, handle, genericContext, body), branchTargets, ref blob, methodDefinition.RelativeVirtualAddress + headerSize); } else { while (blob.RemainingBytes > 0) { cancellationToken.ThrowIfCancellationRequested(); WriteInstruction(output, module, handle, ref blob, methodDefinition.RelativeVirtualAddress); } WriteExceptionHandlers(module, handle, body); } sequencePoints = null; } void DisassembleLocalsBlock(MethodDefinitionHandle method, MethodBodyBlock body) { if (body.LocalSignature.IsNil) return; output.Write(".locals"); WriteMetadataToken(body.LocalSignature, spaceBefore: true); if (body.LocalVariablesInitialized) output.Write(" init"); var blob = metadata.GetStandaloneSignature(body.LocalSignature); var signature = ImmutableArray>.Empty; try { if (blob.GetKind() == StandaloneSignatureKind.LocalVariables) { signature = blob.DecodeLocalSignature(signatureDecoder, genericContext); } else { output.Write(" /* wrong signature kind */"); } } catch (BadImageFormatException ex) { output.Write($" /* {ex.Message} */"); } output.Write(' '); output.WriteLine("("); output.Indent(); int index = 0; foreach (var v in signature) { output.WriteLocalReference("[" + index + "]", "loc_" + index, isDefinition: true); output.Write(' '); v(ILNameSyntax.TypeName); if (DebugInfo != null && DebugInfo.TryGetName(method, index, out var name)) { output.Write(" " + DisassemblerHelpers.Escape(name)); } if (index + 1 < signature.Length) output.Write(','); output.WriteLine(); index++; } output.Unindent(); output.WriteLine(")"); } internal void WriteExceptionHandlers(MetadataFile module, MethodDefinitionHandle handle, MethodBodyBlock body) { this.module = module; metadata = module.Metadata; genericContext = new MetadataGenericContext(handle, module); signatureDecoder = new DisassemblerSignatureTypeProvider(module, output); var handlers = body.ExceptionRegions; if (!handlers.IsEmpty) { output.WriteLine(); foreach (var eh in handlers) { eh.WriteTo(module, genericContext, output); output.WriteLine(); } } } void WriteStructureHeader(ILStructure s) { switch (s.Type) { case ILStructureType.Loop: output.Write("// loop start"); if (s.LoopEntryPointOffset >= 0) { output.Write(" (head: "); DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPointOffset); output.Write(')'); } output.WriteLine(); break; case ILStructureType.Try: output.WriteLine(".try"); output.WriteLine("{"); break; case ILStructureType.Handler: switch (s.ExceptionHandler.Kind) { case ExceptionRegionKind.Filter: // handler block of filter block has no header break; case ExceptionRegionKind.Catch: output.Write("catch"); if (!s.ExceptionHandler.CatchType.IsNil) { output.Write(' '); s.ExceptionHandler.CatchType.WriteTo(s.Module, output, s.GenericContext, ILNameSyntax.TypeName); } output.WriteLine(); break; case ExceptionRegionKind.Finally: output.WriteLine("finally"); break; case ExceptionRegionKind.Fault: output.WriteLine("fault"); break; default: throw new ArgumentOutOfRangeException(); } output.WriteLine("{"); break; case ILStructureType.Filter: output.WriteLine("filter"); output.WriteLine("{"); break; default: throw new ArgumentOutOfRangeException(); } output.Indent(); } void WriteStructureBody(ILStructure s, BitSet branchTargets, ref BlobReader body, int methodRva) { bool isFirstInstructionInStructure = true; bool prevInstructionWasBranch = false; int childIndex = 0; while (body.RemainingBytes > 0 && body.Offset < s.EndOffset) { cancellationToken.ThrowIfCancellationRequested(); int offset = body.Offset; if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); WriteStructureBody(child, branchTargets, ref body, methodRva); WriteStructureFooter(child); } else { if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets[offset])) { output.WriteLine(); // put an empty line after branches, and in front of branch targets } var currentOpCode = ILParser.DecodeOpCode(ref body); body.Offset = offset; // reset IL stream WriteInstruction(output, module, s.MethodHandle, ref body, methodRva); prevInstructionWasBranch = currentOpCode.IsBranch() || currentOpCode.IsReturn() || currentOpCode == ILOpCode.Throw || currentOpCode == ILOpCode.Rethrow || currentOpCode == ILOpCode.Switch; } isFirstInstructionInStructure = false; } } void WriteStructureFooter(ILStructure s) { output.Unindent(); switch (s.Type) { case ILStructureType.Loop: output.WriteLine("// end loop"); break; case ILStructureType.Try: output.WriteLine("} // end .try"); break; case ILStructureType.Handler: output.WriteLine("} // end handler"); break; case ILStructureType.Filter: output.WriteLine("} // end filter"); break; default: throw new ArgumentOutOfRangeException(); } } protected virtual void WriteInstruction(ITextOutput output, MetadataFile metadataFile, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) { var metadata = metadataFile.Metadata; int offset = blob.Offset; if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) { var sp = sequencePoints[nextSequencePointIndex]; if (sp.Offset <= offset) { output.Write("// sequence point: "); if (sp.Offset != offset) { output.Write("!! at " + DisassemblerHelpers.OffsetToString(sp.Offset) + " !!"); } if (sp.IsHidden) { output.WriteLine("hidden"); } else { output.WriteLine($"(line {sp.StartLine}, col {sp.StartColumn}) to (line {sp.EndLine}, col {sp.EndColumn}) in {sp.DocumentUrl}"); } nextSequencePointIndex++; } } ILOpCode opCode = ILParser.DecodeOpCode(ref blob); OperandType opType = opCode.GetOperandType(); if (opCode.IsDefined() && opType.OperandSize() <= blob.RemainingBytes) { WriteRVA(blob, offset + methodRva, opCode); output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); output.Write(": "); WriteOpCode(opCode); switch (opCode.GetOperandType()) { case OperandType.BrTarget: case OperandType.ShortBrTarget: output.Write(' '); int targetOffset = ILParser.DecodeBranchTarget(ref blob, opCode); output.WriteLocalReference($"IL_{targetOffset:x4}", targetOffset); break; case OperandType.Field: case OperandType.Method: case OperandType.Sig: case OperandType.Type: output.Write(' '); int metadataToken = blob.ReadInt32(); EntityHandle? handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); try { handle?.WriteTo(module, output, genericContext); } catch (BadImageFormatException) { handle = null; } WriteMetadataToken(handle, metadataToken, spaceBefore: true); break; case OperandType.Tok: output.Write(' '); metadataToken = blob.ReadInt32(); handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); switch (handle?.Kind) { case HandleKind.MemberReference: switch (metadata.GetMemberReference((MemberReferenceHandle)handle).GetKind()) { case MemberReferenceKind.Method: output.Write("method "); break; case MemberReferenceKind.Field: output.Write("field "); break; } break; case HandleKind.FieldDefinition: output.Write("field "); break; case HandleKind.MethodDefinition: output.Write("method "); break; } try { handle?.WriteTo(module, output, genericContext); } catch (BadImageFormatException) { handle = null; } WriteMetadataToken(handle, metadataToken, spaceBefore: true); break; case OperandType.ShortI: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadSByte()); break; case OperandType.I: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadInt32()); break; case OperandType.I8: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadInt64()); break; case OperandType.ShortR: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadSingle()); break; case OperandType.R: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadDouble()); break; case OperandType.String: metadataToken = blob.ReadInt32(); output.Write(' '); UserStringHandle? userString; string text; try { userString = MetadataTokens.UserStringHandle(metadataToken); text = metadata.GetUserString(userString.Value); } catch (BadImageFormatException) { userString = null; text = null; } if (userString != null) { DisassemblerHelpers.WriteOperand(output, text); } WriteMetadataToken(userString, metadataToken, spaceBefore: true); break; case OperandType.Switch: var tmp = blob; int[] targets = ILParser.DecodeSwitchTargets(ref blob); if (ShowRawRVAOffsetAndBytes) { output.WriteLine(" ("); } else { output.Write(" ("); } tmp.ReadInt32(); for (int i = 0; i < targets.Length; i++) { if (i > 0) { if (ShowRawRVAOffsetAndBytes) { output.WriteLine(","); } else { output.Write(", "); } } if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); output.Write($"{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}"); output.Write(" */ "); } if (ShowRawRVAOffsetAndBytes) { output.Write(" "); } output.WriteLocalReference($"IL_{targets[i]:x4}", targets[i]); } output.Write(")"); break; case OperandType.Variable: output.Write(' '); int index = blob.ReadUInt16(); if (opCode == ILOpCode.Ldloc || opCode == ILOpCode.Ldloca || opCode == ILOpCode.Stloc) { DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); } else { DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); } break; case OperandType.ShortVariable: output.Write(' '); index = blob.ReadByte(); if (opCode == ILOpCode.Ldloc_s || opCode == ILOpCode.Ldloca_s || opCode == ILOpCode.Stloc_s) { DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); } else { DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); } break; } } else { ushort opCodeValue = (ushort)opCode; if (opCodeValue > 0xFF) { if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); output.Write($"0x{offset + methodRva:X8} {(ushort)opCode >> 8:X2}"); output.Write(" */ "); } output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); output.Write(": "); // split 16-bit value into two emitbyte directives output.WriteLine($".emitbyte 0x{(byte)(opCodeValue >> 8):x}"); if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); output.Write($"0x{offset + methodRva + 1:X8} {(ushort)opCode & 0xFF:X2}"); output.Write(" */ "); } // add label output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset + 1), offset + 1, isDefinition: true); output.Write(": "); output.Write($".emitbyte 0x{(byte)(opCodeValue & 0xFF):x}"); } else { if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); output.Write($"0x{offset + methodRva:X8} {(ushort)opCode & 0xFF:X2}"); output.Write(" */ "); } output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); output.Write(": "); output.Write($".emitbyte 0x{(byte)opCodeValue:x}"); } } output.WriteLine(); } void WriteRVA(BlobReader blob, int offset, ILOpCode opCode) { if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); var tmp = blob; if (opCode == ILOpCode.Switch) { tmp.ReadInt32(); } else { ILParser.SkipOperand(ref tmp, opCode); } output.Write($"0x{offset:X8} {(ushort)opCode:X2}"); int appendSpaces = (ushort)opCode > 0xFF ? 14 : 16; while (blob.Offset < tmp.Offset) { output.Write($"{blob.ReadByte():X2}"); appendSpaces -= 2; } if (appendSpaces > 0) { output.Write(new string(' ', appendSpaces)); } output.Write(" */ "); } } private void WriteOpCode(ILOpCode opCode) { var opCodeInfo = new OpCodeInfo(opCode, opCode.GetDisplayName()); string index; switch (opCode) { case ILOpCode.Ldarg_0: case ILOpCode.Ldarg_1: case ILOpCode.Ldarg_2: case ILOpCode.Ldarg_3: output.WriteReference(opCodeInfo, omitSuffix: true); index = opCodeInfo.Name.Substring(6); output.WriteLocalReference(index, "param_" + index); break; case ILOpCode.Ldloc_0: case ILOpCode.Ldloc_1: case ILOpCode.Ldloc_2: case ILOpCode.Ldloc_3: case ILOpCode.Stloc_0: case ILOpCode.Stloc_1: case ILOpCode.Stloc_2: case ILOpCode.Stloc_3: output.WriteReference(opCodeInfo, omitSuffix: true); index = opCodeInfo.Name.Substring(6); output.WriteLocalReference(index, "loc_" + index); break; default: output.WriteReference(opCodeInfo); break; } } private void WriteMetadataToken(EntityHandle handle, bool spaceBefore) { WriteMetadataToken(handle, MetadataTokens.GetToken(handle), spaceBefore); } private void WriteMetadataToken(Handle? handle, int metadataToken, bool spaceBefore) { ReflectionDisassembler.WriteMetadataToken(output, module, handle, metadataToken, spaceAfter: false, spaceBefore, ShowMetadataTokens, ShowMetadataTokensInBase10); } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/OpCodeInfo.cs ================================================ // 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 // 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; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Disassembler { public struct OpCodeInfo : IEquatable { public readonly ILOpCode Code; public readonly string Name; string encodedName; public OpCodeInfo(ILOpCode code, string name) { this.Code = code; this.Name = name ?? ""; this.encodedName = null; } public bool Equals(OpCodeInfo other) { return other.Code == this.Code && other.Name == this.Name; } public static bool operator ==(OpCodeInfo lhs, OpCodeInfo rhs) => lhs.Equals(rhs); public static bool operator !=(OpCodeInfo lhs, OpCodeInfo rhs) => !(lhs == rhs); public override bool Equals(object obj) { if (obj is OpCodeInfo opCode) return Equals(opCode); return false; } public override int GetHashCode() { return unchecked(982451629 * Code.GetHashCode() + 982451653 * Name.GetHashCode()); } public string Link => "https://docs.microsoft.com/dotnet/api/system.reflection.emit.opcodes." + EncodedName.ToLowerInvariant(); public string EncodedName { get { if (encodedName != null) return encodedName; switch (Name) { case "constrained.": encodedName = "Constrained"; return encodedName; case "no.": encodedName = "No"; return encodedName; case "readonly.": encodedName = "Reaonly"; return encodedName; case "tail.": encodedName = "Tailcall"; return encodedName; case "unaligned.": encodedName = "Unaligned"; return encodedName; case "volatile.": encodedName = "Volatile"; return encodedName; } string text = ""; bool toUpperCase = true; foreach (var ch in Name) { if (ch == '.') { text += '_'; toUpperCase = true; } else if (toUpperCase) { text += char.ToUpperInvariant(ch); toUpperCase = false; } else { text += ch; } } encodedName = text; return encodedName; } } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs ================================================ // 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 // 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.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Disassembler { /// /// Disassembles type and member definitions. /// public sealed class ReflectionDisassembler { readonly ITextOutput output; CancellationToken cancellationToken; bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) MethodBodyDisassembler methodBodyDisassembler; public bool DetectControlStructure { get => methodBodyDisassembler.DetectControlStructure; set => methodBodyDisassembler.DetectControlStructure = value; } public bool ShowSequencePoints { get => methodBodyDisassembler.ShowSequencePoints; set => methodBodyDisassembler.ShowSequencePoints = value; } public bool ShowMetadataTokens { get => methodBodyDisassembler.ShowMetadataTokens; set => methodBodyDisassembler.ShowMetadataTokens = value; } public bool ShowMetadataTokensInBase10 { get => methodBodyDisassembler.ShowMetadataTokensInBase10; set => methodBodyDisassembler.ShowMetadataTokensInBase10 = value; } public bool ShowRawRVAOffsetAndBytes { get => methodBodyDisassembler.ShowRawRVAOffsetAndBytes; set => methodBodyDisassembler.ShowRawRVAOffsetAndBytes = value; } public IDebugInfoProvider DebugInfo { get => methodBodyDisassembler.DebugInfo; set => methodBodyDisassembler.DebugInfo = value; } public bool ExpandMemberDefinitions { get; set; } /// /// Gets or sets whether custom attribute blobs should be decoded or dumped as raw bytes. Default is . /// Setting this value to (roughly) corresponds to the /CAVERBAL switch of ildasm. /// public bool DecodeCustomAttributeBlobs { get; set; } public IAssemblyResolver AssemblyResolver { get; set; } public IEntityProcessor EntityProcessor { get; set; } public ReflectionDisassembler(ITextOutput output, CancellationToken cancellationToken) : this(output, new MethodBodyDisassembler(output, cancellationToken), cancellationToken) { } public ReflectionDisassembler(ITextOutput output, MethodBodyDisassembler methodBodyDisassembler, CancellationToken cancellationToken) { if (output == null) throw new ArgumentNullException(nameof(output)); this.output = output; this.cancellationToken = cancellationToken; this.methodBodyDisassembler = methodBodyDisassembler; } #region Disassemble Method internal static readonly EnumNameCollection methodAttributeFlags = new EnumNameCollection() { { MethodAttributes.Final, "final" }, { MethodAttributes.HideBySig, "hidebysig" }, { MethodAttributes.SpecialName, "specialname" }, { MethodAttributes.PinvokeImpl, null }, // handled separately { MethodAttributes.UnmanagedExport, "export" }, { MethodAttributes.RTSpecialName, "rtspecialname" }, { MethodAttributes.RequireSecObject, "reqsecobj" }, { MethodAttributes.NewSlot, "newslot" }, { MethodAttributes.CheckAccessOnOverride, "strict" }, { MethodAttributes.Abstract, "abstract" }, { MethodAttributes.Virtual, "virtual" }, { MethodAttributes.Static, "static" }, { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm }; internal static readonly EnumNameCollection methodVisibility = new EnumNameCollection() { { MethodAttributes.Private, "private" }, { MethodAttributes.FamANDAssem, "famandassem" }, { MethodAttributes.Assembly, "assembly" }, { MethodAttributes.Family, "family" }, { MethodAttributes.FamORAssem, "famorassem" }, { MethodAttributes.Public, "public" }, }; internal static readonly EnumNameCollection callingConvention = new EnumNameCollection() { { SignatureCallingConvention.CDecl, "unmanaged cdecl" }, { SignatureCallingConvention.StdCall, "unmanaged stdcall" }, { SignatureCallingConvention.ThisCall, "unmanaged thiscall" }, { SignatureCallingConvention.FastCall, "unmanaged fastcall" }, { SignatureCallingConvention.VarArgs, "vararg" }, { SignatureCallingConvention.Default, null }, }; internal static readonly EnumNameCollection methodCodeType = new EnumNameCollection() { { MethodImplAttributes.IL, "cil" }, { MethodImplAttributes.Native, "native" }, { MethodImplAttributes.OPTIL, "optil" }, { MethodImplAttributes.Runtime, "runtime" }, }; internal static readonly EnumNameCollection methodImpl = new EnumNameCollection() { { MethodImplAttributes.Synchronized, "synchronized" }, { MethodImplAttributes.NoInlining, "noinlining" }, { MethodImplAttributes.NoOptimization, "nooptimization" }, { MethodImplAttributes.PreserveSig, "preservesig" }, { MethodImplAttributes.InternalCall, "internalcall" }, { MethodImplAttributes.ForwardRef, "forwardref" }, { MethodImplAttributes.AggressiveInlining, "aggressiveinlining" }, }; public void DisassembleMethod(MetadataFile module, MethodDefinitionHandle handle) { var genericContext = new MetadataGenericContext(handle, module); // write method header output.WriteReference(module, handle, ".method", isDefinition: true); output.Write(" "); DisassembleMethodHeaderInternal(module, handle, genericContext); DisassembleMethodBlock(module, handle, genericContext); } public void DisassembleMethodHeader(MetadataFile module, MethodDefinitionHandle handle) { var genericContext = new MetadataGenericContext(handle, module); // write method header output.WriteReference(module, handle, ".method", isDefinition: true); output.Write(" "); DisassembleMethodHeaderInternal(module, handle, genericContext); } void DisassembleMethodHeaderInternal(MetadataFile module, MethodDefinitionHandle handle, MetadataGenericContext genericContext) { var metadata = module.Metadata; WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: false, ShowMetadataTokens, ShowMetadataTokensInBase10); var methodDefinition = metadata.GetMethodDefinition(handle); // .method public hidebysig specialname // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed // //emit flags WriteEnum(methodDefinition.Attributes & MethodAttributes.MemberAccessMask, methodVisibility, output); WriteFlags(methodDefinition.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags, output); bool isCompilerControlled = (methodDefinition.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; if (isCompilerControlled) output.Write("privatescope "); if ((methodDefinition.Attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl) { output.Write("pinvokeimpl"); var info = methodDefinition.GetImport(); if (!info.Module.IsNil) { var moduleRef = metadata.GetModuleReference(info.Module); output.Write("(\"" + DisassemblerHelpers.EscapeString(metadata.GetString(moduleRef.Name)) + "\""); if (!info.Name.IsNil && metadata.GetString(info.Name) != metadata.GetString(methodDefinition.Name)) output.Write(" as \"" + DisassemblerHelpers.EscapeString(metadata.GetString(info.Name)) + "\""); if ((info.Attributes & MethodImportAttributes.ExactSpelling) == MethodImportAttributes.ExactSpelling) output.Write(" nomangle"); switch (info.Attributes & MethodImportAttributes.CharSetMask) { case MethodImportAttributes.CharSetAnsi: output.Write(" ansi"); break; case MethodImportAttributes.CharSetAuto: output.Write(" autochar"); break; case MethodImportAttributes.CharSetUnicode: output.Write(" unicode"); break; } if ((info.Attributes & MethodImportAttributes.SetLastError) == MethodImportAttributes.SetLastError) output.Write(" lasterr"); switch (info.Attributes & MethodImportAttributes.CallingConventionMask) { case MethodImportAttributes.CallingConventionCDecl: output.Write(" cdecl"); break; case MethodImportAttributes.CallingConventionFastCall: output.Write(" fastcall"); break; case MethodImportAttributes.CallingConventionStdCall: output.Write(" stdcall"); break; case MethodImportAttributes.CallingConventionThisCall: output.Write(" thiscall"); break; case MethodImportAttributes.CallingConventionWinApi: output.Write(" winapi"); break; } output.Write(')'); } output.Write(' '); } output.WriteLine(); output.Indent(); var declaringType = methodDefinition.GetDeclaringType(); MethodSignature>? signature; try { var signatureProvider = new DisassemblerSignatureTypeProvider(module, output); signature = methodDefinition.DecodeSignature(signatureProvider, genericContext); if (signature.Value.Header.HasExplicitThis) { output.Write("instance explicit "); } else if (signature.Value.Header.IsInstance) { output.Write("instance "); } //call convention WriteEnum(signature.Value.Header.CallingConvention, callingConvention, output); //return type signature.Value.ReturnType(ILNameSyntax.Signature); } catch (BadImageFormatException) { signature = null; output.Write(""); } output.Write(' '); var parameters = methodDefinition.GetParameters(); if (parameters.Count > 0) { var firstParam = metadata.GetParameter(parameters.First()); if (firstParam.SequenceNumber == 0) { var marshallingDesc = firstParam.GetMarshallingDescriptor(); if (!marshallingDesc.IsNil) { WriteMarshalInfo(metadata.GetBlobReader(marshallingDesc)); } } } if (isCompilerControlled) { output.Write(DisassemblerHelpers.Escape(metadata.GetString(methodDefinition.Name) + "$PST" + MetadataTokens.GetToken(handle).ToString("X8"))); } else { output.Write(DisassemblerHelpers.Escape(metadata.GetString(methodDefinition.Name))); } WriteTypeParameters(output, module, genericContext, methodDefinition.GetGenericParameters()); //( params ) output.Write(" ("); if (signature?.ParameterTypes.Length > 0) { output.WriteLine(); output.Indent(); WriteParameters(metadata, parameters, signature.Value); output.Unindent(); } output.Write(") "); //cil managed WriteEnum(methodDefinition.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType, output); if ((methodDefinition.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) output.Write("managed "); else output.Write("unmanaged "); WriteFlags(methodDefinition.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl, output); output.Unindent(); } internal static void WriteMetadataToken(ITextOutput output, MetadataFile module, Handle? handle, int metadataToken, bool spaceAfter, bool spaceBefore, bool showMetadataTokens, bool base10) { // handle can be null in case of errors, if that's the case, we always want to print a comment, // with the metadataToken. if (showMetadataTokens || handle == null) { if (spaceBefore) { output.Write(' '); } output.Write("/* "); string format = base10 ? null : "X8"; if (handle == null || !handle.Value.IsEntityHandle()) { output.Write(metadataToken.ToString(format)); } else { output.WriteReference(module, handle.GetValueOrDefault(), metadataToken.ToString(format), "metadata"); } output.Write(" */"); if (spaceAfter) { output.Write(' '); } } else if (spaceBefore && spaceAfter) { output.Write(' '); } } void DisassembleMethodBlock(MetadataFile module, MethodDefinitionHandle handle, MetadataGenericContext genericContext) { var metadata = module.Metadata; var methodDefinition = metadata.GetMethodDefinition(handle); OpenBlock(defaultCollapsed: isInType); WriteAttributes(module, methodDefinition.GetCustomAttributes()); foreach (var h in handle.GetMethodImplementations(metadata)) { var impl = metadata.GetMethodImplementation(h); output.Write(".override method "); impl.MethodDeclaration.WriteTo(module, output, genericContext); output.WriteLine(); } foreach (var p in methodDefinition.GetGenericParameters()) { WriteGenericParametersAndAttributes(module, genericContext, p); } foreach (var p in methodDefinition.GetParameters()) { WriteParameterAttributes(module, p); } WriteSecurityDeclarations(module, methodDefinition.GetDeclarativeSecurityAttributes()); if (methodDefinition.HasBody()) { methodBodyDisassembler.Disassemble(module, handle); } var declaringType = metadata.GetTypeDefinition(methodDefinition.GetDeclaringType()); CloseBlock("end of method " + DisassemblerHelpers.Escape(metadata.GetString(declaringType.Name)) + "::" + DisassemblerHelpers.Escape(metadata.GetString(methodDefinition.Name))); } #region Write Security Declarations void WriteSecurityDeclarations(MetadataFile module, DeclarativeSecurityAttributeHandleCollection secDeclProvider) { if (secDeclProvider.Count == 0) return; foreach (var h in secDeclProvider) { output.Write(".permissionset "); var secdecl = module.Metadata.GetDeclarativeSecurityAttribute(h); switch ((ushort)secdecl.Action) { case 1: // DeclarativeSecurityAction.Request output.Write("request"); break; case 2: // DeclarativeSecurityAction.Demand output.Write("demand"); break; case 3: // DeclarativeSecurityAction.Assert output.Write("assert"); break; case 4: // DeclarativeSecurityAction.Deny output.Write("deny"); break; case 5: // DeclarativeSecurityAction.PermitOnly output.Write("permitonly"); break; case 6: // DeclarativeSecurityAction.LinkDemand output.Write("linkcheck"); break; case 7: // DeclarativeSecurityAction.InheritDemand output.Write("inheritcheck"); break; case 8: // DeclarativeSecurityAction.RequestMinimum output.Write("reqmin"); break; case 9: // DeclarativeSecurityAction.RequestOptional output.Write("reqopt"); break; case 10: // DeclarativeSecurityAction.RequestRefuse output.Write("reqrefuse"); break; case 11: // DeclarativeSecurityAction.PreJitGrant output.Write("prejitgrant"); break; case 12: // DeclarativeSecurityAction.PreJitDeny output.Write("prejitdeny"); break; case 13: // DeclarativeSecurityAction.NonCasDemand output.Write("noncasdemand"); break; case 14: // DeclarativeSecurityAction.NonCasLinkDemand output.Write("noncaslinkdemand"); break; case 15: // DeclarativeSecurityAction.NonCasInheritance output.Write("noncasinheritance"); break; default: output.Write(secdecl.Action.ToString()); break; } var blob = module.Metadata.GetBlobReader(secdecl.PermissionSet); if (AssemblyResolver == null) { output.Write(" = "); WriteBlob(blob); output.WriteLine(); } else if ((char)blob.ReadByte() != '.') { blob.Reset(); output.WriteLine(); output.Indent(); output.Write("bytearray"); WriteBlob(blob); output.WriteLine(); output.Unindent(); } else { var outputWithRollback = new TextOutputWithRollback(output); try { TryDecodeSecurityDeclaration(outputWithRollback, blob, module); outputWithRollback.Commit(); } catch (Exception ex) when (ex is BadImageFormatException || ex is EnumUnderlyingTypeResolveException) { blob.Reset(); output.Write(" = "); WriteBlob(blob); output.WriteLine(); } } } } class SecurityDeclarationDecoder : ICustomAttributeTypeProvider<(PrimitiveTypeCode Code, string Name)> { readonly ITextOutput output; readonly IAssemblyResolver resolver; readonly MetadataFile module; public SecurityDeclarationDecoder(ITextOutput output, IAssemblyResolver resolver, MetadataFile module) { this.output = output; this.resolver = resolver; this.module = module; } public (PrimitiveTypeCode, string) GetPrimitiveType(PrimitiveTypeCode typeCode) { return (typeCode, null); } public (PrimitiveTypeCode, string) GetSystemType() { return (0, "type"); } public (PrimitiveTypeCode, string) GetSZArrayType((PrimitiveTypeCode, string) elementType) { return (elementType.Item1, (elementType.Item2 ?? PrimitiveTypeCodeToString(elementType.Item1)) + "[]"); } public (PrimitiveTypeCode, string) GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { string fullTypeName = handle.GetFullTypeName(reader).FullName; if (handle.IsEnum(reader, out var typeCode)) return (typeCode, "enum " + fullTypeName); return (0, fullTypeName); } public (PrimitiveTypeCode, string) GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { string fullTypeName = handle.GetFullTypeName(reader).FullName; var containingModule = GetDeclaringModule(handle); string assemblyQualifiedTypeName = containingModule != null ? fullTypeName + ", " + containingModule : fullTypeName; PrimitiveTypeCode typeCode = 0; var (targetModule, resolvedType) = ResolveType(assemblyQualifiedTypeName, module); if (targetModule != null) { if (!resolvedType.IsEnum(targetModule.Metadata, out typeCode)) { typeCode = 0; } else { assemblyQualifiedTypeName = "enum " + assemblyQualifiedTypeName; } } return (typeCode, assemblyQualifiedTypeName); string GetDeclaringModule(TypeReferenceHandle handle) { var tr = reader.GetTypeReference(handle); switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return GetDeclaringModule((TypeReferenceHandle)tr.ResolutionScope); case HandleKind.AssemblyReference: var asmRef = reader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); return asmRef.TryGetFullAssemblyName(reader, out var assemblyName) ? assemblyName : null; case HandleKind.ModuleReference: var modRef = reader.GetModuleReference((ModuleReferenceHandle)tr.ResolutionScope); return reader.GetString(modRef.Name); default: return null; } } } public (PrimitiveTypeCode, string) GetTypeFromSerializedName(string name) { if (resolver == null) throw new EnumUnderlyingTypeResolveException(); var (containingModule, typeDefHandle) = ResolveType(name, module); if (typeDefHandle.IsNil) throw new EnumUnderlyingTypeResolveException(); if (typeDefHandle.IsEnum(containingModule.Metadata, out var typeCode)) return (typeCode, "enum " + name); return (0, name); } public PrimitiveTypeCode GetUnderlyingEnumType((PrimitiveTypeCode, string) type) { return type.Item1; } public bool IsSystemType((PrimitiveTypeCode, string) type) { return "type" == type.Item2; } (MetadataFile, TypeDefinitionHandle) ResolveType(string typeName, MetadataFile module) { string[] nameParts = typeName.Split(new[] { ", " }, 2, StringSplitOptions.None); string[] typeNameParts = nameParts[0].Split('.'); MetadataFile containingModule = null; TypeDefinitionHandle typeDefHandle = default; // if we deal with an assembly-qualified name, resolve the assembly if (nameParts.Length == 2) containingModule = resolver.Resolve(AssemblyNameReference.Parse(nameParts[1])); if (containingModule != null) { // try to find the type in the assembly typeDefHandle = FindType(containingModule, typeNameParts); } else { // just fully-qualified name, try current assembly typeDefHandle = FindType(module, typeNameParts); containingModule = module; if (typeDefHandle.IsNil && TryResolveMscorlib(out var mscorlib)) { // otherwise try mscorlib typeDefHandle = FindType(mscorlib, typeNameParts); containingModule = mscorlib; } } return (containingModule, typeDefHandle); TypeDefinitionHandle FindType(MetadataFile currentModule, string[] name) { var metadata = currentModule.Metadata; var currentNamespace = metadata.GetNamespaceDefinitionRoot(); ImmutableArray typeDefinitions = default; for (int i = 0; i < name.Length; i++) { string identifier = name[i]; if (!typeDefinitions.IsDefault) { restart: foreach (var type in typeDefinitions) { var typeDef = metadata.GetTypeDefinition(type); var currentTypeName = metadata.GetString(typeDef.Name); if (identifier == currentTypeName) { if (i + 1 == name.Length) return type; typeDefinitions = typeDef.GetNestedTypes(); goto restart; } } } else { var next = currentNamespace.NamespaceDefinitions.FirstOrDefault(ns => metadata.StringComparer.Equals(metadata.GetNamespaceDefinition(ns).Name, identifier)); if (!next.IsNil) { currentNamespace = metadata.GetNamespaceDefinition(next); } else { typeDefinitions = currentNamespace.TypeDefinitions; i--; } } } return default; } } MetadataFile mscorlib; bool TryResolveMscorlib(out MetadataFile mscorlib) { mscorlib = null; if (this.mscorlib != null) { mscorlib = this.mscorlib; return true; } if (resolver == null) { return false; } this.mscorlib = mscorlib = resolver.Resolve(AssemblyNameReference.Parse("mscorlib")); return this.mscorlib != null; } } void TryDecodeSecurityDeclaration(TextOutputWithRollback output, BlobReader blob, MetadataFile module) { output.WriteLine(" = {"); output.Indent(); string currentAssemblyName = null; string currentFullAssemblyName = null; if (module.Metadata.IsAssembly) { try { currentAssemblyName = module.Metadata.GetString(module.Metadata.GetAssemblyDefinition().Name); } catch (BadImageFormatException) { currentAssemblyName = ""; } if (!module.Metadata.TryGetFullAssemblyName(out currentFullAssemblyName)) { currentFullAssemblyName = ""; } } int count = blob.ReadCompressedInteger(); for (int i = 0; i < count; i++) { var fullTypeName = blob.ReadSerializedString(); string[] nameParts = fullTypeName.Split(new[] { ", " }, StringSplitOptions.None); if (nameParts.Length < 2 || nameParts[1] == currentAssemblyName) { output.Write("class "); output.Write(DisassemblerHelpers.Escape(fullTypeName)); } else { output.Write('['); output.Write(nameParts[1]); output.Write(']'); output.Write(nameParts[0]); } output.Write(" = {"); blob.ReadCompressedInteger(); // ? // The specification seems to be incorrect here, so I'm using the logic from Cecil instead. int argCount = blob.ReadCompressedInteger(); var decoder = new CustomAttributeDecoder<(PrimitiveTypeCode Code, string Name)>(new SecurityDeclarationDecoder(output, AssemblyResolver, module), module.Metadata, provideBoxingTypeInfo: true); var arguments = decoder.DecodeNamedArguments(ref blob, argCount); if (argCount > 0) { output.WriteLine(); output.Indent(); } foreach (var argument in arguments) { switch (argument.Kind) { case CustomAttributeNamedArgumentKind.Field: output.Write("field "); break; case CustomAttributeNamedArgumentKind.Property: output.Write("property "); break; } output.Write(argument.Type.Name ?? PrimitiveTypeCodeToString(argument.Type.Code)); output.Write(" " + DisassemblerHelpers.Escape(argument.Name) + " = "); WriteValue(output, argument.Type, argument.Value); output.WriteLine(); } if (argCount > 0) { output.Unindent(); } output.Write('}'); if (i + 1 < count) output.Write(','); output.WriteLine(); } output.Unindent(); output.WriteLine("}"); } void WriteValue(ITextOutput output, (PrimitiveTypeCode Code, string Name) type, object value) { if (value is CustomAttributeTypedArgument<(PrimitiveTypeCode, string)> boxedValue) { output.Write("object("); WriteValue(output, boxedValue.Type, boxedValue.Value); output.Write(")"); } else if (value is ImmutableArray> arrayValue) { string elementType = type.Name != null && !type.Name.StartsWith("enum ", StringComparison.Ordinal) ? type.Name.Remove(type.Name.Length - 2) : PrimitiveTypeCodeToString(type.Code); output.Write(elementType); output.Write("["); output.Write(arrayValue.Length.ToString()); output.Write("]("); bool first = true; foreach (var item in arrayValue) { if (!first) output.Write(" "); if (item.Value is CustomAttributeTypedArgument<(PrimitiveTypeCode, string)> boxedItem) { WriteValue(output, boxedItem.Type, boxedItem.Value); } else { WriteSimpleValue(output, item.Value, elementType); } first = false; } output.Write(")"); } else { string typeName = type.Name != null && !type.Name.StartsWith("enum ", StringComparison.Ordinal) ? type.Name : PrimitiveTypeCodeToString(type.Code); output.Write(typeName); output.Write("("); WriteSimpleValue(output, value, typeName); output.Write(")"); } } private static void WriteSimpleValue(ITextOutput output, object value, string typeName) { switch (typeName) { case "string": output.Write("'" + DisassemblerHelpers.EscapeString(value.ToString()).Replace("'", "\\'") + "'"); break; case "type": var info = ((PrimitiveTypeCode Code, string Name))value; if (info.Name.StartsWith("enum ", StringComparison.Ordinal)) { output.Write(info.Name.Substring(5)); } else { output.Write(info.Name); } break; default: DisassemblerHelpers.WriteOperand(output, value); break; } } static string PrimitiveTypeCodeToString(PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: return "bool"; case PrimitiveTypeCode.Byte: return "uint8"; case PrimitiveTypeCode.SByte: return "int8"; case PrimitiveTypeCode.Char: return "char"; case PrimitiveTypeCode.Int16: return "int16"; case PrimitiveTypeCode.UInt16: return "uint16"; case PrimitiveTypeCode.Int32: return "int32"; case PrimitiveTypeCode.UInt32: return "uint32"; case PrimitiveTypeCode.Int64: return "int64"; case PrimitiveTypeCode.UInt64: return "uint64"; case PrimitiveTypeCode.Single: return "float32"; case PrimitiveTypeCode.Double: return "float64"; case PrimitiveTypeCode.String: return "string"; case PrimitiveTypeCode.Object: return "object"; default: return "unknown"; } } #endregion #region WriteMarshalInfo void WriteMarshalInfo(BlobReader marshalInfo) { output.Write("marshal("); WriteNativeType(ref marshalInfo); output.Write(") "); } void WriteNativeType(ref BlobReader blob) { byte type; switch (type = blob.ReadByte()) { case 0x66: // None case 0x50: // Max break; case 0x02: // NATIVE_TYPE_BOOLEAN output.Write("bool"); break; case 0x03: // NATIVE_TYPE_I1 output.Write("int8"); break; case 0x04: // NATIVE_TYPE_U1 output.Write("unsigned int8"); break; case 0x05: // NATIVE_TYPE_I2 output.Write("int16"); break; case 0x06: // NATIVE_TYPE_U2 output.Write("unsigned int16"); break; case 0x07: // NATIVE_TYPE_I4 output.Write("int32"); break; case 0x08: // NATIVE_TYPE_U4 output.Write("unsigned int32"); break; case 0x09: // NATIVE_TYPE_I8 output.Write("int64"); break; case 0x0a: // NATIVE_TYPE_U8 output.Write("unsigned int64"); break; case 0x0b: // NATIVE_TYPE_R4 output.Write("float32"); break; case 0x0c: // NATIVE_TYPE_R8 output.Write("float64"); break; case 0x14: // NATIVE_TYPE_LPSTR output.Write("lpstr"); break; case 0x1f: // NATIVE_TYPE_INT output.Write("int"); break; case 0x20: // NATIVE_TYPE_UINT output.Write("unsigned int"); break; case 0x26: // NATIVE_TYPE_FUNC output.Write("Func"); break; case 0x2a: // NATIVE_TYPE_ARRAY if (blob.RemainingBytes > 0) WriteNativeType(ref blob); output.Write('['); int sizeParameterIndex = blob.TryReadCompressedInteger(out int value) ? value : -1; int size = blob.TryReadCompressedInteger(out value) ? value : -1; int sizeParameterMultiplier = blob.TryReadCompressedInteger(out value) ? value : -1; if (size >= 0) { output.Write(size.ToString()); } if (sizeParameterIndex >= 0 && sizeParameterMultiplier != 0) { output.Write(" + "); output.Write(sizeParameterIndex.ToString()); } output.Write(']'); break; case 0x0f: // Currency output.Write("currency"); break; case 0x13: // BStr output.Write("bstr"); break; case 0x15: // LPWStr output.Write("lpwstr"); break; case 0x16: // LPTStr output.Write("lptstr"); break; case 0x17: // FixedSysString output.Write("fixed sysstring[{0}]", blob.ReadCompressedInteger()); break; case 0x19: // IUnknown output.Write("iunknown"); break; case 0x1a: // IDispatch output.Write("idispatch"); break; case 0x1b: // Struct output.Write("struct"); break; case 0x1c: // IntF output.Write("interface"); break; case 0x1d: // SafeArray output.Write("safearray "); if (blob.RemainingBytes > 0) { byte elementType = blob.ReadByte(); switch (elementType) { case 0: // None break; case 2: // I2 output.Write("int16"); break; case 3: // I4 output.Write("int32"); break; case 4: // R4 output.Write("float32"); break; case 5: // R8 output.Write("float64"); break; case 6: // Currency output.Write("currency"); break; case 7: // Date output.Write("date"); break; case 8: // BStr output.Write("bstr"); break; case 9: // Dispatch output.Write("idispatch"); break; case 10: // Error output.Write("error"); break; case 11: // Bool output.Write("bool"); break; case 12: // Variant output.Write("variant"); break; case 13: // Unknown output.Write("iunknown"); break; case 14: // Decimal output.Write("decimal"); break; case 16: // I1 output.Write("int8"); break; case 17: // UI1 output.Write("unsigned int8"); break; case 18: // UI2 output.Write("unsigned int16"); break; case 19: // UI4 output.Write("unsigned int32"); break; case 22: // Int output.Write("int"); break; case 23: // UInt output.Write("unsigned int"); break; default: output.Write(elementType.ToString()); break; } } break; case 0x1e: // FixedArray output.Write("fixed array"); output.Write("[{0}]", blob.TryReadCompressedInteger(out value) ? value : 0); if (blob.RemainingBytes > 0) { output.Write(' '); WriteNativeType(ref blob); } break; case 0x22: // ByValStr output.Write("byvalstr"); break; case 0x23: // ANSIBStr output.Write("ansi bstr"); break; case 0x24: // TBStr output.Write("tbstr"); break; case 0x25: // VariantBool output.Write("variant bool"); break; case 0x28: // ASAny output.Write("as any"); break; case 0x2b: // LPStruct output.Write("lpstruct"); break; case 0x2c: // CustomMarshaler string guidValue = blob.ReadSerializedString(); string unmanagedType = blob.ReadSerializedString(); string managedType = blob.ReadSerializedString(); string cookie = blob.ReadSerializedString(); var guid = !string.IsNullOrEmpty(guidValue) ? new Guid(guidValue) : Guid.Empty; output.Write("custom(\"{0}\", \"{1}\"", DisassemblerHelpers.EscapeString(managedType), DisassemblerHelpers.EscapeString(cookie)); if (guid != Guid.Empty || !string.IsNullOrEmpty(unmanagedType)) { output.Write(", \"{0}\", \"{1}\"", guid.ToString(), DisassemblerHelpers.EscapeString(unmanagedType)); } output.Write(')'); break; case 0x2d: // Error output.Write("error"); break; default: output.Write(type.ToString()); break; } } #endregion void WriteParameters(MetadataReader metadata, IEnumerable parameters, MethodSignature> signature) { int i = 0; int offset = signature.Header.IsInstance ? 1 : 0; foreach (var h in parameters) { var p = metadata.GetParameter(h); // skip return type parameter handle if (p.SequenceNumber == 0) continue; // fill gaps in parameter list while (i < p.SequenceNumber - 1) { if (i > 0) { output.Write(','); output.WriteLine(); } signature.ParameterTypes[i](ILNameSyntax.Signature); output.Write(' '); output.WriteLocalReference("''", "param_" + (i + offset), isDefinition: true); i++; } // separator if (i > 0) { output.Write(','); output.WriteLine(); } // print parameter if ((p.Attributes & ParameterAttributes.In) == ParameterAttributes.In) output.Write("[in] "); if ((p.Attributes & ParameterAttributes.Out) == ParameterAttributes.Out) output.Write("[out] "); if ((p.Attributes & ParameterAttributes.Optional) == ParameterAttributes.Optional) output.Write("[opt] "); signature.ParameterTypes[i](ILNameSyntax.Signature); output.Write(' '); var md = p.GetMarshallingDescriptor(); if (!md.IsNil) { WriteMarshalInfo(metadata.GetBlobReader(md)); } output.WriteLocalReference(DisassemblerHelpers.Escape(metadata.GetString(p.Name)), "param_" + (i + offset), isDefinition: true); i++; } // add remaining parameter types as unnamed parameters while (i < signature.RequiredParameterCount) { if (i > 0) { output.Write(','); output.WriteLine(); } signature.ParameterTypes[i](ILNameSyntax.Signature); output.Write(' '); output.WriteLocalReference("''", "param_" + (i + offset), isDefinition: true); i++; } output.WriteLine(); } void WriteGenericParametersAndAttributes(MetadataFile module, MetadataGenericContext context, GenericParameterHandle handle) { var metadata = module.Metadata; var p = metadata.GetGenericParameter(handle); if (p.GetCustomAttributes().Count > 0) { output.Write(".param type {0}", metadata.GetString(p.Name)); output.WriteLine(); output.Indent(); WriteAttributes(module, p.GetCustomAttributes()); output.Unindent(); } foreach (var constraintHandle in p.GetConstraints()) { var constraint = metadata.GetGenericParameterConstraint(constraintHandle); if (constraint.GetCustomAttributes().Count > 0) { output.Write(".param constraint {0}, ", metadata.GetString(p.Name)); constraint.Type.WriteTo(module, output, context, ILNameSyntax.TypeName); output.WriteLine(); output.Indent(); WriteAttributes(module, constraint.GetCustomAttributes()); output.Unindent(); } } } void WriteParameterAttributes(MetadataFile module, ParameterHandle handle) { var metadata = module.Metadata; var p = metadata.GetParameter(handle); if (p.GetDefaultValue().IsNil && p.GetCustomAttributes().Count == 0) return; output.Write(".param [{0}]", p.SequenceNumber); if (!p.GetDefaultValue().IsNil) { output.Write(" = "); WriteConstant(metadata, metadata.GetConstant(p.GetDefaultValue())); } output.WriteLine(); output.Indent(); WriteAttributes(module, p.GetCustomAttributes()); output.Unindent(); } void WriteConstant(MetadataReader metadata, Constant constant) { switch (constant.TypeCode) { case ConstantTypeCode.NullReference: output.Write("nullref"); break; default: var blob = metadata.GetBlobReader(constant.Value); object value; try { value = blob.ReadConstant(constant.TypeCode); } catch (ArgumentOutOfRangeException) { output.Write($"/* Constant with invalid typecode: {constant.TypeCode} */"); return; } if (value is string) { DisassemblerHelpers.WriteOperand(output, value); } else { string typeName = DisassemblerHelpers.PrimitiveTypeName(value.GetType().FullName); output.Write(typeName); output.Write('('); float? cf = value as float?; double? cd = value as double?; if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) { output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0)); } else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) { output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value)); } else { DisassemblerHelpers.WriteOperand(output, value); } output.Write(')'); } break; } } #endregion #region Disassemble Field internal static readonly EnumNameCollection fieldVisibility = new EnumNameCollection() { { FieldAttributes.Private, "private" }, { FieldAttributes.FamANDAssem, "famandassem" }, { FieldAttributes.Assembly, "assembly" }, { FieldAttributes.Family, "family" }, { FieldAttributes.FamORAssem, "famorassem" }, { FieldAttributes.Public, "public" }, }; internal static readonly EnumNameCollection fieldAttributes = new EnumNameCollection() { { FieldAttributes.Static, "static" }, { FieldAttributes.Literal, "literal" }, { FieldAttributes.InitOnly, "initonly" }, { FieldAttributes.SpecialName, "specialname" }, { FieldAttributes.RTSpecialName, "rtspecialname" }, { FieldAttributes.NotSerialized, "notserialized" }, }; public void DisassembleField(MetadataFile module, FieldDefinitionHandle handle) { var metadata = module.Metadata; var fieldDefinition = metadata.GetFieldDefinition(handle); char sectionPrefix = DisassembleFieldHeaderInternal(module, handle, metadata, fieldDefinition); output.WriteLine(); var attributes = fieldDefinition.GetCustomAttributes(); if (attributes.Count > 0) { output.MarkFoldStart(); WriteAttributes(module, fieldDefinition.GetCustomAttributes()); output.MarkFoldEnd(); } if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) { // Field data as specified in II.16.3.1 of ECMA-335 6th edition int rva = fieldDefinition.GetRelativeVirtualAddress(); int sectionIndex = module.GetContainingSectionIndex(rva); if (sectionIndex < 0) { output.WriteLine($"// RVA {rva:X8} invalid (not in any section)"); } else { BlobReader initVal; try { initVal = fieldDefinition.GetInitialValue(module, null); } catch (BadImageFormatException ex) { initVal = default; output.WriteLine("// .data {2}_{0:X8} = {1}", fieldDefinition.GetRelativeVirtualAddress(), ex.Message, sectionPrefix); } if (initVal.Length > 0) { var sectionHeader = module.SectionHeaders[sectionIndex]; output.Write(".data "); if (sectionHeader.Name == ".text") { output.Write("cil "); } else if (sectionHeader.Name == ".tls") { output.Write("tls "); } else if (sectionHeader.Name is not (null or ".data")) { output.Write($"/* {sectionHeader.Name} */ "); } output.Write($"{sectionPrefix}_{rva:X8} = bytearray "); WriteBlob(initVal); output.WriteLine(); } } } } public void DisassembleFieldHeader(MetadataFile module, FieldDefinitionHandle handle) { var metadata = module.Metadata; var fieldDefinition = metadata.GetFieldDefinition(handle); DisassembleFieldHeaderInternal(module, handle, metadata, fieldDefinition); } private char DisassembleFieldHeaderInternal(MetadataFile module, FieldDefinitionHandle handle, MetadataReader metadata, FieldDefinition fieldDefinition) { output.WriteReference(module, handle, ".field", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); int offset = fieldDefinition.GetOffset(); if (offset > -1) { output.Write("[" + offset + "] "); } WriteEnum(fieldDefinition.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility, output); const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; WriteFlags(fieldDefinition.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes, output); var signature = fieldDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new MetadataGenericContext(fieldDefinition.GetDeclaringType(), module)); var marshallingDescriptor = fieldDefinition.GetMarshallingDescriptor(); if (!marshallingDescriptor.IsNil) { WriteMarshalInfo(metadata.GetBlobReader(marshallingDescriptor)); } signature(ILNameSyntax.Signature); output.Write(' '); var fieldName = metadata.GetString(fieldDefinition.Name); output.Write(DisassemblerHelpers.Escape(fieldName)); char sectionPrefix = 'D'; if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) { int rva = fieldDefinition.GetRelativeVirtualAddress(); sectionPrefix = GetRVASectionPrefix(module, rva); output.Write(" at {1}_{0:X8}", rva, sectionPrefix); } var defaultValue = fieldDefinition.GetDefaultValue(); if (!defaultValue.IsNil) { output.Write(" = "); WriteConstant(metadata, metadata.GetConstant(defaultValue)); } return sectionPrefix; } char GetRVASectionPrefix(MetadataFile module, int rva) { if (module is not PEFile peFile) throw new NotSupportedException("Cannot get RVA section prefix from module"); int sectionIndex = peFile.Reader.PEHeaders.GetContainingSectionIndex(rva); if (sectionIndex < 0) return 'D'; var sectionHeader = peFile.Reader.PEHeaders.SectionHeaders[sectionIndex]; switch (sectionHeader.Name) { case ".tls": return 'T'; case ".text": return 'I'; default: return 'D'; } } #endregion #region Disassemble Property internal static readonly EnumNameCollection propertyAttributes = new EnumNameCollection() { { PropertyAttributes.SpecialName, "specialname" }, { PropertyAttributes.RTSpecialName, "rtspecialname" }, { PropertyAttributes.HasDefault, "hasdefault" }, }; public void DisassembleProperty(MetadataFile module, PropertyDefinitionHandle property) { var metadata = module.Metadata; var propertyDefinition = metadata.GetPropertyDefinition(property); PropertyAccessors accessors = DisassemblePropertyHeaderInternal(module, property, metadata, propertyDefinition); OpenBlock(false); WriteAttributes(module, propertyDefinition.GetCustomAttributes()); WriteNestedMethod(".get", module, accessors.Getter); WriteNestedMethod(".set", module, accessors.Setter); foreach (var method in accessors.Others) { WriteNestedMethod(".other", module, method); } CloseBlock(); } public void DisassemblePropertyHeader(MetadataFile module, PropertyDefinitionHandle property) { var metadata = module.Metadata; var propertyDefinition = metadata.GetPropertyDefinition(property); DisassemblePropertyHeaderInternal(module, property, metadata, propertyDefinition); } private PropertyAccessors DisassemblePropertyHeaderInternal(MetadataFile module, PropertyDefinitionHandle handle, MetadataReader metadata, PropertyDefinition propertyDefinition) { output.WriteReference(module, handle, ".property", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); WriteFlags(propertyDefinition.Attributes, propertyAttributes, output); var accessors = propertyDefinition.GetAccessors(); var declaringType = metadata.GetMethodDefinition(accessors.GetAny()).GetDeclaringType(); var signature = propertyDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new MetadataGenericContext(declaringType, module)); if (signature.Header.IsInstance) output.Write("instance "); signature.ReturnType(ILNameSyntax.Signature); output.Write(' '); output.Write(DisassemblerHelpers.Escape(metadata.GetString(propertyDefinition.Name))); output.Write('('); if (signature.ParameterTypes.Length > 0) { var parameters = metadata.GetMethodDefinition(accessors.GetAny()).GetParameters(); int parametersCount = accessors.Getter.IsNil ? parameters.Count - 1 : parameters.Count; output.WriteLine(); output.Indent(); WriteParameters(metadata, parameters.Take(parametersCount), signature); output.Unindent(); } output.Write(')'); return accessors; } void WriteNestedMethod(string keyword, MetadataFile module, MethodDefinitionHandle method) { if (method.IsNil) return; output.Write(keyword); output.Write(' '); ((EntityHandle)method).WriteTo(module, output, default); output.WriteLine(); } #endregion #region Disassemble Event internal static readonly EnumNameCollection eventAttributes = new EnumNameCollection() { { EventAttributes.SpecialName, "specialname" }, { EventAttributes.RTSpecialName, "rtspecialname" }, }; public void DisassembleEvent(MetadataFile module, EventDefinitionHandle handle) { var eventDefinition = module.Metadata.GetEventDefinition(handle); var accessors = eventDefinition.GetAccessors(); DisassembleEventHeaderInternal(module, handle, eventDefinition, accessors); OpenBlock(false); WriteAttributes(module, eventDefinition.GetCustomAttributes()); WriteNestedMethod(".addon", module, accessors.Adder); WriteNestedMethod(".removeon", module, accessors.Remover); WriteNestedMethod(".fire", module, accessors.Raiser); foreach (var method in accessors.Others) { WriteNestedMethod(".other", module, method); } CloseBlock(); } public void DisassembleEventHeader(MetadataFile module, EventDefinitionHandle handle) { var eventDefinition = module.Metadata.GetEventDefinition(handle); var accessors = eventDefinition.GetAccessors(); DisassembleEventHeaderInternal(module, handle, eventDefinition, accessors); } private void DisassembleEventHeaderInternal(MetadataFile module, EventDefinitionHandle handle, EventDefinition eventDefinition, EventAccessors accessors) { TypeDefinitionHandle declaringType; if (!accessors.Adder.IsNil) { declaringType = module.Metadata.GetMethodDefinition(accessors.Adder).GetDeclaringType(); } else if (!accessors.Remover.IsNil) { declaringType = module.Metadata.GetMethodDefinition(accessors.Remover).GetDeclaringType(); } else { declaringType = module.Metadata.GetMethodDefinition(accessors.Raiser).GetDeclaringType(); } output.WriteReference(module, handle, ".event", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); WriteFlags(eventDefinition.Attributes, eventAttributes, output); var provider = new DisassemblerSignatureTypeProvider(module, output); Action signature; switch (eventDefinition.Type.Kind) { case HandleKind.TypeDefinition: signature = provider.GetTypeFromDefinition(module.Metadata, (TypeDefinitionHandle)eventDefinition.Type, 0); break; case HandleKind.TypeReference: signature = provider.GetTypeFromReference(module.Metadata, (TypeReferenceHandle)eventDefinition.Type, 0); break; case HandleKind.TypeSpecification: signature = provider.GetTypeFromSpecification(module.Metadata, new MetadataGenericContext(declaringType, module), (TypeSpecificationHandle)eventDefinition.Type, 0); break; default: throw new BadImageFormatException("Expected a TypeDef, TypeRef or TypeSpec handle!"); } signature(ILNameSyntax.TypeName); output.Write(' '); output.Write(DisassemblerHelpers.Escape(module.Metadata.GetString(eventDefinition.Name))); } #endregion #region Disassemble Type internal static readonly EnumNameCollection typeVisibility = new EnumNameCollection() { { TypeAttributes.Public, "public" }, { TypeAttributes.NotPublic, "private" }, { TypeAttributes.NestedPublic, "nested public" }, { TypeAttributes.NestedPrivate, "nested private" }, { TypeAttributes.NestedAssembly, "nested assembly" }, { TypeAttributes.NestedFamily, "nested family" }, { TypeAttributes.NestedFamANDAssem, "nested famandassem" }, { TypeAttributes.NestedFamORAssem, "nested famorassem" }, }; EnumNameCollection typeLayout = new EnumNameCollection() { { TypeAttributes.AutoLayout, "auto" }, { TypeAttributes.SequentialLayout, "sequential" }, { TypeAttributes.ExplicitLayout, "explicit" }, }; EnumNameCollection typeStringFormat = new EnumNameCollection() { { TypeAttributes.AutoClass, "auto" }, { TypeAttributes.AnsiClass, "ansi" }, { TypeAttributes.UnicodeClass, "unicode" }, }; internal static readonly EnumNameCollection typeAttributes = new EnumNameCollection() { { TypeAttributes.Abstract, "abstract" }, { TypeAttributes.Sealed, "sealed" }, { TypeAttributes.SpecialName, "specialname" }, { TypeAttributes.Import, "import" }, { TypeAttributes.Serializable, "serializable" }, { TypeAttributes.WindowsRuntime, "windowsruntime" }, { TypeAttributes.BeforeFieldInit, "beforefieldinit" }, { TypeAttributes.HasSecurity, null }, }; public void DisassembleType(MetadataFile module, TypeDefinitionHandle type) { var typeDefinition = module.Metadata.GetTypeDefinition(type); MetadataGenericContext genericContext = new MetadataGenericContext(type, module); DisassembleTypeHeaderInternal(module, type, typeDefinition, genericContext); var interfaces = Process(module, typeDefinition.GetInterfaceImplementations()); if (interfaces.Count > 0) { output.Indent(); bool first = true; foreach (var i in interfaces) { if (!first) output.WriteLine(","); if (first) output.Write("implements "); else output.Write(" "); first = false; var iface = module.Metadata.GetInterfaceImplementation(i); iface.Interface.WriteTo(module, output, genericContext, ILNameSyntax.TypeName); } output.WriteLine(); output.Unindent(); } output.WriteLine("{"); output.Indent(); bool oldIsInType = isInType; isInType = true; WriteAttributes(module, typeDefinition.GetCustomAttributes()); WriteSecurityDeclarations(module, typeDefinition.GetDeclarativeSecurityAttributes()); foreach (var tp in typeDefinition.GetGenericParameters()) { WriteGenericParametersAndAttributes(module, genericContext, tp); } var layout = typeDefinition.GetLayout(); if (!layout.IsDefault) { output.WriteLine(".pack {0}", layout.PackingSize); output.WriteLine(".size {0}", layout.Size); output.WriteLine(); } foreach (var ifaceHandle in interfaces) { var iface = module.Metadata.GetInterfaceImplementation(ifaceHandle); var customAttributes = iface.GetCustomAttributes(); if (customAttributes.Count != 0) { output.Write(".interfaceimpl type "); iface.Interface.WriteTo(module, output, genericContext, ILNameSyntax.TypeName); output.WriteLine(); output.Indent(); WriteAttributes(module, customAttributes); output.Unindent(); output.WriteLine(); } } var nestedTypes = Process(module, typeDefinition.GetNestedTypes()); if (nestedTypes.Any()) { output.WriteLine("// Nested Types"); foreach (var nestedType in nestedTypes) { cancellationToken.ThrowIfCancellationRequested(); DisassembleType(module, nestedType); output.WriteLine(); } output.WriteLine(); } var fields = Process(module, typeDefinition.GetFields()); if (fields.Any()) { output.WriteLine("// Fields"); foreach (var field in fields) { cancellationToken.ThrowIfCancellationRequested(); DisassembleField(module, field); } output.WriteLine(); } var methods = Process(module, typeDefinition.GetMethods()); if (methods.Any()) { output.WriteLine("// Methods"); foreach (var m in methods) { cancellationToken.ThrowIfCancellationRequested(); DisassembleMethod(module, m); output.WriteLine(); } } var events = Process(module, typeDefinition.GetEvents()); if (events.Any()) { output.WriteLine("// Events"); foreach (var ev in events) { cancellationToken.ThrowIfCancellationRequested(); DisassembleEvent(module, ev); output.WriteLine(); } output.WriteLine(); } var properties = Process(module, typeDefinition.GetProperties()); if (properties.Any()) { output.WriteLine("// Properties"); foreach (var prop in properties) { cancellationToken.ThrowIfCancellationRequested(); DisassembleProperty(module, prop); } output.WriteLine(); } CloseBlock("end of class " + (!typeDefinition.GetDeclaringType().IsNil ? module.Metadata.GetString(typeDefinition.Name) : typeDefinition.GetFullTypeName(module.Metadata).ToString())); isInType = oldIsInType; } public void DisassembleTypeHeader(MetadataFile module, TypeDefinitionHandle type) { var typeDefinition = module.Metadata.GetTypeDefinition(type); MetadataGenericContext genericContext = new MetadataGenericContext(type, module); DisassembleTypeHeaderInternal(module, type, typeDefinition, genericContext); } private void DisassembleTypeHeaderInternal(MetadataFile module, TypeDefinitionHandle handle, TypeDefinition typeDefinition, MetadataGenericContext genericContext) { output.WriteReference(module, handle, ".class", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); if ((typeDefinition.Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) output.Write("interface "); WriteEnum(typeDefinition.Attributes & TypeAttributes.VisibilityMask, typeVisibility, output); WriteEnum(typeDefinition.Attributes & TypeAttributes.LayoutMask, typeLayout, output); WriteEnum(typeDefinition.Attributes & TypeAttributes.StringFormatMask, typeStringFormat, output); const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; WriteFlags(typeDefinition.Attributes & ~masks, typeAttributes, output); output.Write(typeDefinition.GetDeclaringType().IsNil ? typeDefinition.GetFullTypeName(module.Metadata).ToILNameString() : DisassemblerHelpers.Escape(module.Metadata.GetString(typeDefinition.Name))); WriteTypeParameters(output, module, genericContext, typeDefinition.GetGenericParameters()); output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && isInType, isDefinition: isInType); output.WriteLine(); EntityHandle baseType = typeDefinition.GetBaseTypeOrNil(); if (!baseType.IsNil) { output.Indent(); output.Write("extends "); baseType.WriteTo(module, output, genericContext, ILNameSyntax.TypeName); output.WriteLine(); output.Unindent(); } } void WriteTypeParameters(ITextOutput output, MetadataFile module, MetadataGenericContext context, GenericParameterHandleCollection p) { if (p.Count > 0) { output.Write('<'); var metadata = module.Metadata; for (int i = 0; i < p.Count; i++) { if (i > 0) output.Write(", "); var gp = metadata.GetGenericParameter(p[i]); if ((gp.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) { output.Write("class "); } else if ((gp.Attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) { output.Write("valuetype "); } if ((gp.Attributes & SRMExtensions.AllowByRefLike) == SRMExtensions.AllowByRefLike) { output.Write("byreflike "); } if ((gp.Attributes & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint) { output.Write(".ctor "); } var constraints = gp.GetConstraints(); if (constraints.Count > 0) { output.Write('('); for (int j = 0; j < constraints.Count; j++) { if (j > 0) output.Write(", "); var constraint = metadata.GetGenericParameterConstraint(constraints[j]); constraint.Type.WriteTo(module, output, context, ILNameSyntax.TypeName); } output.Write(") "); } if ((gp.Attributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant) { output.Write('-'); } else if ((gp.Attributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant) { output.Write('+'); } output.Write(DisassemblerHelpers.Escape(metadata.GetString(gp.Name))); } output.Write('>'); } } #endregion #region Processing private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } private IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return EntityProcessor?.Process(module, items) ?? items; } #endregion #region Helper methods void WriteAttributes(MetadataFile module, CustomAttributeHandleCollection attributes) { var metadata = module.Metadata; foreach (CustomAttributeHandle a in Process(module, attributes)) { output.Write(".custom "); WriteMetadataToken(output, module, a, MetadataTokens.GetToken(a), spaceAfter: true, spaceBefore: false, ShowMetadataTokens, ShowMetadataTokensInBase10); var attr = metadata.GetCustomAttribute(a); attr.Constructor.WriteTo(module, output, default); if (!attr.Value.IsNil) { output.Write(" = "); if (DecodeCustomAttributeBlobs) WriteDecodedCustomAttributeBlob(attr, module); else WriteBlob(attr.Value, metadata); } output.WriteLine(); } } void WriteDecodedCustomAttributeBlob(CustomAttribute attr, MetadataFile module) { CustomAttributeValue<(PrimitiveTypeCode Code, string Name)> value; try { var provider = new SecurityDeclarationDecoder(output, AssemblyResolver, module); value = attr.DecodeValue(provider); } catch (BadImageFormatException) { output.Write("/* Could not decode attribute value */ "); WriteBlob(attr.Value, module.Metadata); return; } output.Write("{"); output.Indent(); foreach (var arg in value.FixedArguments) { output.WriteLine(); WriteValue(output, arg.Type, arg.Value); } foreach (var arg in value.NamedArguments) { output.WriteLine(); switch (arg.Kind) { case CustomAttributeNamedArgumentKind.Field: output.Write("field "); break; case CustomAttributeNamedArgumentKind.Property: output.Write("property "); break; } output.Write(arg.Type.Name ?? PrimitiveTypeCodeToString(arg.Type.Code)); output.Write(" " + DisassemblerHelpers.Escape(arg.Name) + " = "); WriteValue(output, arg.Type, arg.Value); } output.WriteLine(); output.Unindent(); output.Write("}"); } void WriteBlob(BlobHandle blob, MetadataReader metadata) { var reader = metadata.GetBlobReader(blob); WriteBlob(reader); } void WriteBlob(BlobReader reader) { output.Write("("); if (reader.Length > 0) { output.Indent(); for (int i = 0; i < reader.Length; i++) { if (i % 16 == 0 && i < reader.Length - 1) { output.WriteLine(); } else { output.Write(' '); } output.Write(reader.ReadByte().ToString("x2")); } output.WriteLine(); output.Unindent(); } output.Write(")"); } void OpenBlock(bool defaultCollapsed) { output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && defaultCollapsed, isDefinition: true); output.WriteLine(); output.WriteLine("{"); output.Indent(); } void CloseBlock(string comment = null) { output.Unindent(); output.Write("}"); if (comment != null) output.Write(" // " + comment); output.MarkFoldEnd(); output.WriteLine(); } internal static void WriteFlags(T flags, EnumNameCollection flagNames, ITextOutput output) where T : struct { long val = Convert.ToInt64(flags); long tested = 0; foreach (var pair in flagNames) { tested |= pair.Key; if ((val & pair.Key) != 0 && pair.Value != null) { output.Write(pair.Value); output.Write(' '); } } if ((val & ~tested) != 0) output.Write("flags({0:x4}) ", val & ~tested); } internal static void WriteEnum(T enumValue, EnumNameCollection enumNames, ITextOutput output) where T : struct { long val = Convert.ToInt64(enumValue); foreach (var pair in enumNames) { if (pair.Key == val) { if (pair.Value != null) { output.Write(pair.Value); output.Write(' '); } return; } } if (val != 0) { output.Write("flags({0:x4}) ", val); } } internal struct EnumNameCollection : IEnumerable> where T : struct { List> names = new List>(); public EnumNameCollection() { } public void Add(T flag, string name) { this.names.Add(new KeyValuePair(Convert.ToInt64(flag), name)); } public IEnumerator> GetEnumerator() { return names.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return names.GetEnumerator(); } } #endregion public void DisassembleNamespace(string nameSpace, MetadataFile module, IEnumerable types) { if (!string.IsNullOrEmpty(nameSpace)) { output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace)); OpenBlock(false); } bool oldIsInType = isInType; isInType = true; foreach (var td in types) { cancellationToken.ThrowIfCancellationRequested(); DisassembleType(module, td); output.WriteLine(); } if (!string.IsNullOrEmpty(nameSpace)) { CloseBlock(); isInType = oldIsInType; } } public void WriteAssemblyHeader(MetadataFile module) { var metadata = module.Metadata; if (!metadata.IsAssembly) return; output.Write(".assembly "); var asm = metadata.GetAssemblyDefinition(); if ((asm.Flags & AssemblyFlags.WindowsRuntime) == AssemblyFlags.WindowsRuntime) output.Write("windowsruntime "); output.Write(DisassemblerHelpers.Escape(metadata.GetString(asm.Name))); OpenBlock(false); WriteAttributes(module, asm.GetCustomAttributes()); WriteSecurityDeclarations(module, asm.GetDeclarativeSecurityAttributes()); if (!asm.PublicKey.IsNil) { output.Write(".publickey = "); WriteBlob(asm.PublicKey, metadata); output.WriteLine(); } if (asm.HashAlgorithm != AssemblyHashAlgorithm.None) { output.Write(".hash algorithm 0x{0:x8}", (int)asm.HashAlgorithm); if (asm.HashAlgorithm == AssemblyHashAlgorithm.Sha1) output.Write(" // SHA1"); output.WriteLine(); } Version v = asm.Version; if (v != null) { output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); } CloseBlock(); } public void WriteAssemblyReferences(MetadataReader metadata) { foreach (var m in metadata.GetModuleReferences()) { var mref = metadata.GetModuleReference(m); output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(metadata.GetString(mref.Name))); } foreach (var a in metadata.AssemblyReferences) { var aref = metadata.GetAssemblyReference(a); output.Write(".assembly extern "); if ((aref.Flags & AssemblyFlags.WindowsRuntime) == AssemblyFlags.WindowsRuntime) output.Write("windowsruntime "); output.Write(DisassemblerHelpers.Escape(metadata.GetString(aref.Name))); OpenBlock(false); if (!aref.PublicKeyOrToken.IsNil) { output.Write(".publickeytoken = "); WriteBlob(aref.PublicKeyOrToken, metadata); output.WriteLine(); } if (aref.Version != null) { output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision); } CloseBlock(); } } public void WriteModuleHeader(MetadataFile module, bool skipMVID = false) { var metadata = module.Metadata; void WriteExportedType(ExportedType exportedType) { if (!exportedType.Namespace.IsNil) { output.Write(DisassemblerHelpers.Escape(metadata.GetString(exportedType.Namespace))); output.Write('.'); } output.Write(DisassemblerHelpers.Escape(metadata.GetString(exportedType.Name))); } foreach (var et in metadata.ExportedTypes) { var exportedType = metadata.GetExportedType(et); output.Write(".class extern "); if (exportedType.IsForwarder) output.Write("forwarder "); WriteExportedType(exportedType); OpenBlock(false); switch (exportedType.Implementation.Kind) { case HandleKind.AssemblyFile: var file = metadata.GetAssemblyFile((AssemblyFileHandle)exportedType.Implementation); output.WriteLine(".file {0}", metadata.GetString(file.Name)); int typeDefId = exportedType.GetTypeDefinitionId(); if (typeDefId != 0) output.WriteLine(".class 0x{0:x8}", typeDefId); break; case HandleKind.ExportedType: output.Write(".class extern "); var declaringType = metadata.GetExportedType((ExportedTypeHandle)exportedType.Implementation); while (true) { WriteExportedType(declaringType); if (declaringType.Implementation.Kind == HandleKind.ExportedType) { declaringType = metadata.GetExportedType((ExportedTypeHandle)declaringType.Implementation); } else { break; } } output.WriteLine(); break; case HandleKind.AssemblyReference: output.Write(".assembly extern "); var reference = metadata.GetAssemblyReference((AssemblyReferenceHandle)exportedType.Implementation); output.Write(DisassemblerHelpers.Escape(metadata.GetString(reference.Name))); output.WriteLine(); break; default: throw new BadImageFormatException("Implementation must either be an index into the File, ExportedType or AssemblyRef table."); } CloseBlock(); } var moduleDefinition = metadata.GetModuleDefinition(); output.WriteLine(".module {0}", metadata.GetString(moduleDefinition.Name)); if (!skipMVID) { output.WriteLine("// MVID: {0}", metadata.GetGuid(moduleDefinition.Mvid).ToString("B").ToUpperInvariant()); } if (module is PEFile peFile) { var headers = peFile.Reader.PEHeaders; output.WriteLine(".imagebase 0x{0:x8}", headers.PEHeader.ImageBase); output.WriteLine(".file alignment 0x{0:x8}", headers.PEHeader.FileAlignment); output.WriteLine(".stackreserve 0x{0:x8}", headers.PEHeader.SizeOfStackReserve); output.WriteLine(".subsystem 0x{0:x} // {1}", headers.PEHeader.Subsystem, headers.PEHeader.Subsystem.ToString()); output.WriteLine(".corflags 0x{0:x} // {1}", headers.CorHeader.Flags, headers.CorHeader.Flags.ToString()); } WriteAttributes(module, metadata.GetCustomAttributes(EntityHandle.ModuleDefinition)); } public void WriteModuleContents(MetadataFile module) { foreach (var handle in Process(module, module.Metadata.GetTopLevelTypeDefinitions().ToArray())) { DisassembleType(module, handle); output.WriteLine(); } } } } ================================================ FILE: ICSharpCode.Decompiler/Disassembler/SortByNameProcessor.cs ================================================ // Copyright (c) 2022 Tom Englert // // 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Disassembler { public class SortByNameProcessor : IEntityProcessor { public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } public IReadOnlyCollection Process(MetadataFile module, IReadOnlyCollection items) { return items.OrderBy(item => GetSortKey(item, module)).ToArray(); } private static string GetSortKey(TypeDefinitionHandle handle, MetadataFile module) => handle.GetFullTypeName(module.Metadata).ToILNameString(); private static string GetSortKey(MethodDefinitionHandle handle, MetadataFile module) { PlainTextOutput output = new PlainTextOutput(); MethodDefinition definition = module.Metadata.GetMethodDefinition(handle); // Start with the methods name, skip return type output.Write(module.Metadata.GetString(definition.Name)); DisassemblerSignatureTypeProvider signatureProvider = new DisassemblerSignatureTypeProvider(module, output); MethodSignature> signature = definition.DecodeSignature(signatureProvider, new MetadataGenericContext(handle, module)); if (signature.GenericParameterCount > 0) { output.Write($"`{signature.GenericParameterCount}"); } InstructionOutputExtensions.WriteParameterList(output, signature); return output.ToString(); } private static string GetSortKey(InterfaceImplementationHandle handle, MetadataFile module) => module.Metadata.GetInterfaceImplementation(handle) .Interface .GetFullTypeName(module.Metadata) .ToILNameString(); private static string GetSortKey(FieldDefinitionHandle handle, MetadataFile module) => module.Metadata.GetString(module.Metadata.GetFieldDefinition(handle).Name); private static string GetSortKey(PropertyDefinitionHandle handle, MetadataFile module) => module.Metadata.GetString(module.Metadata.GetPropertyDefinition(handle).Name); private static string GetSortKey(EventDefinitionHandle handle, MetadataFile module) => module.Metadata.GetString(module.Metadata.GetEventDefinition(handle).Name); private static string GetSortKey(CustomAttributeHandle handle, MetadataFile module) => module.Metadata.GetCustomAttribute(handle) .Constructor .GetDeclaringType(module.Metadata) .GetFullTypeName(module.Metadata) .ToILNameString(); } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/GetPotentiallyNestedClassTypeReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.Documentation { /// /// A type reference of the form 'Some.Namespace.TopLevelType.NestedType`n'. /// We do not know the boundary between namespace name and top level type, so we have to try /// all possibilities. /// The type parameter count only applies to the innermost type, all outer types must be non-generic. /// [Serializable] public class GetPotentiallyNestedClassTypeReference : ITypeReference { readonly string typeName; readonly int typeParameterCount; public GetPotentiallyNestedClassTypeReference(string typeName, int typeParameterCount) { this.typeName = typeName; this.typeParameterCount = typeParameterCount; } public IType Resolve(ITypeResolveContext context) { string[] parts = typeName.Split('.'); var assemblies = new[] { context.CurrentModule }.Concat(context.Compilation.Modules); for (int i = parts.Length - 1; i >= 0; i--) { string ns = string.Join(".", parts, 0, i); string name = parts[i]; int topLevelTPC = (i == parts.Length - 1 ? typeParameterCount : 0); foreach (var asm in assemblies) { if (asm == null) continue; ITypeDefinition typeDef = asm.GetTypeDefinition(new TopLevelTypeName(ns, name, topLevelTPC)); for (int j = i + 1; j < parts.Length && typeDef != null; j++) { int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); typeDef = typeDef.NestedTypes.FirstOrDefault(n => n.Name == parts[j] && n.TypeParameterCount == tpc); } if (typeDef != null) return typeDef; } } int idx = typeName.LastIndexOf('.'); if (idx < 0) return new UnknownType("", typeName, typeParameterCount); // give back a guessed namespace/type name return new UnknownType(typeName.Substring(0, idx), typeName.Substring(idx + 1), typeParameterCount); } /// /// Resolves the type reference within the context of the given PE file. /// /// Either TypeDefinitionHandle, if the type is defined in the module or ExportedTypeHandle, /// if the module contains a type forwarder. Returns a nil handle, if the type was not found. public EntityHandle ResolveInPEFile(MetadataFile module) { string[] parts = typeName.Split('.'); for (int i = parts.Length - 1; i >= 0; i--) { string ns = string.Join(".", parts, 0, i); string name = parts[i]; int topLevelTPC = (i == parts.Length - 1 ? typeParameterCount : 0); var topLevelName = new TopLevelTypeName(ns, name, topLevelTPC); var typeHandle = module.GetTypeDefinition(topLevelName); for (int j = i + 1; j < parts.Length && !typeHandle.IsNil; j++) { int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); var typeDef = module.Metadata.GetTypeDefinition(typeHandle); string lookupName = parts[j] + (tpc > 0 ? "`" + tpc : ""); typeHandle = typeDef.GetNestedTypes().FirstOrDefault(n => IsEqualShortName(n, module.Metadata, lookupName)); } if (!typeHandle.IsNil) return typeHandle; FullTypeName typeName = topLevelName; for (int j = i + 1; j < parts.Length; j++) { int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); typeName = typeName.NestedType(parts[j], tpc); } var exportedType = module.GetTypeForwarder(typeName); if (!exportedType.IsNil) return exportedType; } return default; bool IsEqualShortName(TypeDefinitionHandle h, MetadataReader metadata, string name) { var nestedType = metadata.GetTypeDefinition(h); return metadata.StringComparer.Equals(nestedType.Name, name); } } } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/IdStringMemberReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Documentation { [Serializable] class IdStringMemberReference : IMemberReference { readonly ITypeReference declaringTypeReference; readonly char memberType; readonly string memberIdString; public IdStringMemberReference(ITypeReference declaringTypeReference, char memberType, string memberIdString) { this.declaringTypeReference = declaringTypeReference; this.memberType = memberType; this.memberIdString = memberIdString; } bool CanMatch(IMember member) { switch (member.SymbolKind) { case SymbolKind.Field: return memberType == 'F'; case SymbolKind.Property: case SymbolKind.Indexer: return memberType == 'P'; case SymbolKind.Event: return memberType == 'E'; case SymbolKind.Method: case SymbolKind.Operator: case SymbolKind.Constructor: case SymbolKind.Destructor: return memberType == 'M'; default: throw new NotSupportedException(member.SymbolKind.ToString()); } } public ITypeReference DeclaringTypeReference { get { return declaringTypeReference; } } public IMember Resolve(ITypeResolveContext context) { IType declaringType = declaringTypeReference.Resolve(context); foreach (var member in declaringType.GetMembers(CanMatch, GetMemberOptions.IgnoreInheritedMembers)) { if (IdStringProvider.GetIdString(member) == memberIdString) return member; } return null; } } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/IdStringProvider.cs ================================================ // Copyright (c) 2010-2018 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 // 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; using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.Documentation { /// /// Provides ID strings for entities. (C# 4.0 spec, §A.3.1) /// ID strings are used to identify members in XML documentation files. /// public static class IdStringProvider { #region GetIdString /// /// Gets the ID string (C# 4.0 spec, §A.3.1) for the specified entity. /// public static string GetIdString(this IEntity entity) { StringBuilder b = new StringBuilder(); switch (entity.SymbolKind) { case SymbolKind.TypeDefinition: b.Append("T:"); AppendTypeName(b, (ITypeDefinition)entity, false); return b.ToString(); case SymbolKind.Field: b.Append("F:"); break; case SymbolKind.Property: case SymbolKind.Indexer: b.Append("P:"); break; case SymbolKind.Event: b.Append("E:"); break; default: b.Append("M:"); break; } IMember member = (IMember)entity; if (member.DeclaringType != null) { AppendTypeName(b, member.DeclaringType, false); b.Append('.'); } if (member.IsExplicitInterfaceImplementation && member.Name.IndexOf('.') < 0 && member.ExplicitlyImplementedInterfaceMembers.Count() == 1) { AppendTypeName(b, member.ExplicitlyImplementedInterfaceMembers.First().DeclaringType, true); b.Append('#'); } b.Append(member.Name.Replace('.', '#').Replace('<', '{').Replace('>', '}')); IMethod method = member as IMethod; if (method != null && method.TypeParameters.Count > 0) { b.Append("``"); b.Append(method.TypeParameters.Count); } IParameterizedMember parameterizedMember = member as IParameterizedMember; if (parameterizedMember != null && parameterizedMember.Parameters.Count > 0) { b.Append('('); var parameters = parameterizedMember.Parameters; for (int i = 0; i < parameters.Count; i++) { if (i > 0) b.Append(','); AppendTypeName(b, parameters[i].Type, false); } b.Append(')'); } if (member.SymbolKind == SymbolKind.Operator && (member.Name == "op_Implicit" || member.Name == "op_Explicit")) { b.Append('~'); AppendTypeName(b, member.ReturnType, false); } return b.ToString(); } #endregion #region GetTypeName public static string GetTypeName(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); StringBuilder b = new StringBuilder(); AppendTypeName(b, type, false); return b.ToString(); } static void AppendTypeName(StringBuilder b, IType type, bool explicitInterfaceImpl) { switch (type.Kind) { case TypeKind.Dynamic: b.Append(explicitInterfaceImpl ? "System#Object" : "System.Object"); break; case TypeKind.TypeParameter: ITypeParameter tp = (ITypeParameter)type; if (explicitInterfaceImpl) { b.Append(tp.Name); } else { b.Append('`'); if (tp.OwnerType == SymbolKind.Method) b.Append('`'); b.Append(tp.Index); } break; case TypeKind.Array: ArrayType array = (ArrayType)type; AppendTypeName(b, array.ElementType, explicitInterfaceImpl); b.Append('['); if (array.Dimensions > 1) { for (int i = 0; i < array.Dimensions; i++) { if (i > 0) b.Append(explicitInterfaceImpl ? '@' : ','); if (!explicitInterfaceImpl) b.Append("0:"); } } b.Append(']'); break; case TypeKind.Pointer: AppendTypeName(b, ((PointerType)type).ElementType, explicitInterfaceImpl); b.Append('*'); break; case TypeKind.ByReference: AppendTypeName(b, ((ByReferenceType)type).ElementType, explicitInterfaceImpl); b.Append('@'); break; default: IType declType = type.DeclaringType; if (declType != null) { AppendTypeName(b, declType, explicitInterfaceImpl); b.Append(explicitInterfaceImpl ? '#' : '.'); b.Append(type.Name); AppendTypeParameters(b, type, declType.TypeParameterCount, explicitInterfaceImpl); } else { if (explicitInterfaceImpl) b.Append(type.FullName.Replace('.', '#')); else b.Append(type.FullName); AppendTypeParameters(b, type, 0, explicitInterfaceImpl); } break; } } static void AppendTypeParameters(StringBuilder b, IType type, int outerTypeParameterCount, bool explicitInterfaceImpl) { int tpc = type.TypeParameterCount - outerTypeParameterCount; if (tpc > 0) { ParameterizedType pt = type as ParameterizedType; if (pt != null) { b.Append('{'); var ta = pt.TypeArguments; for (int i = outerTypeParameterCount; i < ta.Count; i++) { if (i > outerTypeParameterCount) b.Append(explicitInterfaceImpl ? '@' : ','); AppendTypeName(b, ta[i], explicitInterfaceImpl); } b.Append('}'); } else { b.Append('`'); b.Append(tpc); } } } #endregion #region ParseMemberName /// /// Parse the ID string into a member reference. /// /// The ID string representing the member (with "M:", "F:", "P:" or "E:" prefix). /// A member reference that represents the ID string. /// The syntax of the ID string is invalid /// /// The member reference will look in first, /// and if the member is not found there, /// it will look in all other assemblies of the compilation. /// public static IMemberReference ParseMemberIdString(string memberIdString) { if (memberIdString == null) throw new ArgumentNullException(nameof(memberIdString)); if (memberIdString.Length < 2 || memberIdString[1] != ':') throw new ReflectionNameParseException(0, "Missing type tag"); char typeChar = memberIdString[0]; int parenPos = memberIdString.IndexOf('('); if (parenPos < 0) parenPos = memberIdString.LastIndexOf('~'); if (parenPos < 0) parenPos = memberIdString.Length; int dotPos = memberIdString.LastIndexOf('.', parenPos - 1); if (dotPos < 0) throw new ReflectionNameParseException(0, "Could not find '.' separating type name from member name"); string typeName = memberIdString.Substring(0, dotPos); int pos = 2; ITypeReference typeReference = ParseTypeName(typeName, ref pos); if (pos != typeName.Length) throw new ReflectionNameParseException(pos, "Expected end of type name"); // string memberName = memberIDString.Substring(dotPos + 1, parenPos - (dotPos + 1)); // pos = memberName.LastIndexOf("``"); // if (pos > 0) // memberName = memberName.Substring(0, pos); // memberName = memberName.Replace('#', '.'); return new IdStringMemberReference(typeReference, typeChar, memberIdString); } #endregion #region ParseTypeName /// /// Parse the ID string type name into a type reference. /// /// The ID string representing the type (the "T:" prefix is optional). /// A type reference that represents the ID string. /// The syntax of the ID string is invalid /// /// /// The type reference will look in first, /// and if the type is not found there, /// it will look in all other assemblies of the compilation. /// /// /// If the type is open (contains type parameters '`0' or '``0'), /// an with the appropriate CurrentTypeDefinition/CurrentMember is required /// to resolve the reference to the ITypeParameter. /// /// public static ITypeReference ParseTypeName(string typeName) { if (typeName == null) throw new ArgumentNullException(nameof(typeName)); int pos = 0; if (typeName.StartsWith("T:", StringComparison.Ordinal)) pos = 2; ITypeReference r = ParseTypeName(typeName, ref pos); if (pos < typeName.Length) throw new ReflectionNameParseException(pos, "Expected end of type name"); return r; } static bool IsIDStringSpecialCharacter(char c) { switch (c) { case ':': case '{': case '}': case '[': case ']': case '(': case ')': case '`': case '*': case '@': case ',': return true; default: return false; } } static ITypeReference ParseTypeName(string typeName, ref int pos) { string reflectionTypeName = typeName; if (pos == typeName.Length) throw new ReflectionNameParseException(pos, "Unexpected end"); ITypeReference result; if (reflectionTypeName[pos] == '`') { // type parameter reference pos++; if (pos == reflectionTypeName.Length) throw new ReflectionNameParseException(pos, "Unexpected end"); if (reflectionTypeName[pos] == '`') { // method type parameter reference pos++; int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos); result = TypeParameterReference.Create(SymbolKind.Method, index); } else { // class type parameter reference int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos); result = TypeParameterReference.Create(SymbolKind.TypeDefinition, index); } } else { // not a type parameter reference: read the actual type name List typeArguments = new List(); string typeNameWithoutSuffix = ReadTypeName(typeName, ref pos, true, out int typeParameterCount, typeArguments); result = new GetPotentiallyNestedClassTypeReference(typeNameWithoutSuffix, typeParameterCount); while (pos < typeName.Length && typeName[pos] == '.') { pos++; string nestedTypeName = ReadTypeName(typeName, ref pos, false, out typeParameterCount, typeArguments); result = new NestedTypeReference(result, nestedTypeName, typeParameterCount); } if (typeArguments.Count > 0) { result = new ParameterizedTypeReference(result, typeArguments); } } while (pos < typeName.Length) { switch (typeName[pos]) { case '[': int dimensions = 1; do { pos++; if (pos == typeName.Length) throw new ReflectionNameParseException(pos, "Unexpected end"); if (typeName[pos] == ',') dimensions++; } while (typeName[pos] != ']'); result = new ArrayTypeReference(result, dimensions); break; case '*': result = new PointerTypeReference(result); break; case '@': result = new ByReferenceTypeReference(result); break; default: return result; } pos++; } return result; } static string ReadTypeName(string typeName, ref int pos, bool allowDottedName, out int typeParameterCount, List typeArguments) { int startPos = pos; // skip the simple name portion: while (pos < typeName.Length && !IsIDStringSpecialCharacter(typeName[pos]) && (allowDottedName || typeName[pos] != '.')) pos++; if (pos == startPos) throw new ReflectionNameParseException(pos, "Expected type name"); string shortTypeName = typeName.Substring(startPos, pos - startPos); // read type arguments: typeParameterCount = 0; if (pos < typeName.Length && typeName[pos] == '`') { // unbound generic type pos++; typeParameterCount = ReflectionHelper.ReadTypeParameterCount(typeName, ref pos); } else if (pos < typeName.Length && typeName[pos] == '{') { // bound generic type do { pos++; typeArguments.Add(ParseTypeName(typeName, ref pos)); typeParameterCount++; if (pos == typeName.Length) throw new ReflectionNameParseException(pos, "Unexpected end"); } while (typeName[pos] == ','); if (typeName[pos] != '}') throw new ReflectionNameParseException(pos, "Expected '}'"); pos++; } return shortTypeName; } #endregion #region FindEntity /// /// Finds the entity in the given type resolve context. /// /// ID string of the entity. /// Type resolve context /// Returns the entity, or null if it is not found. /// The syntax of the ID string is invalid public static IEntity FindEntity(string idString, ITypeResolveContext context) { if (idString == null) throw new ArgumentNullException(nameof(idString)); if (context == null) throw new ArgumentNullException(nameof(context)); if (idString.StartsWith("T:", StringComparison.Ordinal)) { return ParseTypeName(idString.Substring(2)).Resolve(context).GetDefinition(); } else { return ParseMemberIdString(idString).Resolve(context); } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs ================================================ // 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 // 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.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Documentation { /// /// Helps finding and loading .xml documentation. /// public static class XmlDocLoader { static readonly Lazy mscorlibDocumentation = new Lazy(LoadMscorlibDocumentation); static readonly ConditionalWeakTable cache = new(); static XmlDocumentationProvider LoadMscorlibDocumentation() { string xmlDocFile = FindXmlDocumentation("mscorlib.dll", TargetRuntime.Net_4_0) ?? FindXmlDocumentation("mscorlib.dll", TargetRuntime.Net_2_0); if (xmlDocFile != null) return new XmlDocumentationProvider(xmlDocFile); else return null; } public static XmlDocumentationProvider MscorlibDocumentation { get { return mscorlibDocumentation.Value; } } public static XmlDocumentationProvider LoadDocumentation(MetadataFile module) { if (module == null) throw new ArgumentNullException(nameof(module)); lock (cache) { if (!cache.TryGetValue(module, out XmlDocumentationProvider xmlDoc)) { string xmlDocFile = LookupLocalizedXmlDoc(module.FileName); if (xmlDocFile == null) { xmlDocFile = FindXmlDocumentation(Path.GetFileName(module.FileName), module.GetRuntime()); } if (xmlDocFile != null) { xmlDoc = new XmlDocumentationProvider(xmlDocFile); cache.Add(module, xmlDoc); } else { cache.Add(module, null); // add missing documentation files as well xmlDoc = null; } } return xmlDoc; } } static readonly string referenceAssembliesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\\Framework"); static readonly string frameworkPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework"); static string FindXmlDocumentation(string assemblyFileName, TargetRuntime runtime) { string fileName; switch (runtime) { case TargetRuntime.Net_1_0: fileName = LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v1.0.3705", assemblyFileName)); break; case TargetRuntime.Net_1_1: fileName = LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v1.1.4322", assemblyFileName)); break; case TargetRuntime.Net_2_0: fileName = LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.5", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v3.5\Profile\Client", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.0", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v2.0.50727", assemblyFileName)); break; case TargetRuntime.Net_4_0: default: fileName = LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.8.1", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.8", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7.2", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7.1", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6.2", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6.1", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5.2", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5.1", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.0", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v4.0.30319", assemblyFileName)); break; } return fileName; } /// /// Given the assembly file name, looks up the XML documentation file name. /// Returns null if no XML documentation file is found. /// internal static string LookupLocalizedXmlDoc(string fileName) { if (string.IsNullOrEmpty(fileName)) return null; string xmlFileName = Path.ChangeExtension(fileName, ".xml"); CultureInfo currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture; string localizedXmlDocFile = GetLocalizedName(xmlFileName, currentCulture.Name); string localizedXmlDocFallbackFile = GetLocalizedName(xmlFileName, currentCulture.TwoLetterISOLanguageName); //Debug.WriteLine("Try find XMLDoc @" + localizedXmlDocFile); if (File.Exists(localizedXmlDocFile)) { return localizedXmlDocFile; } //Debug.WriteLine("Try find XMLDoc @" + localizedXmlDocFallbackFile); if (File.Exists(localizedXmlDocFallbackFile)) { return localizedXmlDocFallbackFile; } //Debug.WriteLine("Try find XMLDoc @" + xmlFileName); if (File.Exists(xmlFileName)) { return xmlFileName; } if (currentCulture.TwoLetterISOLanguageName != "en") { string englishXmlDocFile = GetLocalizedName(xmlFileName, "en"); //Debug.WriteLine("Try find XMLDoc @" + englishXmlDocFile); if (File.Exists(englishXmlDocFile)) { return englishXmlDocFile; } } return null; } private static string GetLocalizedName(string fileName, string language) { return Path.Combine(Path.GetDirectoryName(fileName), language, Path.GetFileName(fileName)); } } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs ================================================ // Copyright (c) 2009-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Documentation { /// /// Represents an element in the XML documentation. /// Any occurrences of "<inheritdoc/>" are replaced with the inherited documentation. /// public class XmlDocumentationElement { readonly XElement? element; readonly IEntity? declaringEntity; readonly Func? crefResolver; volatile string? textContent; /// /// Inheritance level; used to prevent cyclic doc inheritance. /// int nestingLevel; /// /// Creates a new documentation element. /// public XmlDocumentationElement(XElement element, IEntity? declaringEntity, Func? crefResolver) { if (element == null) throw new ArgumentNullException(nameof(element)); this.element = element; this.declaringEntity = declaringEntity; this.crefResolver = crefResolver; } /// /// Creates a new documentation element. /// public XmlDocumentationElement(string text, IEntity? declaringEntity) { if (text == null) throw new ArgumentNullException(nameof(text)); this.declaringEntity = declaringEntity; this.textContent = text; } /// /// Gets the entity on which this documentation was originally declared. /// May return null. /// public IEntity? DeclaringEntity { get { return declaringEntity; } } IEntity? referencedEntity; volatile bool referencedEntityInitialized; /// /// Gets the entity referenced by the 'cref' attribute. /// May return null. /// public IEntity? ReferencedEntity { get { if (!referencedEntityInitialized) { string? cref = GetAttribute("cref"); try { if (!string.IsNullOrEmpty(cref) && crefResolver != null) referencedEntity = crefResolver(cref!); } catch { referencedEntity = null; } referencedEntityInitialized = true; } return referencedEntity; } } /// /// Gets the element name. /// public string Name { get { return element != null ? element.Name.LocalName : string.Empty; } } /// /// Gets the attribute value. /// public string? GetAttribute(string? name) { return name == null ? null : element?.Attribute(name)?.Value; } /// /// Gets whether this is a pure text node. /// public bool IsTextNode { get { return element == null; } } /// /// Gets the text content. /// public string TextContent { get { if (textContent == null) { StringBuilder b = new StringBuilder(); foreach (var child in this.Children) b.Append(child.TextContent); textContent = b.ToString(); } return textContent; } } IList? children; /// /// Gets the child elements. /// public IList Children { get { if (element == null) return EmptyList.Instance; return LazyInitializer.EnsureInitialized( ref this.children, () => CreateElements(element.Nodes(), declaringEntity, crefResolver, nestingLevel))!; } } static readonly string[] doNotInheritIfAlreadyPresent = { "example", "exclude", "filterpriority", "preliminary", "summary", "remarks", "returns", "threadsafety", "value" }; static List CreateElements(IEnumerable childObjects, IEntity? declaringEntity, Func? crefResolver, int nestingLevel) { List list = new List(); foreach (var child in childObjects) { var childText = child as XText; var childTag = child as XCData; var childElement = child as XElement; if (childText != null) { list.Add(new XmlDocumentationElement(childText.Value, declaringEntity)); } else if (childTag != null) { list.Add(new XmlDocumentationElement(childTag.Value, declaringEntity)); } else if (childElement != null) { if (nestingLevel < 5 && childElement.Name == "inheritdoc") { string? cref = childElement.Attribute("cref")?.Value; IEntity? inheritedFrom = null; string? inheritedDocumentation = null; if (cref != null && crefResolver != null) { inheritedFrom = crefResolver(cref); if (inheritedFrom != null) inheritedDocumentation = "" + inheritedFrom.GetDocumentation() + ""; } else if (declaringEntity != null) { foreach (IMember baseMember in InheritanceHelper.GetBaseMembers((IMember)declaringEntity, includeImplementedInterfaces: true)) { inheritedDocumentation = baseMember.GetDocumentation(); if (inheritedDocumentation != null) { inheritedFrom = baseMember; inheritedDocumentation = "" + inheritedDocumentation + ""; break; } } } if (inheritedDocumentation != null) { var doc = XDocument.Parse(inheritedDocumentation).Element("doc"); // XPath filter not yet implemented if (doc != null && childElement.Parent?.Parent == null && childElement.Attribute("select")?.Value == null) { // Inheriting documentation at the root level List doNotInherit = new List(); doNotInherit.Add("overloads"); doNotInherit.AddRange(childObjects.OfType().Select(e => e.Name.LocalName).Intersect( doNotInheritIfAlreadyPresent)); var inheritedChildren = doc.Nodes().Where( inheritedObject => { XElement? inheritedElement = inheritedObject as XElement; return !(inheritedElement != null && doNotInherit.Contains(inheritedElement.Name.LocalName)); }); list.AddRange(CreateElements(inheritedChildren, inheritedFrom, crefResolver, nestingLevel + 1)); } } } else { list.Add(new XmlDocumentationElement(childElement, declaringEntity, crefResolver) { nestingLevel = nestingLevel }); } } } if (list.Count > 0 && list[0].IsTextNode) { if (string.IsNullOrWhiteSpace(list[0].textContent)) list.RemoveAt(0); else list[0].textContent = list[0].textContent!.TrimStart(); } if (list.Count > 0 && list[list.Count - 1].IsTextNode) { if (string.IsNullOrWhiteSpace(list[list.Count - 1].textContent)) list.RemoveAt(list.Count - 1); else list[list.Count - 1].textContent = list[list.Count - 1].textContent!.TrimEnd(); } return list; } /// public override string ToString() { if (element != null) return "<" + element.Name + ">"; else return this.TextContent; } } } ================================================ FILE: ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs ================================================ // Copyright (c) 2010-2013 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 // 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.IO; using System.Runtime.Serialization; using System.Text; using System.Xml; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Documentation { /// /// Provides XML documentation for type and member definitions in source code. /// public interface IDocumentationProvider { /// /// Returns the XML documentation for the given . /// May return null, if no documentation is present for the entity. /// /// is null. string GetDocumentation(IEntity entity); } /// /// Provides documentation from an .xml file (as generated by the Microsoft C# compiler). /// /// /// This class first creates an in-memory index of the .xml file, and then uses that to read only the requested members. /// This way, we avoid keeping all the documentation in memory. /// The .xml file is only opened when necessary, the file handle is not kept open all the time. /// If the .xml file is changed, the index will automatically be recreated. /// [Serializable] public class XmlDocumentationProvider : IDeserializationCallback, IDocumentationProvider { #region Cache sealed class XmlDocumentationCache { readonly KeyValuePair[] entries; int pos; public XmlDocumentationCache(int size = 50) { if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size), size, "Value must be positive"); this.entries = new KeyValuePair[size]; } internal bool TryGet(string key, out string value) { foreach (var pair in entries) { if (pair.Key == key) { value = pair.Value; return true; } } value = null; return false; } internal void Add(string key, string value) { entries[pos++] = new KeyValuePair(key, value); if (pos == entries.Length) pos = 0; } } #endregion [Serializable] struct IndexEntry : IComparable { /// /// Hash code of the documentation tag /// internal readonly int HashCode; /// /// Position in the .xml file where the documentation starts /// internal readonly int PositionInFile; internal IndexEntry(int hashCode, int positionInFile) { this.HashCode = hashCode; this.PositionInFile = positionInFile; } public int CompareTo(IndexEntry other) { return this.HashCode.CompareTo(other.HashCode); } } [NonSerialized] XmlDocumentationCache cache = new XmlDocumentationCache(); readonly string fileName; readonly Encoding encoding; volatile IndexEntry[] index; // SORTED array of index entries #region Constructor / Redirection support /// /// Creates a new XmlDocumentationProvider. /// /// Name of the .xml file. /// Error reading from XML file (or from redirected file) /// Invalid XML file public XmlDocumentationProvider(string fileName) { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { using (XmlTextReader xmlReader = new XmlTextReader(fs)) { xmlReader.XmlResolver = null; // no DTD resolving xmlReader.MoveToContent(); if (string.IsNullOrEmpty(xmlReader.GetAttribute("redirect"))) { this.fileName = fileName; this.encoding = xmlReader.Encoding; ReadXmlDoc(xmlReader); } else { string redirectionTarget = GetRedirectionTarget(fileName, xmlReader.GetAttribute("redirect")); if (redirectionTarget != null) { Debug.WriteLine("XmlDoc " + fileName + " is redirecting to " + redirectionTarget); using (FileStream redirectedFs = new FileStream(redirectionTarget, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { using (XmlTextReader redirectedXmlReader = new XmlTextReader(redirectedFs)) { redirectedXmlReader.XmlResolver = null; // no DTD resolving redirectedXmlReader.MoveToContent(); this.fileName = redirectionTarget; this.encoding = redirectedXmlReader.Encoding; ReadXmlDoc(redirectedXmlReader); } } } else { throw new XmlException("XmlDoc " + fileName + " is redirecting to " + xmlReader.GetAttribute("redirect") + ", but that file was not found."); } } } } } static string GetRedirectionTarget(string xmlFileName, string target) { string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); programFilesDir = AppendDirectorySeparator(programFilesDir); string corSysDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); corSysDir = AppendDirectorySeparator(corSysDir); var fileName = target.Replace("%PROGRAMFILESDIR%", programFilesDir) .Replace("%CORSYSDIR%", corSysDir); if (!Path.IsPathRooted(fileName)) fileName = Path.Combine(Path.GetDirectoryName(xmlFileName), fileName); return XmlDocLoader.LookupLocalizedXmlDoc(fileName); } static string AppendDirectorySeparator(string dir) { if (dir.EndsWith("\\", StringComparison.Ordinal) || dir.EndsWith("/", StringComparison.Ordinal)) return dir; else return dir + Path.DirectorySeparatorChar; } #endregion #region Load / Create Index void ReadXmlDoc(XmlTextReader reader) { //lastWriteDate = File.GetLastWriteTimeUtc(fileName); // Open up a second file stream for the line<->position mapping using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { LinePositionMapper linePosMapper = new LinePositionMapper(fs, encoding); List indexList = new List(); while (reader.Read()) { if (reader.IsStartElement()) { switch (reader.LocalName) { case "members": ReadMembersSection(reader, linePosMapper, indexList); break; } } } indexList.Sort(); this.index = indexList.ToArray(); // volatile write } } sealed class LinePositionMapper { readonly FileStream fs; readonly Decoder decoder; int currentLine = 1; char prevChar = '\0'; // buffers for use with Decoder: readonly byte[] input = new byte[1]; readonly char[] output = new char[2]; public LinePositionMapper(FileStream fs, Encoding encoding) { this.decoder = encoding.GetDecoder(); this.fs = fs; } public int GetPositionForLine(int line) { Debug.Assert(line >= currentLine); while (line > currentLine) { int b = fs.ReadByte(); if (b < 0) throw new EndOfStreamException(); input[0] = (byte)b; decoder.Convert(input, 0, 1, output, 0, output.Length, false, out int bytesUsed, out int charsUsed, out _); Debug.Assert(bytesUsed == 1); if (charsUsed == 1) { if ((prevChar != '\r' && output[0] == '\n') || output[0] == '\r') currentLine++; prevChar = output[0]; } } return checked((int)fs.Position); } } static void ReadMembersSection(XmlTextReader reader, LinePositionMapper linePosMapper, List indexList) { while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.EndElement: if (reader.LocalName == "members") { return; } break; case XmlNodeType.Element: if (reader.LocalName == "member") { int pos = linePosMapper.GetPositionForLine(reader.LineNumber) + Math.Max(reader.LinePosition - 2, 0); string memberAttr = reader.GetAttribute("name"); if (memberAttr != null) indexList.Add(new IndexEntry(GetHashCode(memberAttr), pos)); reader.Skip(); } break; } } } /// /// Hash algorithm used for the index. /// This is a custom implementation so that old index files work correctly /// even when the .NET string.GetHashCode implementation changes /// (e.g. due to .NET 4.5 hash randomization) /// static int GetHashCode(string key) { unchecked { int h = 0; foreach (char c in key) { h = (h << 5) - h + c; } return h; } } #endregion #region GetDocumentation /// /// Get the documentation for the member with the specified documentation key. /// public string GetDocumentation(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); return GetDocumentation(key, true); } /// /// Get the documentation for the specified member. /// public string GetDocumentation(IEntity entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); return GetDocumentation(entity.GetIdString()); } string GetDocumentation(string key, bool allowReload) { int hashcode = GetHashCode(key); var index = this.index; // read volatile field // index is sorted, so we can use binary search int m = Array.BinarySearch(index, new IndexEntry(hashcode, 0)); if (m < 0) return null; // correct hash code found. // possibly there are multiple items with the same hash, so go to the first. while (--m >= 0 && index[m].HashCode == hashcode) ; // m is now 1 before the first item with the correct hash XmlDocumentationCache cache = this.cache; lock (cache) { if (!cache.TryGet(key, out string val)) { try { // go through all items that have the correct hash while (++m < index.Length && index[m].HashCode == hashcode) { val = LoadDocumentation(key, index[m].PositionInFile); if (val != null) break; } // cache the result (even if it is null) cache.Add(key, val); } catch (IOException) { // may happen if the documentation file was deleted/is inaccessible/changed (EndOfStreamException) return allowReload ? ReloadAndGetDocumentation(key) : null; } catch (XmlException) { // may happen if the documentation file was changed so that the file position no longer starts on a valid XML element return allowReload ? ReloadAndGetDocumentation(key) : null; } } return val; } } string ReloadAndGetDocumentation(string key) { try { // Reload the index using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { using (XmlTextReader xmlReader = new XmlTextReader(fs)) { xmlReader.XmlResolver = null; // no DTD resolving xmlReader.MoveToContent(); ReadXmlDoc(xmlReader); } } } catch (IOException) { // Ignore errors on reload; IEntity.Documentation callers aren't prepared to handle exceptions this.index = Empty.Array; // clear index to avoid future load attempts return null; } catch (XmlException) { this.index = Empty.Array; // clear index to avoid future load attempts return null; } return GetDocumentation(key, allowReload: false); // prevent infinite reload loops } #endregion #region Load / Read XML string LoadDocumentation(string key, int positionInFile) { using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { fs.Position = positionInFile; var context = new XmlParserContext(null, null, null, XmlSpace.None) { Encoding = encoding }; using (XmlTextReader r = new XmlTextReader(fs, XmlNodeType.Element, context)) { r.XmlResolver = null; // no DTD resolving while (r.Read()) { if (r.NodeType == XmlNodeType.Element) { string memberAttr = r.GetAttribute("name"); if (memberAttr == key) { return r.ReadInnerXml(); } else { return null; } } } return null; } } } #endregion public virtual void OnDeserialization(object sender) { cache = new XmlDocumentationCache(); } } } ================================================ FILE: ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// Represents a block in the control flow graph. /// [DebuggerDisplay("CFG UserIndex={UserIndex}, UserData={UserData}")] public class ControlFlowNode { /// /// User index, can be used to look up additional information in an array. /// public int UserIndex; /// /// User data. /// public object UserData; /// /// Visited flag, used in various algorithms. /// public bool Visited; /// /// Gets the node index in a post-order traversal of the control flow graph, starting at the /// entry point. This field gets computed by dominance analysis. /// public int PostOrderNumber; /// /// Gets whether this node is reachable. Requires that dominance is computed! /// public bool IsReachable { get { return DominatorTreeChildren != null; } } /// /// Gets the immediate dominator (the parent in the dominator tree). /// Null if dominance has not been calculated; or if the node is unreachable. /// public ControlFlowNode ImmediateDominator { get; internal set; } /// /// List of children in the dominator tree. /// Null if dominance has not been calculated; or if the node is unreachable. /// public List DominatorTreeChildren { get; internal set; } /// /// List of incoming control flow edges. /// public readonly List Predecessors = new List(); /// /// List of outgoing control flow edges. /// public readonly List Successors = new List(); public void AddEdgeTo(ControlFlowNode target) { this.Successors.Add(target); target.Predecessors.Add(this); } public void TraversePreOrder(Func> children, Action visitAction) { GraphTraversal.DepthFirstSearch(new[] { this }, Visit, children); bool Visit(ControlFlowNode node) { if (node.Visited) return false; node.Visited = true; visitAction(node); return true; } } public void TraversePostOrder(Func> children, Action visitAction) { GraphTraversal.DepthFirstSearch(new[] { this }, Visit, children, postorderAction: visitAction); bool Visit(ControlFlowNode node) { if (node.Visited) return false; node.Visited = true; return true; } } /// /// Gets whether this dominates . /// public bool Dominates(ControlFlowNode node) { // TODO: this can be made O(1) by numbering the dominator tree ControlFlowNode tmp = node; while (tmp != null) { if (tmp == this) return true; tmp = tmp.ImmediateDominator; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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 ICSharpCode.Decompiler.IL; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// Interface for use with DataFlowVisitor. /// /// A mutable container for the state tracked by the data flow analysis. /// /// /// States must form a join-semilattice: https://en.wikipedia.org/wiki/Semilattice /// /// To handle try{} finally{} properly, states should implement MeetWith() as well, /// and thus should form a lattice. /// /// DataFlowVisitor expects the state to behave like a mutable reference type. /// It might still be a good idea to use a struct to implement it so that .NET uses static dispatch for /// method calls on the type parameter, but that struct must consist only of a readonly field /// referencing some mutable object, to ensure the type parameter behaves as it if was a mutable reference type. /// public interface IDataFlowState where Self : IDataFlowState { /// /// Gets whether this state is "less than" (or equal to) another state. /// This is the partial order of the semi-lattice. /// /// /// The exact meaning of this relation is up to the concrete implementation, /// but usually "less than" means "has less information than". /// A given position in the code starts at the "bottom state" (=no information) /// and then adds more information as the analysis progresses. /// After each change to the state, the old state must be less than the new state, /// so that the analysis does not run into an infinite loop. /// The partially ordered set must also have finite height (no infinite ascending chains s1 < s2 < ...), /// to ensure the analysis terminates. /// /// /// The simplest possible non-trivial state, bool isReachable, would implement LessThanOrEqual as: /// return (this.isReachable ? 1 : 0) <= (otherState.isReachable ? 1 : 0); /// Which can be simpified to: /// return !this.isReachable || otherState.isReachable; /// bool LessThanOrEqual(Self otherState); /// /// Creates a new object with a copy of the state. /// /// /// Mutating methods such as ReplaceWith or JoinWith modify the contents of a state object. /// Cloning the object allows the analysis to track multiple independent states, /// such as the /// /// /// The simple state "bool isReachable", would implement Clone as: /// return new MyState(this.isReachable); /// Self Clone(); /// /// Replace the contents of this state object with a copy of those in . /// /// /// x = x.Clone(); x.ReplaceWith(newContent); /// is equivalent to /// x = newContent.Clone(); /// /// ReplaceWith() is used to avoid allocating new state objects where possible. /// /// /// The simple state "bool isReachable", would implement ReplaceWith as: /// this.isReachable = newContent.isReachable; /// void ReplaceWith(Self newContent); /// /// Join the incomingState into this state. /// /// /// Postcondition: old(this).LessThanOrEqual(this) && incomingState.LessThanOrEqual(this) /// This method should set this to the smallest state that is greater than (or equal to) /// both input states. /// /// JoinWith() is used when multiple control flow paths are joined together. /// For example, it is used to combine the thenState with the elseState /// at the end of a if-else construct. /// /// /// The simple state "bool isReachable", would implement JoinWith as: /// this.isReachable |= incomingState.isReachable; /// void JoinWith(Self incomingState); /// /// A special operation to merge the end-state of the finally-block with the end state of /// a branch leaving the try-block. /// /// If either input state is unreachable, this call must result in an unreachable state. /// /// /// The simple state "bool isReachable", would implement TriggerFinally as: /// this.isReachable &= finallyState.isReachable; /// void TriggerFinally(Self finallyState); /// /// Gets whether this is the bottom state. /// /// The bottom state represents that the data flow analysis has not yet /// found a code path from the entry point to this state's position. /// It thus contains no information, and is "less than" all other states. /// /// /// The bottom state is the bottom element in the semi-lattice. /// /// Initially, all code blocks not yet visited by the analysis will be in the bottom state. /// Unreachable code will always remain in the bottom state. /// Some analyses may also use the bottom state for reachable code after it was processed by the analysis. /// For example, in DefiniteAssignmentVisitor the bottom states means /// "either this code is unreachable, or all variables are definitely initialized". /// /// /// The simple state "bool isReachable", would implement IsBottom as: /// return !this.isReachable; /// bool IsBottom { get; } /// /// Equivalent to this.ReplaceWith(bottomState), but may be implemented more efficiently. /// /// /// Since the DataFlowVisitor can only create states by cloning from the initial state, /// this method is necessary for the DataFlowVisitor to gain access to the bottom element in /// the first place. /// /// /// The simple state "bool isReachable", would implement ReplaceWithBottom as: /// this.isReachable = false; /// void ReplaceWithBottom(); } /// /// Generic base class for forward data flow analyses. /// /// /// The state type used for the data flow analysis. See for details. /// public abstract class DataFlowVisitor : ILVisitor where State : IDataFlowState { // The data flow analysis tracks a 'state'. // There are many states (one per source code position, i.e. ILInstruction), but we don't store all of them. // We only keep track of: // a) the current state in the RDVisitor // This state corresponds to the instruction currently being visited, // and gets mutated as we traverse the ILAst. // b) the input state for each control flow node // These also gets mutated as the analysis learns about new control flow edges. /// /// The bottom state. /// Must not be mutated. /// State bottomState; /// /// Current state. /// /// Caution: any state object assigned to this member gets mutated as the visitor traverses the ILAst! /// protected State state; /// /// Combined state of all possible exceptional control flow paths in the current try block. /// Serves as input state for catch blocks. /// /// Caution: any state object assigned to this member gets mutated as the visitor encounters instructions that may throw exceptions! /// /// Within a try block, currentStateOnException == stateOnException[tryBlock.Parent]. /// /// protected State currentStateOnException; bool initialized; /// /// Initializes the DataFlowVisitor. /// This method must be called once before any Visit()-methods can be called. /// It must not be called more than once. /// /// The initial state at the entry point of the analysis. /// /// This is a method instead of a constructor because derived classes might need complex initialization /// before they can construct the initial state. /// protected void Initialize(State initialState) { Debug.Assert(!initialized); initialized = true; this.state = initialState.Clone(); this.bottomState = initialState.Clone(); this.bottomState.ReplaceWithBottom(); Debug.Assert(bottomState.IsBottom); this.stateOnNullableRewrap = bottomState.Clone(); this.currentStateOnException = state.Clone(); } #if DEBUG // For debugging, capture the input + output state at every instruction. readonly Dictionary debugInputState = new Dictionary(); readonly Dictionary debugOutputState = new Dictionary(); void DebugPoint(Dictionary debugDict, ILInstruction inst) { #if DEBUG Debug.Assert(initialized, "Initialize() was not called"); if (debugDict.TryGetValue(inst, out State previousState)) { Debug.Assert(previousState.LessThanOrEqual(state)); previousState.JoinWith(state); } else { // limit the number of tracked instructions to make memory usage in debug builds less horrible if (debugDict.Count < 1000) { debugDict.Add(inst, state.Clone()); } } // currentStateOnException should be all states within the try block joined together // -> state should already have been joined into currentStateOnException. Debug.Assert(state.LessThanOrEqual(currentStateOnException)); #endif } #endif [Conditional("DEBUG")] protected void DebugStartPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugInputState, inst); #endif } [Conditional("DEBUG")] protected void DebugEndPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugOutputState, inst); #endif } /// /// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method. /// protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable; protected sealed override void Default(ILInstruction inst) { DebugStartPoint(inst); // This method assumes normal control flow and no branches. if ((inst.DirectFlags & flagsRequiringManualImpl) != 0) { throw new NotImplementedException(GetType().Name + " is missing implementation for " + inst.GetType().Name); } // Since this instruction has normal control flow, we can evaluate our children left-to-right. foreach (var child in inst.Children) { child.AcceptVisitor(this); Debug.Assert(state.IsBottom || !child.HasFlag(InstructionFlags.EndPointUnreachable), "Unreachable code must be in the bottom state."); } DebugEndPoint(inst); } /// /// Handle control flow when the current instruction throws an exception: /// joins the current state into the "exception state" of the current try block. /// /// /// This should not only be called for instructions that may throw an exception, /// but for all instructions (due to async exceptions like ThreadAbortException)! /// /// To avoid redundant calls, every Visit() call may assume that the current state /// is already propagated, and has to guarantee the same at the end. /// This means this method should be called after every state change. /// Alternatively, derived classes may directly modify both state /// and currentStateOnException, so that a full JoinWith() call /// is not necessary. /// protected void PropagateStateOnException() { currentStateOnException.JoinWith(state); } /// /// Replace the current state with the bottom state. /// protected void MarkUnreachable() { state.ReplaceWithBottom(); } /// /// Holds the state for incoming branches. /// /// /// Only used for blocks in block containers; not for inline blocks. /// readonly Dictionary stateOnBranch = new Dictionary(); /// /// Holds the state at the block container end-point. (=state for incoming 'leave' instructions) /// readonly Dictionary stateOnLeave = new Dictionary(); /// /// Gets the state object that holds the state for incoming branches to the block. /// /// /// Returns the a clone of the bottom state on the first call for a given block, /// then returns the same object instance on further calls. /// The caller is expected to mutate the returned state by calling JoinWith(). /// State GetBlockInputState(Block block) { if (stateOnBranch.TryGetValue(block, out State s)) { return s; } else { s = bottomState.Clone(); stateOnBranch.Add(block, s); return s; } } /// /// For each block container, stores the set of blocks (via Block.ChildIndex) /// that had their incoming state changed and were not processed yet. /// readonly Dictionary> workLists = new Dictionary>(); protected internal override void VisitBlockContainer(BlockContainer container) { DebugStartPoint(container); SortedSet worklist = new SortedSet(); // register work list so that branches within this container can add to it workLists.Add(container, worklist); var stateOnEntry = GetBlockInputState(container.EntryPoint); if (!state.LessThanOrEqual(stateOnEntry)) { // If we have new information for the container's entry point, // add the container entry point to the work list. stateOnEntry.JoinWith(state); worklist.Add(0); } // To handle loops, we need to analyze the loop body before we can know the state for the loop backedge, // but we need to know the input state for the loop body (to which the backedge state contributes) // before we can analyze the loop body. // Solution: we repeat the analysis of the loop body multiple times, until the state no longer changes. // To make it terminate reasonably quickly, we need to process the control flow nodes in the correct order: // reverse post-order. We use a SortedSet for this, and assume that the block indices used in the SortedSet // are ordered appropriately. The caller can use BlockContainer.SortBlocks() for this. while (worklist.Count > 0) { int blockIndex = worklist.Min; worklist.Remove(blockIndex); Block block = container.Blocks[blockIndex]; state.ReplaceWith(stateOnBranch[block]); block.AcceptVisitor(this); } if (stateOnLeave.TryGetValue(container, out State stateOnExit)) { state.ReplaceWith(stateOnExit); } else { MarkUnreachable(); } DebugEndPoint(container); workLists.Remove(container); } readonly List<(IBranchOrLeaveInstruction, State)> branchesTriggeringFinally = new List<(IBranchOrLeaveInstruction, State)>(); protected internal override void VisitBranch(Branch inst) { if (inst.TriggersFinallyBlock) { Debug.Assert(state.LessThanOrEqual(currentStateOnException)); branchesTriggeringFinally.Add((inst, state.Clone())); } else { MergeBranchStateIntoTargetBlock(inst, state); } MarkUnreachable(); } void MergeBranchStateIntoTargetBlock(Branch inst, State branchState) { var targetBlock = inst.TargetBlock; var targetState = GetBlockInputState(targetBlock); if (!branchState.LessThanOrEqual(targetState)) { targetState.JoinWith(branchState); BlockContainer container = (BlockContainer)targetBlock.Parent; if (workLists.TryGetValue(container, out var workList)) { workList.Add(targetBlock.ChildIndex); } else { Debug.Fail("Failed to find target BlockContainer"); } } } protected internal override void VisitLeave(Leave inst) { inst.Value.AcceptVisitor(this); if (inst.TriggersFinallyBlock) { Debug.Assert(state.LessThanOrEqual(currentStateOnException)); branchesTriggeringFinally.Add((inst, state.Clone())); } else { MergeBranchStateIntoStateOnLeave(inst, state); } MarkUnreachable(); } void MergeBranchStateIntoStateOnLeave(Leave inst, State branchState) { if (stateOnLeave.TryGetValue(inst.TargetContainer, out State targetState)) { targetState.JoinWith(branchState); } else { stateOnLeave.Add(inst.TargetContainer, branchState.Clone()); } // Note: We don't have to put the block container onto the work queue, // because it's an ancestor of the Leave instruction, and hence // we are currently somewhere within the VisitBlockContainer() call. } protected internal override void VisitThrow(Throw inst) { inst.Argument.AcceptVisitor(this); MarkUnreachable(); } protected internal override void VisitRethrow(Rethrow inst) { MarkUnreachable(); } protected internal override void VisitInvalidBranch(InvalidBranch inst) { MarkUnreachable(); } /// /// Stores the stateOnException per try instruction. /// readonly Dictionary stateOnException = new Dictionary(); /// /// Visits the TryBlock. /// /// Returns a new State object representing the exceptional control flow transfer out of the try block. /// protected State HandleTryBlock(TryInstruction inst) { State oldStateOnException = currentStateOnException; if (stateOnException.TryGetValue(inst, out State newStateOnException)) { newStateOnException.JoinWith(state); } else { newStateOnException = state.Clone(); stateOnException.Add(inst, newStateOnException); } currentStateOnException = newStateOnException; inst.TryBlock.AcceptVisitor(this); // swap back to the old object instance currentStateOnException = oldStateOnException; // No matter what kind of try-instruction this is, it's possible // that an async exception is thrown immediately in the handler block, // so propagate the state: oldStateOnException.JoinWith(newStateOnException); // Return a copy, so that the caller mutating the returned state // does not influence the 'stateOnException' dict return newStateOnException.Clone(); } protected internal override void VisitTryCatch(TryCatch inst) { DebugStartPoint(inst); State onException = HandleTryBlock(inst); State endpoint = state.Clone(); foreach (var handler in inst.Handlers) { state.ReplaceWith(onException); BeginTryCatchHandler(handler); handler.Filter.AcceptVisitor(this); // if the filter return false, any mutations done by the filter // will be visible by the remaining handlers // (but it's also possible that the filter didn't get executed at all // because the exception type doesn't match) onException.JoinWith(state); handler.Body.AcceptVisitor(this); endpoint.JoinWith(state); } state = endpoint; DebugEndPoint(inst); } protected virtual void BeginTryCatchHandler(TryCatchHandler inst) { } /// /// TryCatchHandler is handled directly in VisitTryCatch /// protected internal override sealed void VisitTryCatchHandler(TryCatchHandler inst) { throw new NotSupportedException(); } protected internal override void VisitTryFinally(TryFinally inst) { DebugStartPoint(inst); int branchesTriggeringFinallyOldCount = branchesTriggeringFinally.Count; // At first, handle 'try { .. } finally { .. }' like 'try { .. } catch {} .. if (?) rethrow; }' State onException = HandleTryBlock(inst); State onSuccess = state.Clone(); state.JoinWith(onException); inst.FinallyBlock.AcceptVisitor(this); //PropagateStateOnException(); // rethrow the exception after the finally block -- should be redundant Debug.Assert(state.LessThanOrEqual(currentStateOnException)); ProcessBranchesLeavingTryFinally(inst, branchesTriggeringFinallyOldCount); // Use TriggerFinally() to ensure points after the try-finally are reachable only if both the // try and the finally endpoints are reachable. onSuccess.TriggerFinally(state); state = onSuccess; DebugEndPoint(inst); } /// /// Process branches leaving the try-finally, /// * Calls TriggerFinally() on each branchesTriggeringFinally /// * Removes entries from branchesTriggeringFinally if they won't trigger additional finally blocks. /// * After all finallies are applied, the branch state is merged into the target block. /// void ProcessBranchesLeavingTryFinally(TryFinally tryFinally, int branchesTriggeringFinallyOldCount) { int outPos = branchesTriggeringFinallyOldCount; for (int i = branchesTriggeringFinallyOldCount; i < branchesTriggeringFinally.Count; ++i) { var (branch, stateOnBranch) = branchesTriggeringFinally[i]; Debug.Assert(((ILInstruction)branch).IsDescendantOf(tryFinally)); Debug.Assert(tryFinally.IsDescendantOf(branch.TargetContainer)); stateOnBranch.TriggerFinally(state); bool triggersAnotherFinally = Branch.GetExecutesFinallyBlock(tryFinally, branch.TargetContainer); if (triggersAnotherFinally) { branchesTriggeringFinally[outPos++] = (branch, stateOnBranch); } else { // Merge state into target block. if (branch is Leave leave) { MergeBranchStateIntoStateOnLeave((Leave)branch, stateOnBranch); } else { MergeBranchStateIntoTargetBlock((Branch)branch, stateOnBranch); } } } branchesTriggeringFinally.RemoveRange(outPos, branchesTriggeringFinally.Count - outPos); } protected internal override void VisitTryFault(TryFault inst) { DebugStartPoint(inst); // try-fault executes fault block if an exception occurs in try, // and always rethrows the exception at the end. State onException = HandleTryBlock(inst); State onSuccess = state; state = onException; inst.FaultBlock.AcceptVisitor(this); //PropagateStateOnException(); // rethrow the exception after the fault block Debug.Assert(state.LessThanOrEqual(currentStateOnException)); // try-fault exits normally only if no exception occurred state = onSuccess; DebugEndPoint(inst); } protected internal override void VisitIfInstruction(IfInstruction inst) { DebugStartPoint(inst); var (beforeThen, beforeElse) = EvaluateCondition(inst.Condition); state = beforeThen; inst.TrueInst.AcceptVisitor(this); State afterTrueState = state; state = beforeElse; inst.FalseInst.AcceptVisitor(this); state.JoinWith(afterTrueState); DebugEndPoint(inst); } /// /// Evaluates the condition of an if. /// /// /// A pair of: /// * The state after the condition evaluates to true /// * The state after the condition evaluates to false /// /// /// this.state is invalid after this function was called, and must be overwritten /// with one of the return values. /// (State OnTrue, State OnFalse) EvaluateCondition(ILInstruction inst) { if (inst is IfInstruction ifInst) { // 'if (a?b:c)' or similar. // This also includes conditions that are logic.not, logic.and, logic.or. DebugStartPoint(ifInst); var (beforeThen, beforeElse) = EvaluateCondition(ifInst.Condition); state = beforeThen; var (afterThenTrue, afterThenFalse) = EvaluateCondition(ifInst.TrueInst); state = beforeElse; var (afterElseTrue, afterElseFalse) = EvaluateCondition(ifInst.FalseInst); var onTrue = afterThenTrue; onTrue.JoinWith(afterElseTrue); var onFalse = afterThenFalse; onFalse.JoinWith(afterElseFalse); DebugEndPoint(ifInst); return (onTrue, onFalse); } else if (inst is LdcI4 constant) { if (constant.Value == 0) { return (bottomState.Clone(), state); } else { return (state, bottomState.Clone()); } } else if (inst is MatchInstruction match) { return EvaluateMatch(match); } else { // other kind of condition inst.AcceptVisitor(this); return (state, state.Clone()); } } protected internal override void VisitMatchInstruction(MatchInstruction inst) { var (onTrue, onFalse) = EvaluateMatch(inst); state = onTrue; state.JoinWith(onFalse); } /// /// Evaluates a match instruction. /// /// /// A pair of: /// * The state after the pattern matches /// * The state after the pattern fails to match /// /// /// this.state is invalid after this function was called, and must be overwritten /// with one of the return values. /// (State OnTrue, State OnFalse) EvaluateMatch(MatchInstruction inst) { DebugStartPoint(inst); inst.TestedOperand.AcceptVisitor(this); State onFalse = state.Clone(); if (!inst.CheckNotNull && !inst.CheckType) { onFalse.ReplaceWithBottom(); } HandleMatchStore(inst); foreach (var subPattern in inst.SubPatterns) { var (subTrue, subFalse) = EvaluateCondition(subPattern); onFalse.JoinWith(subFalse); state = subTrue; } DebugEndPoint(inst); return (state, onFalse); } protected abstract void HandleMatchStore(MatchInstruction inst); protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) { HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst); } protected internal override void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) { HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); } protected internal override void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) { HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); } void HandleBinaryWithOptionalEvaluation(ILInstruction parent, ILInstruction left, ILInstruction right) { DebugStartPoint(parent); left.AcceptVisitor(this); State branchState = state.Clone(); right.AcceptVisitor(this); state.JoinWith(branchState); DebugEndPoint(parent); } State stateOnNullableRewrap; protected internal override void VisitNullableRewrap(NullableRewrap inst) { DebugStartPoint(inst); var oldState = stateOnNullableRewrap.Clone(); stateOnNullableRewrap.ReplaceWithBottom(); inst.Argument.AcceptVisitor(this); // Join incoming control flow from the NullableUnwraps. state.JoinWith(stateOnNullableRewrap); stateOnNullableRewrap = oldState; DebugEndPoint(inst); } protected internal override void VisitNullableUnwrap(NullableUnwrap inst) { DebugStartPoint(inst); inst.Argument.AcceptVisitor(this); // Conditional control flow edge to the surrounding NullableRewrap. stateOnNullableRewrap.JoinWith(state); DebugEndPoint(inst); } protected internal override void VisitSwitchInstruction(SwitchInstruction inst) { DebugStartPoint(inst); inst.Value.AcceptVisitor(this); State beforeSections = state.Clone(); inst.Sections[0].AcceptVisitor(this); State afterSections = state.Clone(); for (int i = 1; i < inst.Sections.Count; ++i) { state.ReplaceWith(beforeSections); inst.Sections[i].AcceptVisitor(this); afterSections.JoinWith(state); } state = afterSections; DebugEndPoint(inst); } protected internal override void VisitYieldReturn(YieldReturn inst) { DebugStartPoint(inst); inst.Value.AcceptVisitor(this); DebugEndPoint(inst); } protected internal override void VisitUsingInstruction(UsingInstruction inst) { DebugStartPoint(inst); inst.ResourceExpression.AcceptVisitor(this); inst.Body.AcceptVisitor(this); DebugEndPoint(inst); } protected internal override void VisitLockInstruction(LockInstruction inst) { DebugStartPoint(inst); inst.OnExpression.AcceptVisitor(this); inst.Body.AcceptVisitor(this); DebugEndPoint(inst); } protected internal override void VisitILFunction(ILFunction function) { throw new NotImplementedException(); } } } ================================================ FILE: ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Threading; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// DataFlowVisitor that performs definite assignment analysis. /// class DefiniteAssignmentVisitor : DataFlowVisitor { /// /// State for definite assignment analysis. /// [DebuggerDisplay("{bits}")] public struct State : IDataFlowState { /// /// bits[i]: There is a code path from the entry point to this state's position /// that does not write to function.Variables[i]. /// (i.e. the variable is not definitely assigned at the state's position) /// /// Initial state: all bits set = nothing is definitely assigned /// Bottom state: all bits clear /// readonly BitSet bits; /// /// Creates the initial state. /// public State(int variableCount) { this.bits = new BitSet(variableCount); this.bits.Set(0, variableCount); } private State(BitSet bits) { this.bits = bits; } public bool LessThanOrEqual(State otherState) { return bits.IsSubsetOf(otherState.bits); } public State Clone() { return new State(bits.Clone()); } public void ReplaceWith(State newContent) { bits.ReplaceWith(newContent.bits); } public void JoinWith(State incomingState) { bits.UnionWith(incomingState.bits); } public void TriggerFinally(State finallyState) { // If there is no path to the end of the try-block that leaves a variable v // uninitialized, then there is no such path to the end of the whole try-finally either. // (the try-finally cannot complete successfully unless the try block does the same) // ==> any bits that are false in this.state must be false in the output state. // Or said otherwise: a variable is definitely assigned after try-finally if it is // definitely assigned in either the try or the finally block. // Given that the bits are the opposite of definite assignment, this gives us: // !outputBits[i] == !bits[i] || !finallyState.bits[i]. // and thus: // outputBits[i] == bits[i] && finallyState.bits[i]. bits.IntersectWith(finallyState.bits); } public void ReplaceWithBottom() { bits.ClearAll(); } public bool IsBottom { get { return !bits.Any(); } } public void MarkVariableInitialized(int variableIndex) { bits.Clear(variableIndex); } public bool IsPotentiallyUninitialized(int variableIndex) { return bits[variableIndex]; } } readonly CancellationToken cancellationToken; readonly ILFunction scope; readonly BitSet variablesWithUninitializedUsage; readonly Dictionary stateOfLocalFunctionUse = new Dictionary(); readonly HashSet localFunctionsNeedingAnalysis = new HashSet(); public DefiniteAssignmentVisitor(ILFunction scope, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); this.cancellationToken = cancellationToken; this.scope = scope; this.variablesWithUninitializedUsage = new BitSet(scope.Variables.Count); base.flagsRequiringManualImpl |= InstructionFlags.MayReadLocals | InstructionFlags.MayWriteLocals; Initialize(new State(scope.Variables.Count)); } public bool IsPotentiallyUsedUninitialized(ILVariable v) { Debug.Assert(v.Function == scope); return variablesWithUninitializedUsage[v.IndexInFunction]; } void HandleStore(ILVariable v) { cancellationToken.ThrowIfCancellationRequested(); if (v.Function == scope) { // Mark the variable as initialized: state.MarkVariableInitialized(v.IndexInFunction); // Note that this gets called even if the store is in unreachable code, // but that's OK because bottomState.MarkVariableInitialized() has no effect. // After the state change, we have to call // PropagateStateOnException() = currentStateOnException.JoinWith(state); // but because MarkVariableInitialized() only clears a bit, // this is guaranteed to be a no-op. } } void EnsureInitialized(ILVariable v) { if (v.Function == scope && state.IsPotentiallyUninitialized(v.IndexInFunction)) { variablesWithUninitializedUsage.Set(v.IndexInFunction); } } protected internal override void VisitStLoc(StLoc inst) { inst.Value.AcceptVisitor(this); HandleStore(inst.Variable); } protected override void HandleMatchStore(MatchInstruction inst) { HandleStore(inst.Variable); } protected override void BeginTryCatchHandler(TryCatchHandler inst) { HandleStore(inst.Variable); base.BeginTryCatchHandler(inst); } protected internal override void VisitPinnedRegion(PinnedRegion inst) { inst.Init.AcceptVisitor(this); HandleStore(inst.Variable); inst.Body.AcceptVisitor(this); } protected internal override void VisitLdLoc(LdLoc inst) { EnsureInitialized(inst.Variable); } protected internal override void VisitLdLoca(LdLoca inst) { // A variable needs to be initialized before we can take it by reference. // The exception is if the variable is passed to an out parameter (handled in VisitCall). EnsureInitialized(inst.Variable); } protected internal override void VisitCall(Call inst) { HandleCall(inst); } protected internal override void VisitCallVirt(CallVirt inst) { HandleCall(inst); } protected internal override void VisitNewObj(NewObj inst) { HandleCall(inst); } protected internal override void VisitILFunction(ILFunction inst) { DebugStartPoint(inst); State stateBeforeFunction = state.Clone(); State stateOnExceptionBeforeFunction = currentStateOnException.Clone(); // Note: lambdas are handled at their point of declaration. // We immediately visit their body, because captured variables need to be definitely initialized at this point. // We ignore the state after the lambda body (by resetting to the state before), because we don't know // when the lambda will be invoked. // This also makes this logic unsuitable for reaching definitions, as we wouldn't see the effect of stores in lambdas. // Only the simpler case of definite assignment can support lambdas. inst.Body.AcceptVisitor(this); // For local functions, the situation is similar to lambdas. // However, we don't use the state of the declaration site when visiting local functions, // but instead the state(s) of their point of use. // Because we might discover additional points of use within the local functions, // we use a fixed-point iteration. bool changed; do { changed = false; foreach (var nestedFunction in inst.LocalFunctions) { if (!localFunctionsNeedingAnalysis.Contains(nestedFunction.ReducedMethod)) continue; localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod); State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod]; this.state.ReplaceWith(stateOnEntry); this.currentStateOnException.ReplaceWith(stateOnEntry); nestedFunction.AcceptVisitor(this); changed = true; } } while (changed); currentStateOnException = stateOnExceptionBeforeFunction; state = stateBeforeFunction; DebugEndPoint(inst); } void HandleCall(CallInstruction call) { DebugStartPoint(call); bool hasOutArgs = false; foreach (var arg in call.Arguments) { if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.ReferenceKind == ReferenceKind.Out) { // Visiting ldloca would require the variable to be initialized, // but we don't need out arguments to be initialized. hasOutArgs = true; } else { arg.AcceptVisitor(this); } } // Mark out arguments as initialized, but only after the whole call: if (hasOutArgs) { foreach (var arg in call.Arguments) { if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.ReferenceKind == ReferenceKind.Out) { HandleStore(v); } } } HandleLocalFunctionUse(call.Method); DebugEndPoint(call); } /// /// For a use of a local function, remember the current state to use as stateOnEntry when /// later processing the local function body. /// void HandleLocalFunctionUse(IMethod method) { if (method.IsLocalFunction) { if (stateOfLocalFunctionUse.TryGetValue(method, out var stateOnEntry)) { if (!state.LessThanOrEqual(stateOnEntry)) { stateOnEntry.JoinWith(state); localFunctionsNeedingAnalysis.Add(method); } } else { stateOfLocalFunctionUse.Add(method, state.Clone()); localFunctionsNeedingAnalysis.Add(method); } } } protected internal override void VisitLdFtn(LdFtn inst) { DebugStartPoint(inst); HandleLocalFunctionUse(inst.Method); DebugEndPoint(inst); } } } ================================================ FILE: ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// Description of Dominance. /// public static class Dominance { /// /// Computes the dominator tree. /// /// /// Precondition: the dominance tree is not already computed for some nodes reachable from entryPoint /// (i.e. ImmediateDominator and DominatorTreeChildren are both null), /// and the visited flag is false for any nodes reachable from entryPoint. /// /// Postcondition: a dominator tree is constructed for all nodes reachable from entryPoint, /// and the visited flag remains false. /// public static void ComputeDominance(ControlFlowNode entryPoint, CancellationToken cancellationToken = default(CancellationToken)) { // A Simple, Fast Dominance Algorithm // Keith D. Cooper, Timothy J. Harvey and Ken Kennedy var nodes = new List(); entryPoint.TraversePostOrder(n => n.Successors, nodes.Add); Debug.Assert(nodes.Last() == entryPoint); for (int i = 0; i < nodes.Count; i++) { nodes[i].PostOrderNumber = i; } // For the purpose of this algorithm, make the entry point its own dominator. // We'll reset it back to null at the end of this function. entryPoint.ImmediateDominator = entryPoint; bool changed; do { changed = false; cancellationToken.ThrowIfCancellationRequested(); // For all nodes b except the entry point (in reverse post-order) for (int i = nodes.Count - 2; i >= 0; i--) { ControlFlowNode b = nodes[i]; // Compute new immediate dominator: ControlFlowNode newIdom = null; foreach (var p in b.Predecessors) { // Ignore predecessors that were not processed yet if (p.ImmediateDominator != null) { if (newIdom == null) newIdom = p; else newIdom = FindCommonDominator(p, newIdom); } } // The reverse post-order ensures at least one of our predecessors was processed. Debug.Assert(newIdom != null); if (newIdom != b.ImmediateDominator) { b.ImmediateDominator = newIdom; changed = true; } } } while (changed); // Create dominator tree for all reachable nodes: foreach (ControlFlowNode node in nodes) { if (node.ImmediateDominator != null) node.DominatorTreeChildren = new List(); } entryPoint.ImmediateDominator = null; foreach (ControlFlowNode node in nodes) { // Create list of children in dominator tree if (node.ImmediateDominator != null) node.ImmediateDominator.DominatorTreeChildren.Add(node); // Also reset the visited flag node.Visited = false; } } /// /// Returns the common ancestor of a and b in the dominator tree. /// /// Precondition: a and b are part of the same dominator tree. /// public static ControlFlowNode FindCommonDominator(ControlFlowNode a, ControlFlowNode b) { while (a != b) { while (a.PostOrderNumber < b.PostOrderNumber) a = a.ImmediateDominator; while (b.PostOrderNumber < a.PostOrderNumber) b = b.ImmediateDominator; } return a; } /// /// Computes a BitSet where /// result[i] == true iff cfg[i] is reachable and there is some node that is /// reachable from cfg[i] but not dominated by cfg[i]. /// /// This is similar to "does cfg[i] have a non-empty dominance frontier?", /// except that it uses non-strict dominance where the definition of dominance frontiers /// uses "strictly dominates". /// /// Precondition: /// Dominance was computed for cfg and cfg[i].UserIndex == i for all i. /// public static BitSet MarkNodesWithReachableExits(ControlFlowNode[] cfg) { #if DEBUG for (int i = 0; i < cfg.Length; i++) { Debug.Assert(cfg[i].UserIndex == i); } #endif BitSet nonEmpty = new BitSet(cfg.Length); foreach (var j in cfg) { // If j is a join-point (more than one incoming edge): // `j.IsReachable && j.ImmediateDominator == null` is the root node, which counts as an extra incoming edge if (j.IsReachable && (j.Predecessors.Count >= 2 || (j.Predecessors.Count >= 1 && j.ImmediateDominator == null))) { // Add j to frontier of all predecessors and their dominators up to j's immediate dominator. foreach (var p in j.Predecessors) { for (var runner = p; runner != j.ImmediateDominator && runner != j && runner != null; runner = runner.ImmediateDominator) { nonEmpty.Set(runner.UserIndex); } } } } return nonEmpty; } } } ================================================ FILE: ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Threading; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// Implements the "reaching definitions" analysis. /// /// https://en.wikipedia.org/wiki/Reaching_definition /// /// By "definitions", we mean stores to local variables. /// /// /// Possible "definitions" that store to a variable are: /// * StLoc /// * TryCatchHandler (for the exception variable) /// * ReachingDefinitionsVisitor.UninitializedVariable for uninitialized variables. /// Note that we do not keep track of LdLoca/references/pointers. /// The analysis will likely be wrong/incomplete for variables with AddressCount != 0. /// /// Note: this class does not store the computed information, because doing so /// would significantly increase the number of states we need to store. /// The only way to get the computed information out of this class is to /// derive from the class and override the Visit methods at the points of interest /// (usually the load instructions). /// class ReachingDefinitionsVisitor : DataFlowVisitor { #region State representation /// /// The state during the reaching definitions analysis. /// /// /// A state can either be reachable, or unreachable: /// 1) unreachable /// Note that during the analysis, "unreachable" just means we have not yet found a path /// from the entry point to the node. States transition from unreachable to reachable as /// the analysis processes more control flow paths. /// 2) reachable /// In this case, the state contains, for each variable, the set of stores that might have /// written to the variable before the control flow reached the state's source code position. /// This set does not include stores that were definitely overwritten by other stores to the /// same variable. /// During the analysis, the set of stores gets extended as the analysis processes more code paths. /// /// The reachable state could be represented as a `Dictionary{ILVariable, ISet{ILInstruction}}`. /// To consume less memory, we instead assign an integer index to all stores in the analyzed function ("store index"), /// and store the state as a `BitSet` instead. /// Each bit in the set corresponds to one store instruction, and is `true` iff the store is a reaching definition /// for the variable it is storing to. /// The `allStores` array has the same length as the bit sets and holds the corresponding `ILInstruction` objects (store instructions). /// All stores for a single variable occupy a contiguous segment of the `allStores` array (and thus also of the `state`), /// which allows us to efficient clear out all stores that get overwritten by a new store. /// [DebuggerDisplay("{bits}")] public struct State : IDataFlowState { /// /// This bitset contains three different kinds of bits: /// Reachable bit: (bit 0) /// This state's position is reachable from the entry point. /// /// Reaching uninitialized variable bit: (bit si, where si > 0 and allStores[si] == null) /// There is a code path from the scope's entry point to this state's position /// that does not pass through any store to the variable. /// /// firstStoreIndexForVariable[v.IndexInScope] gives the index of that variable's uninitialized bit. /// /// Reaching store bit (bit si, where allStores[si] != null): /// There is a code path from the entry point to this state's position /// that passes through through allStores[si] and does not pass through another /// store to allStores[si].Variable. /// /// The indices for a variable's reaching store bits are between firstStoreIndexForVariable[v.IndexInScope] /// to firstStoreIndexForVariable[v.IndexInScope + 1] (both endpoints exclusive!). /// /// /// The initial state has the "reachable bit" and the "reaching uninitialized variable bits" set, /// and the "reaching store bits" unset. /// /// The bottom state has all bits unset. /// readonly BitSet bits; public State(BitSet bits) { this.bits = bits; } public bool LessThanOrEqual(State otherState) { return bits.IsSubsetOf(otherState.bits); } public State Clone() { return new State(bits.Clone()); } public void ReplaceWith(State newContent) { bits.ReplaceWith(newContent.bits); } public void JoinWith(State incomingState) { // When control flow is joined together, we can simply union our bitsets. // (a store is reachable iff it is reachable through either incoming path) bits.UnionWith(incomingState.bits); } public void TriggerFinally(State finallyState) { // Some cases to consider: // try { v = 1; } finally { v = 2; } // => only the store 2 is visible after the try-finally // v = 1; try { v = 2; } finally { } // => both stores are visible after the try-finally // In general, we're looking for the post-state of the finally-block // assume the finally-block was entered without throwing an exception. // But we don't have that information (it would require analyzing the finally block twice), // so the next best thing is to approximate it by just keeping the state after the finally // (i.e. doing nothing at all). // However, the DataFlowVisitor requires us to return bottom if the end-state of the // try-block was unreachable, so let's so at least that. // (note that in principle we could just AND the reachable and uninitialized bits, // but we don't have a good solution for the normal store bits) if (IsReachable) { ReplaceWith(finallyState); } } public bool IsBottom { get { return !bits[ReachableBit]; } } public void ReplaceWithBottom() { // We need to clear all bits, not just ReachableBit, so that // the bottom state behaves as expected in joins. bits.ClearAll(); } public bool IsReachable { get { return bits[ReachableBit]; } } /// /// Clears all store bits between startStoreIndex (incl.) and endStoreIndex (excl.) /// public void KillStores(int startStoreIndex, int endStoreIndex) { Debug.Assert(startStoreIndex >= FirstStoreIndex); Debug.Assert(endStoreIndex >= startStoreIndex); bits.Clear(startStoreIndex, endStoreIndex); } public bool IsReachingStore(int storeIndex) { return bits[storeIndex]; } public int NextReachingStore(int startIndex, int endIndex) { return bits.NextSetBit(startIndex, endIndex); } public void SetStore(int storeIndex) { Debug.Assert(storeIndex >= FirstStoreIndex); bits.Set(storeIndex); } } /// /// To distinguish unreachable from reachable states, we use the first bit in the bitset to store the 'reachable bit'. /// If this bit is set, the state is reachable, and the remaining bits /// const int ReachableBit = 0; /// /// Because bit number 0 is the ReachableBit, we start counting store indices at 1. /// const int FirstStoreIndex = 1; #endregion #region Documentation + member fields protected readonly CancellationToken cancellationToken; /// /// The function being analyzed. /// protected readonly ILFunction scope; /// /// All stores for all variables in the scope. /// /// state[storeIndex] is true iff allStores[storeIndex] is a reaching definition. /// Invariant: state.bits.Length == allStores.Length. /// readonly ILInstruction[] allStores; /// /// Maps instructions appearing in allStores to their index. /// /// Invariant: allStores[storeIndexMap[inst]] == inst /// /// Does not contain UninitializedVariable (as that special instruction has multiple store indices, one per variable) /// readonly Dictionary storeIndexMap = new Dictionary(); /// /// For all variables v: allStores[firstStoreIndexForVariable[v.IndexInScope]] is the UninitializedVariable entry for v. /// The next few stores (up to firstStoreIndexForVariable[v.IndexInScope + 1], exclusive) are the full list of stores for v. /// /// /// Invariant: firstStoreIndexForVariable[scope.Variables.Count] == allStores.Length /// readonly int[] firstStoreIndexForVariable; /// /// analyzedVariables[v.IndexInScope] is true iff RD analysis is enabled for the variable. /// readonly BitSet analyzedVariables; #endregion #region Constructor /// /// Prepare reaching definitions analysis for the specified variable scope. /// /// The analysis will track all variables in the scope for which the predicate returns true /// ("analyzed variables"). /// public ReachingDefinitionsVisitor(ILFunction scope, Predicate pred, CancellationToken cancellationToken) : this(scope, GetActiveVariableBitSet(scope, pred), cancellationToken) { this.cancellationToken = cancellationToken; } static BitSet GetActiveVariableBitSet(ILFunction scope, Predicate pred) { if (scope == null) throw new ArgumentNullException(nameof(scope)); BitSet activeVariables = new BitSet(scope.Variables.Count); for (int vi = 0; vi < scope.Variables.Count; vi++) { activeVariables[vi] = pred(scope.Variables[vi]); } return activeVariables; } /// /// Prepare reaching definitions analysis for the specified variable scope. /// /// The analysis will track all variables in the scope for which analyzedVariables[v.IndexInScope] is true. /// public ReachingDefinitionsVisitor(ILFunction scope, BitSet analyzedVariables, CancellationToken cancellationToken) { if (scope == null) throw new ArgumentNullException(nameof(scope)); if (analyzedVariables == null) throw new ArgumentNullException(nameof(analyzedVariables)); this.scope = scope; this.analyzedVariables = analyzedVariables; base.flagsRequiringManualImpl |= InstructionFlags.MayWriteLocals; // Fill `allStores` and `storeIndexMap` and `firstStoreIndexForVariable`. var storesByVar = FindAllStoresByVariable(scope, analyzedVariables, cancellationToken); allStores = new ILInstruction[FirstStoreIndex + storesByVar.Sum(l => l != null ? l.Count : 0)]; firstStoreIndexForVariable = new int[scope.Variables.Count + 1]; int si = FirstStoreIndex; for (int vi = 0; vi < storesByVar.Length; vi++) { cancellationToken.ThrowIfCancellationRequested(); firstStoreIndexForVariable[vi] = si; var stores = storesByVar[vi]; if (stores != null) { int expectedStoreCount = scope.Variables[vi].StoreInstructions.Count; // Extra store for the uninitialized state. expectedStoreCount += 1; Debug.Assert(stores.Count == expectedStoreCount); stores.CopyTo(allStores, si); // Add all stores except for the first (representing the uninitialized state) // to storeIndexMap. for (int i = 1; i < stores.Count; i++) { storeIndexMap.Add(stores[i], si + i); } si += stores.Count; } } firstStoreIndexForVariable[scope.Variables.Count] = si; Debug.Assert(si == allStores.Length); Initialize(CreateInitialState()); } /// /// Fill allStores and storeIndexMap. /// static List[] FindAllStoresByVariable(ILFunction scope, BitSet activeVariables, CancellationToken cancellationToken) { // For each variable, find the list of ILInstructions storing to that variable List[] storesByVar = new List[scope.Variables.Count]; for (int vi = 0; vi < storesByVar.Length; vi++) { if (activeVariables[vi]) storesByVar[vi] = new List { null }; } foreach (var inst in scope.Descendants) { if (inst.HasDirectFlag(InstructionFlags.MayWriteLocals)) { cancellationToken.ThrowIfCancellationRequested(); ILVariable v = ((IInstructionWithVariableOperand)inst).Variable; if (v.Function == scope && activeVariables[v.IndexInFunction]) { storesByVar[v.IndexInFunction].Add(inst); } } } return storesByVar; } /// /// Create the initial state (reachable bit + uninit variable bits set, store bits unset). /// State CreateInitialState() { BitSet initialState = new BitSet(allStores.Length); initialState.Set(ReachableBit); for (int vi = 0; vi < scope.Variables.Count; vi++) { if (analyzedVariables[vi]) { Debug.Assert(allStores[firstStoreIndexForVariable[vi]] == null); initialState.Set(firstStoreIndexForVariable[vi]); } } return new State(initialState); } #endregion #region Analysis void HandleStore(ILInstruction inst, ILVariable v) { cancellationToken.ThrowIfCancellationRequested(); if (v.Function == scope && analyzedVariables[v.IndexInFunction] && state.IsReachable) { // Clear the set of stores for this variable: state.KillStores(firstStoreIndexForVariable[v.IndexInFunction], firstStoreIndexForVariable[v.IndexInFunction + 1]); // And replace it with this store: int si = storeIndexMap[inst]; state.SetStore(si); // We should call PropagateStateOnException() here because we changed the state. // But that's equal to: currentStateOnException.UnionWith(state); // Because we're already guaranteed that state.LessThanOrEqual(currentStateOnException) // when entering HandleStore(), all we really need to do to achieve what PropagateStateOnException() does // is to add the single additional store to the exceptional state as well: currentStateOnException.SetStore(si); } } protected internal override void VisitStLoc(StLoc inst) { inst.Value.AcceptVisitor(this); HandleStore(inst, inst.Variable); } protected override void HandleMatchStore(MatchInstruction inst) { HandleStore(inst, inst.Variable); } protected override void BeginTryCatchHandler(TryCatchHandler inst) { base.BeginTryCatchHandler(inst); HandleStore(inst, inst.Variable); } protected internal override void VisitPinnedRegion(PinnedRegion inst) { inst.Init.AcceptVisitor(this); HandleStore(inst, inst.Variable); inst.Body.AcceptVisitor(this); } public bool IsAnalyzedVariable(ILVariable v) { return v.Function == scope && analyzedVariables[v.IndexInFunction]; } /// /// Gets all stores to v that reach the specified state. /// /// Precondition: v is an analyzed variable. /// protected IEnumerable GetStores(State state, ILVariable v) { Debug.Assert(v.Function == scope && analyzedVariables[v.IndexInFunction]); int startIndex = firstStoreIndexForVariable[v.IndexInFunction] + 1; int endIndex = firstStoreIndexForVariable[v.IndexInFunction + 1]; while (startIndex < endIndex) { int nextReachingStore = state.NextReachingStore(startIndex, endIndex); if (nextReachingStore == -1) { break; } Debug.Assert(((IInstructionWithVariableOperand)allStores[nextReachingStore]).Variable == v); yield return allStores[nextReachingStore]; startIndex = nextReachingStore + 1; } } /// /// Gets whether v is potentially uninitialized in the specified state. /// /// Precondition: v is an analyzed variable. /// protected bool IsPotentiallyUninitialized(State state, ILVariable v) { Debug.Assert(v.Function == scope && analyzedVariables[v.IndexInFunction]); return state.IsReachingStore(firstStoreIndexForVariable[v.IndexInFunction]); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Humanizer/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Scott Kirkland 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. ================================================ FILE: ICSharpCode.Decompiler/Humanizer/StringHumanizeExtensions.cs ================================================ namespace Humanizer.Inflections; using CharSpan = System.ReadOnlySpan; /// /// Contains extension methods for humanizing string values. /// internal static class StringHumanizeExtensions { internal static unsafe string Concat(CharSpan left, CharSpan right) { var result = new string('\0', left.Length + right.Length); fixed (char* pResult = result) { left.CopyTo(new(pResult, left.Length)); right.CopyTo(new(pResult + left.Length, right.Length)); } return result; } internal static unsafe string Concat(char left, CharSpan right) => Concat(new CharSpan(&left, 1), right); } ================================================ FILE: ICSharpCode.Decompiler/Humanizer/Vocabularies.cs ================================================ using System; using System.Threading; namespace Humanizer.Inflections; /// /// Container for registered Vocabularies. At present, only a single vocabulary is supported: Default. /// internal static class Vocabularies { static readonly Lazy Instance = new(BuildDefault, LazyThreadSafetyMode.PublicationOnly); /// /// The default vocabulary used for singular/plural irregularities. /// Rules can be added to this vocabulary and will be picked up by called to Singularize() and Pluralize(). /// At this time, multiple vocabularies and removing existing rules are not supported. /// public static Vocabulary Default => Instance.Value; static Vocabulary BuildDefault() { var _default = new Vocabulary(); _default.AddPlural("$", "s"); _default.AddPlural("s$", "s"); _default.AddPlural("(ax|test)is$", "$1es"); _default.AddPlural("(octop|vir|alumn|fung|cact|foc|hippopotam|radi|stimul|syllab|nucle)us$", "$1i"); _default.AddPlural("(alias|bias|iris|status|campus|apparatus|virus|walrus|trellis)$", "$1es"); _default.AddPlural("(buffal|tomat|volcan|ech|embarg|her|mosquit|potat|torped|vet)o$", "$1oes"); _default.AddPlural("([dti])um$", "$1a"); _default.AddPlural("sis$", "ses"); _default.AddPlural("(?:([^f])fe|([lr])f)$", "$1$2ves"); _default.AddPlural("(hive)$", "$1s"); _default.AddPlural("([^aeiouy]|qu)y$", "$1ies"); _default.AddPlural("(x|ch|ss|sh)$", "$1es"); _default.AddPlural("(matr|vert|ind|d)(ix|ex)$", "$1ices"); _default.AddPlural("(^[m|l])ouse$", "$1ice"); _default.AddPlural("^(ox)$", "$1en"); _default.AddPlural("(quiz)$", "$1zes"); _default.AddPlural("(buz|blit|walt)z$", "$1zes"); _default.AddPlural("(hoo|lea|loa|thie)f$", "$1ves"); _default.AddPlural("(alumn|alg|larv|vertebr)a$", "$1ae"); _default.AddPlural("(criteri|phenomen)on$", "$1a"); _default.AddSingular("s$", ""); _default.AddSingular("(n)ews$", "$1ews"); _default.AddSingular("([dti])a$", "$1um"); _default.AddSingular("(analy|ba|diagno|parenthe|progno|synop|the|ellip|empha|neuro|oa|paraly)ses$", "$1sis"); _default.AddSingular("([^f])ves$", "$1fe"); _default.AddSingular("(hive)s$", "$1"); _default.AddSingular("(tive)s$", "$1"); _default.AddSingular("([lr]|hoo|lea|loa|thie)ves$", "$1f"); _default.AddSingular("(^zomb)?([^aeiouy]|qu)ies$", "$2y"); _default.AddSingular("(s)eries$", "$1eries"); _default.AddSingular("(m)ovies$", "$1ovie"); _default.AddSingular("(x|ch|ss|sh)es$", "$1"); _default.AddSingular("(^[m|l])ice$", "$1ouse"); _default.AddSingular("(? /// A container for exceptions to simple pluralization/singularization rules. /// Vocabularies.Default contains an extensive list of rules for US English. /// At this time, multiple vocabularies and removing existing rules are not supported. /// internal class Vocabulary { internal Vocabulary() { } readonly List plurals = []; readonly List singulars = []; readonly HashSet uncountables = new(StringComparer.CurrentCultureIgnoreCase); readonly Regex letterS = new("^([sS])[sS]*$"); /// /// Adds a word to the vocabulary which cannot easily be pluralized/singularized by RegEx, e.g. "person" and "people". /// /// The singular form of the irregular word, e.g. "person". /// The plural form of the irregular word, e.g. "people". /// True to match these words on their own as well as at the end of longer words. False, otherwise. public void AddIrregular(string singular, string plural, bool matchEnding = true) { if (matchEnding) { var singularSubstring = singular.Substring(1); var pluralSubString = plural.Substring(1); AddPlural($"({singular[0]}){singularSubstring}$", $"$1{pluralSubString}"); AddSingular($"({plural[0]}){pluralSubString}$", $"$1{singularSubstring}"); } else { AddPlural($"^{singular}$", plural); AddSingular($"^{plural}$", singular); } } /// /// Adds an uncountable word to the vocabulary, e.g. "fish". Will be ignored when plurality is changed. /// /// Word to be added to the list of uncountables. public void AddUncountable(string word) => uncountables.Add(word); /// /// Adds a rule to the vocabulary that does not follow trivial rules for pluralization, e.g. "bus" -> "buses" /// /// RegEx to be matched, case insensitive, e.g. "(bus)es$" /// RegEx replacement e.g. "$1" public void AddPlural(string rule, string replacement) => plurals.Add(new(rule, replacement)); /// /// Adds a rule to the vocabulary that does not follow trivial rules for singularization, e.g. "vertices/indices -> "vertex/index" /// /// RegEx to be matched, case insensitive, e.g. ""(vert|ind)ices$"" /// RegEx replacement e.g. "$1ex" public void AddSingular(string rule, string replacement) => singulars.Add(new(rule, replacement)); /// /// Pluralizes the provided input considering irregular words /// /// Word to be pluralized /// Normally you call Pluralize on singular words; but if you're unsure call it with false [return: NotNullIfNotNull(nameof(word))] public string? Pluralize(string? word, bool inputIsKnownToBeSingular = true) { if (word == null) { return null; } var s = LetterS(word); if (s != null) { return s + "s"; } var result = ApplyRules(plurals, word, false); if (inputIsKnownToBeSingular) { return result ?? word; } var asSingular = ApplyRules(singulars, word, false); var asSingularAsPlural = ApplyRules(plurals, asSingular, false); if (asSingular != null && asSingular != word && asSingular + "s" != word && asSingularAsPlural == word && result != word) { return word; } return result!; } /// /// Singularizes the provided input considering irregular words /// /// Word to be singularized /// Normally you call Singularize on plural words; but if you're unsure call it with false /// Skip singularizing single words that have an 's' on the end [return: NotNullIfNotNull(nameof(word))] public string? Singularize(string? word, bool inputIsKnownToBePlural = true, bool skipSimpleWords = false) { if (word == null) { return null; } var s = LetterS(word); if (s != null) { return s; } var result = ApplyRules(singulars, word, skipSimpleWords); if (inputIsKnownToBePlural) { return result ?? word; } // the Plurality is unknown so we should check all possibilities var asPlural = ApplyRules(plurals, word, false); if (asPlural == word || word + "s" == asPlural) { return result ?? word; } var asPluralAsSingular = ApplyRules(singulars, asPlural, false); if (asPluralAsSingular != word || result == word) { return result ?? word; } return word; } string? ApplyRules(IList rules, string? word, bool skipFirstRule) { if (word == null) { return null; } if (word.Length < 1) { return word; } if (IsUncountable(word)) { return word; } var result = word; var end = skipFirstRule ? 1 : 0; for (var i = rules.Count - 1; i >= end; i--) { if ((result = rules[i].Apply(word)) != null) { break; } } if (result == null) { return null; } return MatchUpperCase(word, result); } bool IsUncountable(string word) => uncountables.Contains(word); static string MatchUpperCase(string word, string replacement) => char.IsUpper(word[0]) && char.IsLower(replacement[0]) ? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1)) : replacement; /// /// If the word is the letter s, singular or plural, return the letter s singular /// string? LetterS(string word) { var s = letterS.Match(word); return s.Groups.Count > 1 ? s.Groups[1].Value : null; } class Rule(string pattern, string replacement) { readonly Regex regex = new(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); public string? Apply(string word) { if (!regex.IsMatch(word)) { return null; } return regex.Replace(word, replacement); } } } ================================================ FILE: ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj ================================================ netstandard2.0 ICSharpCode.Decompiler 8.0.0.0-noversion ILSpy Decompiler Engine ILSpy Contributors MIT https://github.com/icsharpcode/ILSpy/ ICSharpCode.Decompiler is the decompiler engine used in ILSpy. PackageReadme.md ic#code ILSpy git https://github.com/icsharpcode/ILSpy.git DecompilerNuGetPackageIcon.png false Copyright 2011-$([System.DateTime]::Now.Year) AlphaSierraPapa C# Decompiler ILSpy true en-US False False False false 13 true True ICSharpCode.Decompiler.snk $(NoWarn);1701;1702;1591;1573 nullable embedded true true true true true true True $(DefineConstants);STEP true ILSpyUpdateAssemblyInfo; $(GetPackageVersionDependsOn) false all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers True True Instructions.tt True True ILOpCodes.tt TextTemplatingFileGenerator ILOpCodes.cs TextTemplatingFileGenerator Instructions.cs CSharp\Syntax\PatternMatching\Pattern Matching.html powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) git rev-parse HEAD^^{commit} pwsh -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) git rev-parse HEAD^{commit} ================================================ FILE: ICSharpCode.Decompiler/IL/BlockBuilder.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { /// /// Converts the list of basic blocks from ILReader into a BlockContainer structure. /// This involves creating nested block containers for exception handlers, and creating /// branches between the blocks. /// class BlockBuilder { readonly MethodBodyBlock body; readonly Dictionary variableByExceptionHandler; readonly ICompilation compilation; /// /// Gets/Sets whether to create extended basic blocks instead of basic blocks. /// The default is false. /// public bool CreateExtendedBlocks; internal BlockBuilder(MethodBodyBlock body, Dictionary variableByExceptionHandler, ICompilation compilation) { Debug.Assert(body != null); Debug.Assert(variableByExceptionHandler != null); Debug.Assert(compilation != null); this.body = body; this.variableByExceptionHandler = variableByExceptionHandler; this.compilation = compilation; } List tryInstructionList = new List(); readonly Dictionary handlerContainers = new Dictionary(); void CreateContainerStructure() { List tryCatchList = new List(); foreach (var eh in body.ExceptionRegions) { var tryRange = new Interval(eh.TryOffset, eh.TryOffset + eh.TryLength); var handlerBlock = new BlockContainer(); handlerBlock.AddILRange(new Interval(eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength)); handlerContainers.Add(handlerBlock.StartILOffset, handlerBlock); if (eh.Kind == ExceptionRegionKind.Fault || eh.Kind == ExceptionRegionKind.Finally) { var tryBlock = new BlockContainer(); tryBlock.AddILRange(tryRange); if (eh.Kind == ExceptionRegionKind.Finally) tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock).WithILRange(tryRange)); else tryInstructionList.Add(new TryFault(tryBlock, handlerBlock).WithILRange(tryRange)); continue; } // var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRanges.SingleOrDefault() == tryRange); if (tryCatch == null) { var tryBlock = new BlockContainer(); tryBlock.AddILRange(tryRange); tryCatch = new TryCatch(tryBlock); tryCatch.AddILRange(tryRange); tryCatchList.Add(tryCatch); tryInstructionList.Add(tryCatch); } ILInstruction filter; if (eh.Kind == System.Reflection.Metadata.ExceptionRegionKind.Filter) { var filterBlock = new BlockContainer(expectedResultType: StackType.I4); filterBlock.AddILRange(new Interval(eh.FilterOffset, eh.HandlerOffset)); handlerContainers.Add(filterBlock.StartILOffset, filterBlock); filter = filterBlock; } else { filter = new LdcI4(1); } var handler = new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh]); handler.AddILRange(filter); handler.AddILRange(handlerBlock); tryCatch.Handlers.Add(handler); tryCatch.AddILRange(handler); } if (tryInstructionList.Count > 0) { tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.StartILOffset).ThenByDescending(tc => tc.TryBlock.EndILOffset).ToList(); nextTry = tryInstructionList[0]; } } int currentTryIndex; TryInstruction? nextTry; BlockContainer? currentContainer; readonly Stack containerStack = new Stack(); public void CreateBlocks(BlockContainer mainContainer, IEnumerable basicBlocks, CancellationToken cancellationToken) { CreateContainerStructure(); mainContainer.SetILRange(new Interval(0, body.GetCodeSize())); currentContainer = mainContainer; foreach (var block in basicBlocks.OrderBy(b => b.StartILOffset)) { cancellationToken.ThrowIfCancellationRequested(); int start = block.StartILOffset; // Leave nested containers if necessary while (start >= currentContainer.EndILOffset && containerStack.Count > 0) { currentContainer = containerStack.Pop(); } // Enter a handler if necessary if (handlerContainers.TryGetValue(start, out BlockContainer? handlerContainer)) { containerStack.Push(currentContainer); currentContainer = handlerContainer; } // Enter a try block if necessary while (nextTry != null && start == nextTry.TryBlock.StartILOffset) { var blockForTry = new Block(); blockForTry.SetILRange(nextTry); blockForTry.Instructions.Add(nextTry); currentContainer.Blocks.Add(blockForTry); containerStack.Push(currentContainer); currentContainer = (BlockContainer)nextTry.TryBlock; nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex); } currentContainer.Blocks.Add(block); } Debug.Assert(currentTryIndex == tryInstructionList.Count && nextTry == null); ConnectBranches(mainContainer, cancellationToken); CreateOnErrorDispatchers(); } void ConnectBranches(ILInstruction inst, CancellationToken cancellationToken) { switch (inst) { case Branch branch: cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(branch.TargetBlock == null); var targetBlock = FindBranchTarget(branch.TargetILOffset); if (targetBlock == null) { branch.ReplaceWith(new InvalidBranch("Could not find block for branch target " + Disassembler.DisassemblerHelpers.OffsetToString(branch.TargetILOffset)).WithILRange(branch)); } else { branch.TargetBlock = targetBlock; } break; case Leave leave: // ret (in void method) = leave(mainContainer) // endfinally = leave(null) if (leave.TargetContainer == null) { // assign the finally/filter container leave.TargetContainer = containerStack.Peek(); leave.Value = ILReader.Cast(leave.Value, leave.TargetContainer.ExpectedResultType, null, leave.StartILOffset); } break; case BlockContainer container: containerStack.Push(container); // Note: FindBranchTarget()/CreateBranchTargetForOnErrorJump() may append to container.Blocks while we are iterating. // Don't process those artificial blocks here. int blockCount = container.Blocks.Count; for (int i = 0; i < blockCount; i++) { cancellationToken.ThrowIfCancellationRequested(); var block = container.Blocks[i]; ConnectBranches(block, cancellationToken); if (block.Instructions.Count == 0 || !block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable)) { block.Instructions.Add(new InvalidBranch("Unexpected end of block")); } } containerStack.Pop(); break; default: foreach (var child in inst.Children) ConnectBranches(child, cancellationToken); break; } } Block? FindBranchTarget(int targetILOffset) { foreach (var container in containerStack) { foreach (var block in container.Blocks) { if (block.StartILOffset == targetILOffset && !block.ILRangeIsEmpty) return block; } if (container.SlotInfo == TryCatchHandler.BodySlot) { // catch handler is allowed to branch back into try block (VB On Error) TryCatch tryCatch = (TryCatch)container.Parent!.Parent!; if (tryCatch.TryBlock.StartILOffset < targetILOffset && targetILOffset < tryCatch.TryBlock.EndILOffset) { return CreateBranchTargetForOnErrorJump(tryCatch, targetILOffset); } } } return null; } readonly Dictionary onErrorDispatchers = new Dictionary(); class OnErrorDispatch { public readonly ILVariable Variable; public readonly HashSet TargetILOffsets = new HashSet(); public readonly List Branches = new List(); public OnErrorDispatch(ILVariable variable) { Debug.Assert(variable != null); this.Variable = variable; } } /// Create a new block that sets a helper variable and then branches to the start of the try-catch Block CreateBranchTargetForOnErrorJump(TryCatch tryCatch, int targetILOffset) { if (!onErrorDispatchers.TryGetValue(tryCatch, out var dispatch)) { var int32 = compilation.FindType(KnownTypeCode.Int32); var newDispatchVar = new ILVariable(VariableKind.Local, int32, StackType.I4); newDispatchVar.Name = $"try{tryCatch.StartILOffset:x4}_dispatch"; dispatch = new OnErrorDispatch(newDispatchVar); onErrorDispatchers.Add(tryCatch, dispatch); } dispatch.TargetILOffsets.Add(targetILOffset); Block block = new Block(); block.Instructions.Add(new StLoc(dispatch.Variable, new LdcI4(targetILOffset))); var branch = new Branch(tryCatch.TryBlock.StartILOffset); block.Instructions.Add(branch); dispatch.Branches.Add(branch); containerStack.Peek().Blocks.Add(block); return block; } /// New variables introduced for the "on error" dispatchers public IEnumerable OnErrorDispatcherVariables => onErrorDispatchers.Values.Select(d => d.Variable); void CreateOnErrorDispatchers() { foreach (var (tryCatch, dispatch) in onErrorDispatchers) { Block block = (Block)tryCatch.Parent!; // Before the regular entry point of the try-catch, insert an. instruction that resets the dispatcher variable block.Instructions.Insert(tryCatch.ChildIndex, new StLoc(dispatch.Variable, new LdcI4(-1))); // Split the block, so that we can introduce branches that jump directly into the try block int splitAt = tryCatch.ChildIndex; Block newBlock = new Block(); newBlock.AddILRange(tryCatch); newBlock.Instructions.AddRange(block.Instructions.Skip(splitAt)); block.Instructions.RemoveRange(splitAt, block.Instructions.Count - splitAt); block.Instructions.Add(new Branch(newBlock)); ((BlockContainer)block.Parent!).Blocks.Add(newBlock); // Update the branches that jump directly into the try block foreach (var b in dispatch.Branches) { b.TargetBlock = newBlock; } // Inside the try-catch, create the dispatch switch BlockContainer tryBody = (BlockContainer)tryCatch.TryBlock; Block dispatchBlock = new Block(); dispatchBlock.AddILRange(new Interval(tryCatch.StartILOffset, tryCatch.StartILOffset + 1)); var switchInst = new SwitchInstruction(new LdLoc(dispatch.Variable)); switchInst.AddILRange(new Interval(tryCatch.StartILOffset, tryCatch.StartILOffset + 1)); foreach (int offset in dispatch.TargetILOffsets) { var targetBlock = tryBody.Blocks.FirstOrDefault(b => b.StartILOffset == offset && !b.ILRangeIsEmpty); ILInstruction branchInst; if (targetBlock == null) { branchInst = new InvalidBranch("Could not find block for branch target " + Disassembler.DisassemblerHelpers.OffsetToString(offset)); } else { branchInst = new Branch(targetBlock); } switchInst.Sections.Add(new SwitchSection { Labels = new LongSet(offset), Body = branchInst }); } var usedLabels = new LongSet(dispatch.TargetILOffsets.Select(offset => LongInterval.Inclusive(offset, offset))); switchInst.Sections.Add(new SwitchSection { Labels = usedLabels.Invert(), Body = new Branch(tryBody.EntryPoint) }); dispatchBlock.Instructions.Add(new InvalidExpression("ILSpy has introduced the following switch to emulate a goto from catch-block to try-block") { Severity = "Note" }); dispatchBlock.Instructions.Add(switchInst); tryBody.Blocks.Insert(0, dispatchBlock); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Decompiler step for C# 5 async/await. /// public class AsyncAwaitDecompiler : IILTransform { internal static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata) { TypeDefinition td; if (type.IsNil || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) return false; if (!(type.IsCompilerGenerated(metadata) || td.GetDeclaringType().IsCompilerGenerated(metadata))) return false; foreach (var i in td.GetInterfaceImplementations()) { var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata); if (!tr.IsNested && tr.TopLevelTypeName.Namespace == "System.Runtime.CompilerServices" && tr.TopLevelTypeName.Name == "IAsyncStateMachine") return true; } return false; } internal static bool IsCompilerGeneratedMainMethod(MetadataFile module, MethodDefinitionHandle method) { var metadata = module.Metadata; var definition = metadata.GetMethodDefinition(method); var entrypoint = System.Reflection.Metadata.Ecma335.MetadataTokens.MethodDefinitionHandle(module.CorHeader?.EntryPointTokenOrRelativeVirtualAddress ?? 0); return method == entrypoint && metadata.GetString(definition.Name).Equals("
", StringComparison.Ordinal); } enum AsyncMethodType { Void, Task, TaskOfT, AsyncEnumerator, AsyncEnumerable } ILTransformContext context; // These fields are set by MatchTaskCreationPattern() or MatchEnumeratorCreationNewObj() IType taskType; // return type of the async method; or IAsyncEnumerable{T}/IAsyncEnumerator{T} IType underlyingReturnType; // return type of the method (only the "T" for Task{T}), for async enumerators this is the type being yielded AsyncMethodType methodType; ITypeDefinition stateMachineType; IType builderType; IField builderField; IField stateField; int initialState; Dictionary fieldToParameterMap = new Dictionary(); Dictionary cachedFieldToParameterMap = new Dictionary(); IField disposeModeField; // 'disposeMode' field (IAsyncEnumerable/IAsyncEnumerator only) // These fields are set by AnalyzeMoveNext(): ILFunction moveNextFunction; ILVariable cachedStateVar; // variable in MoveNext that caches the stateField. TryCatch mainTryCatch; Block setResultReturnBlock; // block that is jumped to for return statements // Note: for async enumerators, a jump to setResultReturnBlock is a 'yield break;' int finalState; // final state after the setResultAndExitBlock bool finalStateKnown; ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock Block setResultYieldBlock; // block that is jumped to for 'yield return' statements ILVariable doFinallyBodies; // These fields are set by AnalyzeStateMachine(): int smallestAwaiterVarIndex; HashSet moveNextLeaves = new HashSet(); // For each block containing an 'await', stores the awaiter variable, and the field storing the awaiter // across the yield point. Dictionary awaitBlocks = new Dictionary(); bool isVisualBasicStateMachine; int catchHandlerOffset; List awaitDebugInfos = new List(); public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AsyncAwait) return; // abort if async/await decompilation is disabled this.context = context; fieldToParameterMap.Clear(); cachedFieldToParameterMap.Clear(); awaitBlocks.Clear(); awaitDebugInfos.Clear(); moveNextLeaves.Clear(); if (!MatchTaskCreationPattern(function) && !MatchAsyncEnumeratorCreationPattern(function)) return; try { AnalyzeMoveNext(); ValidateCatchBlock(); AnalyzeDisposeAsync(); } catch (SymbolicAnalysisFailedException) { return; } InlineBodyOfMoveNext(function); function.CheckInvariant(ILPhase.InAsyncAwait); CleanUpBodyOfMoveNext(function); function.CheckInvariant(ILPhase.InAsyncAwait); AnalyzeStateMachine(function); DetectAwaitPattern(function); CleanDoFinallyBodies(function); context.Step("Translate fields to local accesses", function); YieldReturnDecompiler.TranslateFieldsToLocalAccess(function, function, fieldToParameterMap); TranslateCachedFieldsToLocals(); FinalizeInlineMoveNext(function); if (methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator) { ((BlockContainer)function.Body).ExpectedResultType = StackType.Void; } else { ((BlockContainer)function.Body).ExpectedResultType = underlyingReturnType.GetStackType(); } // Re-run control flow simplification over the newly constructed set of gotos, // and inlining because TranslateFieldsToLocalAccess() might have opened up new inlining opportunities. function.RunTransforms(CSharpDecompiler.EarlyILTransforms(), context); if (!isVisualBasicStateMachine) { context.StepStartGroup("AwaitInCatchTransform"); AwaitInCatchTransform.Run(function, context); context.StepEndGroup(); context.StepStartGroup("AwaitInFinallyTransform"); AwaitInFinallyTransform.Run(function, context); context.StepEndGroup(); } awaitDebugInfos.SortBy(row => row.YieldOffset); function.AsyncDebugInfo = new AsyncDebugInfo(catchHandlerOffset, awaitDebugInfos.ToImmutableArray()); } private void CleanUpBodyOfMoveNext(ILFunction function) { context.StepStartGroup("CleanUpBodyOfMoveNext", function); // Copy-propagate stack slots holding an 'ldloca': foreach (var stloc in function.Descendants.OfType().Where(s => s.Variable.Kind == VariableKind.StackSlot && s.Variable.IsSingleDefinition && s.Value is LdLoca).ToList()) { CopyPropagation.Propagate(stloc, context); } // Simplify stobj(ldloca) -> stloc foreach (var stobj in function.Descendants.OfType()) { EarlyExpressionTransforms.StObjToStLoc(stobj, context); } if (isVisualBasicStateMachine) { // Visual Basic state machines often contain stack slots for ldflda instruction for the builder field. // This messes up the matching code in AnalyzeAwaitBlock() and causes it to fail. // Propagating these stack stores makes the matching code work without the need for a lot of // additional matching code to handle these stack slots. foreach (var stloc in function.Descendants.OfType().Where(s => s.Variable.Kind == VariableKind.StackSlot && s.Variable.IsSingleDefinition && s.Value.MatchLdFlda(out var target, out var field) && field.Equals(builderField) && target.MatchLdThis()).ToList()) { CopyPropagation.Propagate(stloc, context); } // Remove lone 'ldc.i4', present in older Roslyn VB compiler output foreach (var block in function.Descendants.OfType()) block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.LdcI4); } // Copy-propagate temporaries holding a copy of 'this'. foreach (var stloc in function.Descendants.OfType().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) { CopyPropagation.Propagate(stloc, context); } new RemoveDeadVariableInit().Run(function, context); foreach (var block in function.Descendants.OfType()) { // Run inlining, but don't remove dead variables (they might get revived by TranslateFieldsToLocalAccess) ILInlining.InlineAllInBlock(function, block, InliningOptions.None, context); if (IsAsyncEnumerator) { // Remove lone 'ldc.i4', those are sometimes left over after C# compiler // optimizes out stores to the state variable. block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.LdcI4); } } context.StepEndGroup(); } #region MatchTaskCreationPattern bool MatchTaskCreationPattern(ILFunction function) { if (!(function.Body is BlockContainer blockContainer)) return false; if (blockContainer.Blocks.Count != 1) return false; var body = blockContainer.EntryPoint.Instructions; if (body.Count < 4) return false; /* Example: V_0 is an instance of the compiler-generated struct/class, V_1 is an instance of the builder struct/class Block IL_0000 (incoming: 1) { ... stobj AsyncVoidMethodBuilder(ldflda [Field Async+d__3.<>t__builder](ldloca V_0), call Create()) stobj System.Int32(ldflda [Field Async+d__3.<>1__state](ldloca V_0), ldc.i4 -1) stloc V_1(ldobj System.Runtime.CompilerServices.AsyncVoidMethodBuilder(ldflda [Field Async+d__3.<>t__builder](ldloc V_0))) call Start(ldloca V_1, ldloca V_0) leave IL_0000 (or ret for non-void async methods) } With custom task types, it's possible that the builder is a reference type. In that case, the variable V_1 may be inlined. */ // Check the second-to-last instruction (the start call) first, as we can get the most information from that int pos = body.Count - 2; if (!(body[pos] is CallInstruction startCall)) return false; if (startCall.Method.Name != "Start") return false; taskType = function.Method.ReturnType; builderType = startCall.Method.DeclaringType; FullTypeName builderTypeName; if (builderType?.GetDefinitionOrUnknown() is { } builderTypeDef) { builderTypeName = builderTypeDef.FullTypeName; } else { return false; } if (taskType.IsKnownType(KnownTypeCode.Void)) { methodType = AsyncMethodType.Void; underlyingReturnType = taskType; if (builderTypeName != new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncVoidMethodBuilder")) return false; } else if (TaskType.IsNonGenericTaskType(taskType, out var builderTypeNameFromTask)) { methodType = AsyncMethodType.Task; underlyingReturnType = context.TypeSystem.FindType(KnownTypeCode.Void); if (builderTypeNameFromTask != builderTypeName) return false; } else if (TaskType.IsGenericTaskType(taskType, out builderTypeNameFromTask)) { methodType = AsyncMethodType.TaskOfT; if (taskType.IsKnownType(KnownTypeCode.TaskOfT)) underlyingReturnType = TaskType.UnpackTask(context.TypeSystem, taskType); else underlyingReturnType = startCall.Method.DeclaringType.TypeArguments[0]; if (builderTypeNameFromTask != builderTypeName) return false; } else { return false; } if (startCall.Arguments.Count != 2) return false; ILInstruction loadBuilderExpr = startCall.Arguments[0]; if (!startCall.Arguments[1].MatchLdLoca(out ILVariable stateMachineVar)) return false; stateMachineType = stateMachineVar.Type.GetDefinition(); if (stateMachineType == null) return false; pos--; if (loadBuilderExpr.MatchLdLocRef(out ILVariable builderVar)) { // Check third-to-last instruction (copy of builder) // stloc builder(ldfld StateMachine::<>t__builder(ldloc stateMachine)) if (!body[pos].MatchStLoc(builderVar, out loadBuilderExpr)) return false; pos--; } if (loadBuilderExpr.MatchLdFld(out var loadStateMachineForBuilderExpr, out builderField)) { // OK, calling Start on copy of stateMachine.<>t__builder } else if (loadBuilderExpr.MatchLdFlda(out loadStateMachineForBuilderExpr, out builderField)) { // OK, Roslyn 3.6 started directly calling Start without making a copy } else { return false; } builderField = (IField)builderField.MemberDefinition; if (!(loadStateMachineForBuilderExpr.MatchLdLocRef(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdLoc(stateMachineVar))) return false; // Check the last instruction (ret) if (methodType == AsyncMethodType.Void) { if (!body.Last().MatchLeave(blockContainer)) return false; } else { // ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine)))) if (!body.Last().MatchReturn(out var returnValue)) return false; if (!MatchCall(returnValue, "get_Task", out var getTaskArgs) || getTaskArgs.Count != 1) return false; ILInstruction target; IField builderField2; if (builderType.IsReferenceType == true) { if (!getTaskArgs[0].MatchLdFld(out target, out builderField2)) return false; } else { if (!getTaskArgs[0].MatchLdFlda(out target, out builderField2)) return false; } if (builderField2.MemberDefinition != builderField) return false; if (!(target.MatchLdLoc(stateMachineVar) || target.MatchLdLoca(stateMachineVar))) return false; } if (IsPotentialVisualBasicStateMachineInitialiation(body[0], stateMachineVar)) { // Visual Basic compiler uses a different order of field assignements if (MatchVisualBasicStateMachineFieldAssignements(body, stateMachineVar)) { isVisualBasicStateMachine = true; return true; } // If the Visual Basic matching code failed, reset the map in case it // was partially populated and try matching the C# state machine initialization. fieldToParameterMap.Clear(); } // Check the last field assignment - this should be the state field // stfld <>1__state(ldloca stateField, ldc.i4 -1) if (!MatchStFld(body[pos], stateMachineVar, out stateField, out var initialStateExpr)) return false; if (!initialStateExpr.MatchLdcI4(out initialState)) return false; if (initialState != -1) return false; int stopPos = pos; pos = 0; if (stateMachineType.Kind == TypeKind.Class) { // If state machine is a class, the first instruction creates an instance: // stloc stateMachine(newobj StateMachine.ctor()) if (!body[pos].MatchStLoc(stateMachineVar, out var init)) return false; if (!(init is NewObj newobj && newobj.Arguments.Count == 0 && newobj.Method.DeclaringTypeDefinition == stateMachineType)) return false; pos++; } bool builderFieldIsInitialized = false; for (; pos < stopPos; pos++) { // stfld StateMachine.field(ldloca stateMachine, ldvar(param)) if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit)) return false; if (field == builderField) { // stfld StateMachine.builder(ldloca stateMachine, call Create()) if (!(fieldInit is Call { Method: { Name: "Create" }, Arguments: { Count: 0 } })) return false; builderFieldIsInitialized = true; } else if (fieldInit.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) { // OK, copies parameter into state machine fieldToParameterMap[field] = v; } else if (fieldInit is LdObj ldobj && ldobj.Target.MatchLdThis()) { // stfld <>4__this(ldloc stateMachine, ldobj AsyncInStruct(ldloc this)) fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable; } else { return false; } } return builderFieldIsInitialized; } bool IsPotentialVisualBasicStateMachineInitialiation(ILInstruction inst, ILVariable stateMachineVar) { // stobj VB$StateMachine(ldloca stateMachine, default.value VB$StateMachine) // Used by VBC (Debug and Release) and Roslyn VB (Release) builds if (inst.MatchStObj(out var target, out var val, out var type1) && target.MatchLdLoca(stateMachineVar) && val.MatchDefaultValue(out var type2) && type1.Equals(type2)) return true; // stloc stateMachine(newobj VB$StateMachine..ctor()) // Used by Roslyn VB (Debug) builds if (inst.MatchStLoc(stateMachineVar, out var init) && init is NewObj newObj && newObj.Arguments.Count == 0 && stateMachineType.Equals(newObj.Method.DeclaringTypeDefinition)) return true; return false; } bool MatchVisualBasicStateMachineFieldAssignements(IList body, ILVariable stateMachineVar) { // stfld $State(ldloca stateField, ldc.i4 -1) if (!MatchStFld(body[body.Count - 4], stateMachineVar, out stateField, out var initialStateExpr)) return false; if (!initialStateExpr.MatchLdcI4(out initialState)) return false; if (initialState != -1) return false; // stfld $Builder(ldloca stateMachine, call Create()) if (!MatchStFld(body[body.Count - 3], stateMachineVar, out var builderField2, out var fieldInit)) return false; if (!builderField.Equals(builderField2)) return false; if (fieldInit is not Call { Method: { Name: "Create" }, Arguments: { Count: 0 } }) return false; for (int i = 1; i < body.Count - 4; i++) { if (!MatchStFld(body[i], stateMachineVar, out var field, out fieldInit)) return false; // Legacy Visual Basic compiler can emit a call to RuntimeHelpers.GetObjectValue here if (fieldInit is Call call && call.Arguments.Count == 1 && call.Method.IsStatic && call.Method.Name == "GetObjectValue") fieldInit = call.Arguments[0]; if (fieldInit.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) { // OK, copies parameter into state machine fieldToParameterMap[field] = v; } else if (fieldInit is LdObj ldobj && ldobj.Target.MatchLdThis()) { // stfld <>4__this(ldloc stateMachine, ldobj AsyncInStruct(ldloc this)) fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable; } else return false; } return true; } /// /// Matches a (potentially virtual) instance method call. /// static bool MatchCall(ILInstruction inst, string name, out InstructionCollection args) { if (inst is CallInstruction call && (call.OpCode == OpCode.Call || call.OpCode == OpCode.CallVirt) && call.Method.Name == name && !call.Method.IsStatic) { args = call.Arguments; return args.Count > 0; } args = null; return false; } /// /// Matches a store to the state machine. /// static bool MatchStFld(ILInstruction stfld, ILVariable stateMachineVar, out IField field, out ILInstruction value) { if (!stfld.MatchStFld(out var target, out field, out value)) return false; field = field.MemberDefinition as IField; return field != null && target.MatchLdLocRef(stateMachineVar); } #endregion #region MatchAsyncEnumeratorCreationPattern private bool MatchAsyncEnumeratorCreationPattern(ILFunction function) { if (!context.Settings.AsyncEnumerator) return false; taskType = function.ReturnType; if (taskType.IsKnownType(KnownTypeCode.IAsyncEnumeratorOfT)) { methodType = AsyncMethodType.AsyncEnumerator; } else if (taskType.IsKnownType(KnownTypeCode.IAsyncEnumerableOfT)) { methodType = AsyncMethodType.AsyncEnumerable; } else { return false; } underlyingReturnType = taskType.TypeArguments.Single(); if (!(function.Body is BlockContainer blockContainer)) return false; if (blockContainer.Blocks.Count != 1) return false; var body = blockContainer.EntryPoint; if (body.Instructions.Count == 1) { // No parameters passed to enumerator (not even 'this'): // ret(newobj(...)) if (!body.Instructions[0].MatchReturn(out var newObj)) return false; if (MatchEnumeratorCreationNewObj(newObj, context, out initialState, out stateMachineType)) { // HACK: the normal async/await logic expects 'initialState' to be the 'in progress' state initialState = -1; try { AnalyzeEnumeratorCtor(((NewObj)newObj).Method, context, out builderField, out builderType, out stateField); } catch (SymbolicAnalysisFailedException) { return false; } return true; } else { return false; } } else { // stloc v(newobj d__0..ctor(ldc.i4 - 2)) // stfld <>4__this(ldloc v, ldloc this) // stfld <>3__otherParam(ldloc v, ldloc otherParam) // leave IL_0000(ldloc v) int pos = 0; if (!body.Instructions[pos].MatchStLoc(out var v, out var newObj)) return false; if (!MatchEnumeratorCreationNewObj(newObj, context, out initialState, out stateMachineType)) return false; pos++; while (MatchStFld(body.Instructions[pos], v, out var field, out var value)) { if (value.MatchLdLoc(out var p) && p.Kind == VariableKind.Parameter) { fieldToParameterMap[field] = p; } else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) { fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable; } else { return false; } pos++; } if (!body.Instructions[pos].MatchReturn(out var returnValue)) return false; if (!returnValue.MatchLdLoc(v)) return false; // HACK: the normal async/await logic expects 'initialState' to be the 'in progress' state initialState = -1; try { AnalyzeEnumeratorCtor(((NewObj)newObj).Method, context, out builderField, out builderType, out stateField); if (methodType == AsyncMethodType.AsyncEnumerable) { ResolveIEnumerableIEnumeratorFieldMapping(); } } catch (SymbolicAnalysisFailedException) { return false; } return true; } } static bool MatchEnumeratorCreationNewObj(ILInstruction inst, ILTransformContext context, out int initialState, out ITypeDefinition stateMachineType) { initialState = default; stateMachineType = default; // newobj(CurrentType/...::.ctor, ldc.i4(-2)) if (!(inst is NewObj newObj)) return false; if (newObj.Arguments.Count != 1) return false; if (!newObj.Arguments[0].MatchLdcI4(out initialState)) return false; stateMachineType = newObj.Method.DeclaringTypeDefinition; if (stateMachineType == null) return false; if (stateMachineType.DeclaringTypeDefinition != context.Function.Method.DeclaringTypeDefinition) return false; return IsCompilerGeneratorAsyncEnumerator( (TypeDefinitionHandle)stateMachineType.MetadataToken, context.TypeSystem.MainModule.metadata); } public static bool IsCompilerGeneratorAsyncEnumerator(TypeDefinitionHandle type, MetadataReader metadata) { TypeDefinition td; if (type.IsNil || !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) return false; foreach (var i in td.GetInterfaceImplementations()) { var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata); if (!tr.IsNested && tr.TopLevelTypeName.Namespace == "System.Collections.Generic" && tr.TopLevelTypeName.Name == "IAsyncEnumerator" && tr.TopLevelTypeName.TypeParameterCount == 1) return true; } return false; } static void AnalyzeEnumeratorCtor(IMethod ctor, ILTransformContext context, out IField builderField, out IType builderType, out IField stateField) { builderField = null; stateField = null; var ctorHandle = (MethodDefinitionHandle)ctor.MetadataToken; Block body = YieldReturnDecompiler.SingleBlock(YieldReturnDecompiler.CreateILAst(ctorHandle, context).Body); if (body == null) throw new SymbolicAnalysisFailedException("Missing enumeratorCtor.Body"); // Block IL_0000 (incoming: 1) { // call Object..ctor(ldloc this) // stfld <>1__state(ldloc this, ldloc <>1__state) // stfld <>t__builder(ldloc this, call Create()) // leave IL_0000 (nop) // } foreach (var inst in body.Instructions) { if (inst.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && value.MatchLdLoc(out var arg) && arg.Kind == VariableKind.Parameter && arg.Index == 0) { stateField = (IField)field.MemberDefinition; } if (inst.MatchStFld(out target, out field, out value) && target.MatchLdThis() && value is Call call && call.Method.Name == "Create") { builderField = (IField)field.MemberDefinition; } } if (stateField == null || builderField == null) throw new SymbolicAnalysisFailedException(); builderType = builderField.Type; if (builderType == null) throw new SymbolicAnalysisFailedException(); } private void ResolveIEnumerableIEnumeratorFieldMapping() { var getAsyncEnumerator = stateMachineType.Methods.FirstOrDefault(m => m.Name.EndsWith(".GetAsyncEnumerator", StringComparison.Ordinal)); if (getAsyncEnumerator == null) throw new SymbolicAnalysisFailedException(); YieldReturnDecompiler.ResolveIEnumerableIEnumeratorFieldMapping((MethodDefinitionHandle)getAsyncEnumerator.MetadataToken, context, fieldToParameterMap); } #endregion #region AnalyzeMoveNext /// /// First peek into MoveNext(); analyzes everything outside the big try-catch. /// void AnalyzeMoveNext() { if (stateMachineType.MetadataToken.IsNil) throw new SymbolicAnalysisFailedException(); var metadata = context.PEFile.Metadata; var moveNextMethod = metadata.GetTypeDefinition((TypeDefinitionHandle)stateMachineType.MetadataToken) .GetMethods().FirstOrDefault(f => metadata.GetString(metadata.GetMethodDefinition(f).Name) == "MoveNext"); if (moveNextMethod == null) throw new SymbolicAnalysisFailedException(); moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context); if (!(moveNextFunction.Body is BlockContainer blockContainer)) throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); bool[] blocksAnalyzed = new bool[blockContainer.Blocks.Count]; cachedStateVar = null; int pos = 0; // Visual Basic state machines initialize doFinallyBodies at the start of MoveNext() // stloc doFinallyBodies(ldc.i4 1) if (isVisualBasicStateMachine && blockContainer.EntryPoint.Instructions[pos].MatchStLoc(out var v, out var ldci4) && ldci4.MatchLdcI4(1)) { doFinallyBodies = v; pos++; } while (blockContainer.EntryPoint.Instructions[pos] is StLoc stloc) { // stloc V_1(ldfld <>4__this(ldloc this)) if (!stloc.Value.MatchLdFld(out var target, out var field)) throw new SymbolicAnalysisFailedException(); if (!target.MatchLdThis()) throw new SymbolicAnalysisFailedException(); if (field.MemberDefinition == stateField && cachedStateVar == null) { // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) cachedStateVar = stloc.Variable; } else if (fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) { if (!stloc.Variable.IsSingleDefinition) throw new SymbolicAnalysisFailedException(); cachedFieldToParameterMap[stloc.Variable] = param; } else { throw new SymbolicAnalysisFailedException(); } pos++; } mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch; if (mainTryCatch == null) throw new SymbolicAnalysisFailedException(); // CatchHandler will be validated in ValidateCatchBlock() // stloc doFinallyBodies(ldc.i4 1) if (((BlockContainer)mainTryCatch.TryBlock).EntryPoint.Instructions[0] is StLoc initDoFinallyBodies && initDoFinallyBodies.Variable.Kind == VariableKind.Local && initDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean) && initDoFinallyBodies.Value.MatchLdcI4(1)) { doFinallyBodies = initDoFinallyBodies.Variable; } Debug.Assert(blockContainer.Blocks[0] == blockContainer.EntryPoint); // already checked this block blocksAnalyzed[0] = true; pos = 1; if (MatchYieldBlock(blockContainer, pos)) { setResultYieldBlock = blockContainer.Blocks[pos]; blocksAnalyzed[pos] = true; pos++; } else { setResultYieldBlock = null; } setResultReturnBlock = CheckSetResultReturnBlock(blockContainer, pos, blocksAnalyzed); if (!blocksAnalyzed.All(b => b)) throw new SymbolicAnalysisFailedException("too many blocks"); } private bool IsAsyncEnumerator => methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator; bool MatchYieldBlock(BlockContainer blockContainer, int pos) { if (!IsAsyncEnumerator) return false; var block = blockContainer.Blocks.ElementAtOrDefault(pos); if (block == null) return false; // call SetResult(ldflda <>v__promiseOfValueOrEnd(ldloc this), ldc.i4 1) // leave IL_0000(nop) if (block.Instructions.Count != 2) return false; if (!MatchCall(block.Instructions[0], "SetResult", out var args)) return false; if (args.Count != 2) return false; if (!IsBuilderOrPromiseFieldOnThis(args[0])) return false; if (!args[1].MatchLdcI4(1)) return false; return block.Instructions[1].MatchLeave(blockContainer); } private Block CheckSetResultReturnBlock(BlockContainer blockContainer, int setResultReturnBlockIndex, bool[] blocksAnalyzed) { if (setResultReturnBlockIndex >= blockContainer.Blocks.Count) { // This block can be absent if the function never exits normally, // but always throws an exception/loops infinitely. resultVar = null; finalStateKnown = false; // final state will be detected in ValidateCatchBlock() instead return null; } var block = blockContainer.Blocks[setResultReturnBlockIndex]; int pos = 0; // [vb-only] stloc S_10(ldloc this) if (block.Instructions[pos] is StLoc stlocThisCache && stlocThisCache.Value.MatchLdThis() && stlocThisCache.Variable.Kind == VariableKind.StackSlot) { pos++; } // [vb-only] stloc S_11(ldc.i4 -2) ILVariable finalStateSlot = null; int? finalStateSlotValue = null; if (block.Instructions[pos] is StLoc stlocFinalState && stlocFinalState.Value is LdcI4 ldcI4 && stlocFinalState.Variable.Kind == VariableKind.StackSlot) { finalStateSlot = stlocFinalState.Variable; finalStateSlotValue = ldcI4.Value; pos++; } // [vb-only] stloc cachedStateVar(ldloc S_11) if (block.Instructions[pos] is StLoc stlocCachedState && stlocCachedState.Variable.Kind == VariableKind.Local && stlocCachedState.Variable.Index == cachedStateVar?.Index && stlocCachedState.Value.MatchLdLoc(finalStateSlot)) { pos++; } // stfld <>1__state(ldloc this, ldc.i4 -2) if (!MatchStateAssignment(block.Instructions[pos], out finalState)) throw new SymbolicAnalysisFailedException(); if (finalStateSlotValue is not null && finalState != finalStateSlotValue.Value) throw new SymbolicAnalysisFailedException(); finalStateKnown = true; pos++; if (pos + 2 == block.Instructions.Count && block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) { if (MatchDisposeCombinedTokens(blockContainer, condition, trueInst, falseInst, blocksAnalyzed, out var setResultAndExitBlock)) { blocksAnalyzed[block.ChildIndex] = true; block = setResultAndExitBlock; pos = 0; } else { throw new SymbolicAnalysisFailedException(); } } // [optional] stfld <>u__N(ldloc this, ldnull) MatchHoistedLocalCleanup(block, ref pos); CheckSetResultAndExit(blockContainer, block, ref pos); blocksAnalyzed[block.ChildIndex] = true; return blockContainer.Blocks[setResultReturnBlockIndex]; } private bool MatchDisposeCombinedTokens(BlockContainer blockContainer, ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst, bool[] blocksAnalyzed, out Block setResultAndExitBlock) { setResultAndExitBlock = null; // ... // if (comp.o(ldfld <>x__combinedTokens(ldloc this) == ldnull)) br setResultAndExit // br disposeCombinedTokens // } // // Block disposeCombinedTokens (incoming: 1) { // callvirt Dispose(ldfld <>x__combinedTokens(ldloc this)) // stfld <>x__combinedTokens(ldloc this, ldnull) // br setResultAndExit // } if (!condition.MatchCompEqualsNull(out var testedInst)) return false; if (!testedInst.MatchLdFld(out var target, out var ctsField)) return false; if (!target.MatchLdThis()) return false; if (!(ctsField.Type is ITypeDefinition { FullTypeName: { IsNested: false, TopLevelTypeName: { Name: "CancellationTokenSource", Namespace: "System.Threading" } } })) return false; if (!(trueInst.MatchBranch(out setResultAndExitBlock) && falseInst.MatchBranch(out var disposedCombinedTokensBlock))) return false; if (!(setResultAndExitBlock.Parent == blockContainer && disposedCombinedTokensBlock.Parent == blockContainer)) return false; var block = disposedCombinedTokensBlock; int pos = 0; // callvirt Dispose(ldfld <>x__combinedTokens(ldloc this)) if (!(block.Instructions[pos] is CallVirt { Method: { Name: "Dispose" } } disposeCall)) return false; if (disposeCall.Arguments.Count != 1) return false; if (!disposeCall.Arguments[0].MatchLdFld(out var target2, out var ctsField2)) return false; if (!(target2.MatchLdThis() && ctsField2.Equals(ctsField))) return false; pos++; // stfld <>x__combinedTokens(ldloc this, ldnull) if (!block.Instructions[pos].MatchStFld(out var target3, out var ctsField3, out var storedValue)) return false; if (!(target3.MatchLdThis() && ctsField3.Equals(ctsField))) return false; if (!storedValue.MatchLdNull()) return false; pos++; // br setResultAndExit if (!block.Instructions[pos].MatchBranch(setResultAndExitBlock)) return false; blocksAnalyzed[block.ChildIndex] = true; return true; } private void MatchHoistedLocalCleanup(Block block, ref int pos) { while (block.Instructions[pos].MatchStFld(out var target, out _, out var value)) { // https://github.com/dotnet/roslyn/pull/39735 hoisted local cleanup if (!target.MatchLdThis()) throw new SymbolicAnalysisFailedException(); if (!value.MatchDefaultOrNullOrZero()) throw new SymbolicAnalysisFailedException(); pos++; } } void CheckSetResultAndExit(BlockContainer blockContainer, Block block, ref int pos) { // [optional] call Complete(ldflda <>t__builder(ldloc this)) (Roslyn >=3.9) // call SetResult(ldflda <>t__builder(ldloc this), ldloc result) // [optional] call Complete(ldflda <>t__builder(ldloc this)) // leave IL_0000 MatchCompleteCall(block, ref pos); if (!MatchCall(block.Instructions[pos], "SetResult", out var args)) throw new SymbolicAnalysisFailedException(); if (!IsBuilderOrPromiseFieldOnThis(args[0])) throw new SymbolicAnalysisFailedException(); switch (methodType) { case AsyncMethodType.TaskOfT: if (args.Count != 2) throw new SymbolicAnalysisFailedException(); if (!args[1].MatchLdLoc(out resultVar)) throw new SymbolicAnalysisFailedException(); break; case AsyncMethodType.Task: case AsyncMethodType.Void: resultVar = null; if (args.Count != 1) throw new SymbolicAnalysisFailedException(); break; case AsyncMethodType.AsyncEnumerable: case AsyncMethodType.AsyncEnumerator: resultVar = null; if (args.Count != 2) throw new SymbolicAnalysisFailedException(); if (!args[1].MatchLdcI4(0)) throw new SymbolicAnalysisFailedException(); break; } pos++; MatchCompleteCall(block, ref pos); if (!block.Instructions[pos].MatchLeave(blockContainer)) throw new SymbolicAnalysisFailedException(); } void ValidateCatchBlock() { // catch E_143 : System.Exception if (ldc.i4 1) BlockContainer { // Block IL_008f (incoming: 1) { // stloc exception(ldloc E_143) // stfld <>1__state(ldloc this, ldc.i4 -2) // [optional] stfld <>u__N(ldloc this, ldnull) // call SetException(ldfld <>t__builder(ldloc this), ldloc exception) // [optional] call Complete(ldfld <>t__builder(ldloc this)) // leave IL_0000 // } // } if (mainTryCatch?.Handlers.Count != 1) throw new SymbolicAnalysisFailedException(); var handler = mainTryCatch.Handlers[0]; if (!handler.Variable.Type.IsKnownType(KnownTypeCode.Exception)) throw new SymbolicAnalysisFailedException(); if (!handler.Filter.MatchLdcI4(1)) throw new SymbolicAnalysisFailedException(); if (!(handler.Body is BlockContainer handlerContainer)) throw new SymbolicAnalysisFailedException(); bool[] blocksAnalyzed = new bool[handlerContainer.Blocks.Count]; var catchBlock = handlerContainer.EntryPoint; catchHandlerOffset = catchBlock.StartILOffset; int pos = 0; // [vb-only] call SetProjectError(ldloc E_143) if (isVisualBasicStateMachine && catchBlock.Instructions[pos] is Call call && call.Method.Name == "SetProjectError" && call.Arguments.Count == 1 && call.Arguments[0].MatchLdLoc(handler.Variable)) { pos++; } // stloc exception(ldloc E_143) if (!(catchBlock.Instructions[pos++] is StLoc stloc)) throw new SymbolicAnalysisFailedException(); if (!stloc.Value.MatchLdLoc(handler.Variable)) throw new SymbolicAnalysisFailedException(); // stfld <>1__state(ldloc this, ldc.i4 -2) if (!MatchStateAssignment(catchBlock.Instructions[pos++], out int newState)) throw new SymbolicAnalysisFailedException(); if (finalStateKnown) { if (newState != finalState) throw new SymbolicAnalysisFailedException(); } else { finalState = newState; finalStateKnown = true; } if (pos + 2 == catchBlock.Instructions.Count && catchBlock.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) { if (MatchDisposeCombinedTokens(handlerContainer, condition, trueInst, falseInst, blocksAnalyzed, out var setResultAndExitBlock)) { blocksAnalyzed[catchBlock.ChildIndex] = true; catchBlock = setResultAndExitBlock; pos = 0; } else { throw new SymbolicAnalysisFailedException(); } } MatchHoistedLocalCleanup(catchBlock, ref pos); // [optional] call Complete(ldfld <>t__builder(ldloc this)) MatchCompleteCall(catchBlock, ref pos); // call SetException(ldfld <>t__builder(ldloc this), ldloc exception) if (!MatchCall(catchBlock.Instructions[pos], "SetException", out var args)) throw new SymbolicAnalysisFailedException(); if (args.Count != 2) throw new SymbolicAnalysisFailedException(); if (!IsBuilderOrPromiseFieldOnThis(args[0])) throw new SymbolicAnalysisFailedException(); if (!args[1].MatchLdLoc(stloc.Variable)) throw new SymbolicAnalysisFailedException(); pos++; // [optional] call Complete(ldfld <>t__builder(ldloc this)) MatchCompleteCall(catchBlock, ref pos); // [vb-only] call ClearProjectError() if (isVisualBasicStateMachine && catchBlock.Instructions[pos] is Call call2 && call2.Method.Name == "ClearProjectError" && call2.Arguments.Count == 0) { pos++; } // leave IL_0000 if (!catchBlock.Instructions[pos].MatchLeave((BlockContainer)moveNextFunction.Body)) throw new SymbolicAnalysisFailedException(); blocksAnalyzed[catchBlock.ChildIndex] = true; if (!blocksAnalyzed.All(b => b)) throw new SymbolicAnalysisFailedException(); } private void MatchCompleteCall(Block block, ref int pos) { if (MatchCall(block.Instructions[pos], "Complete", out var args)) { if (!(args.Count == 1 && IsBuilderFieldOnThis(args[0]))) throw new SymbolicAnalysisFailedException(); pos++; } } bool IsBuilderFieldOnThis(ILInstruction inst) { IField field; ILInstruction target; if (builderType.IsReferenceType == true) { // ldfld(StateMachine::<>t__builder, ldloc(this)) if (!inst.MatchLdFld(out target, out field)) return false; } else { // ldflda(StateMachine::<>t__builder, ldloc(this)) if (!inst.MatchLdFlda(out target, out field)) return false; } return target.MatchLdThis() && field.MemberDefinition == builderField; } bool IsBuilderOrPromiseFieldOnThis(ILInstruction inst) { if (methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator) { return true; // TODO: check against uses of promise fields in other methods? } else { return IsBuilderFieldOnThis(inst); } } bool MatchStateAssignment(ILInstruction inst, out int newState) { // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId)) if (inst.MatchStFld(out var target, out var field, out var value) && StackSlotValue(target).MatchLdThis() && field.MemberDefinition == stateField && StackSlotValue(value).MatchLdcI4(out newState)) { return true; } newState = 0; return false; } /// /// Analyse the DisposeAsync() method in order to find the disposeModeField. /// private void AnalyzeDisposeAsync() { disposeModeField = null; if (!IsAsyncEnumerator) { return; } var disposeAsync = stateMachineType.Methods.FirstOrDefault(m => m.Name.EndsWith(".DisposeAsync", StringComparison.Ordinal)); if (disposeAsync == null) throw new SymbolicAnalysisFailedException("Could not find DisposeAsync()"); var disposeAsyncHandle = (MethodDefinitionHandle)disposeAsync.MetadataToken; var function = YieldReturnDecompiler.CreateILAst(disposeAsyncHandle, context); foreach (var store in function.Descendants) { if (!store.MatchStFld(out var target, out var field, out var value)) continue; if (!target.MatchLdThis()) continue; if (!value.MatchLdcI4(1)) throw new SymbolicAnalysisFailedException(); if (disposeModeField != null) throw new SymbolicAnalysisFailedException("Multiple stores to disposeMode in DisposeAsync()"); disposeModeField = (IField)field.MemberDefinition; } } #endregion #region InlineBodyOfMoveNext void InlineBodyOfMoveNext(ILFunction function) { context.Step("Inline body of MoveNext()", function); function.Body = mainTryCatch.TryBlock; function.AsyncReturnType = underlyingReturnType; function.MoveNextMethod = moveNextFunction.Method; function.SequencePointCandidates = moveNextFunction.SequencePointCandidates; function.CodeSize = moveNextFunction.CodeSize; function.LocalVariableSignatureLength = moveNextFunction.LocalVariableSignatureLength; function.IsIterator = IsAsyncEnumerator; moveNextFunction.Variables.Clear(); moveNextFunction.ReleaseRef(); foreach (var branch in function.Descendants.OfType()) { if (branch.TargetBlock == setResultReturnBlock) { branch.ReplaceWith(new Leave((BlockContainer)function.Body, resultVar == null ? null : new LdLoc(resultVar)).WithILRange(branch)); } } if (setResultYieldBlock != null) { // We still might have branches to this block; and we can't quite yet get rid of it. ((BlockContainer)function.Body).Blocks.Add(setResultYieldBlock); } foreach (var leave in function.Descendants.OfType()) { if (leave.TargetContainer == moveNextFunction.Body) { leave.TargetContainer = (BlockContainer)function.Body; moveNextLeaves.Add(leave); } } function.Variables.AddRange(function.Descendants.OfType().Select(inst => inst.Variable).Distinct()); function.Variables.RemoveDead(); function.Variables.AddRange(fieldToParameterMap.Values); } void FinalizeInlineMoveNext(ILFunction function) { context.Step("FinalizeInlineMoveNext()", function); foreach (var leave in function.Descendants.OfType()) { if (moveNextLeaves.Contains(leave)) { leave.ReplaceWith(new InvalidBranch { Message = "leave MoveNext - await not detected correctly" }.WithILRange(leave)); } } // Delete dead loads of the state cache variable: foreach (var block in function.Descendants.OfType()) { for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (block.Instructions[i].MatchStLoc(out var v, out var value) && v.IsSingleDefinition && v.LoadCount == 0 && value.MatchLdLoc(cachedStateVar)) { block.Instructions.RemoveAt(i); } } } } #endregion #region AnalyzeStateMachine /// /// Analyze the the state machine; and replace 'leave IL_0000' with await+jump to block that gets /// entered on the next MoveNext() call. /// void AnalyzeStateMachine(ILFunction function) { context.StepStartGroup("AnalyzeStateMachine()", function); smallestAwaiterVarIndex = int.MaxValue; foreach (var container in function.Descendants.OfType()) { // Use a separate state range analysis per container. var sra = new StateRangeAnalysis(StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); sra.CancellationToken = context.CancellationToken; sra.doFinallyBodies = doFinallyBodies; sra.AssignStateRanges(container, LongSet.Universe); var stateToBlockMap = sra.GetBlockStateSetMapping(container); foreach (var block in container.Blocks) { context.CancellationToken.ThrowIfCancellationRequested(); if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave)) { // This is likely an 'await' block context.Step($"AnalyzeAwaitBlock({block.StartILOffset:x4})", block); if (AnalyzeAwaitBlock(block, out var awaiterVar, out var awaiterField, out int state, out int yieldOffset)) { block.Instructions.Add(new Await(new LdLoca(awaiterVar))); Block targetBlock = stateToBlockMap.GetOrDefault(state); if (targetBlock != null) { awaitDebugInfos.Add(new AsyncDebugInfo.Await(yieldOffset, targetBlock.StartILOffset)); block.Instructions.Add(new Branch(targetBlock)); } else { block.Instructions.Add(new InvalidBranch("Could not find block for state " + state)); } awaitBlocks.Add(block, (awaiterVar, awaiterField)); if (awaiterVar.Index < smallestAwaiterVarIndex) { smallestAwaiterVarIndex = awaiterVar.Index.Value; } } } else if (block.Instructions.Last().MatchBranch(setResultYieldBlock)) { // This is a 'yield return' in an async enumerator. context.Step($"AnalyzeYieldReturn({block.StartILOffset:x4})", block); if (AnalyzeYieldReturn(block, out var yieldValue, out int state)) { block.Instructions.Add(new YieldReturn(yieldValue)); Block targetBlock = stateToBlockMap.GetOrDefault(state); if (targetBlock != null) { block.Instructions.Add(new Branch(targetBlock)); } else { block.Instructions.Add(new InvalidBranch("Could not find block for state " + state)); } } else { block.Instructions.Add(new InvalidBranch("Could not detect 'yield return'")); } } TransformYieldBreak(block); } foreach (var block in container.Blocks) { SimplifyIfDisposeMode(block); } // Skip the state dispatcher and directly jump to the initial state var entryPoint = stateToBlockMap.GetOrDefault(initialState); if (entryPoint != null) { container.Blocks.Insert(0, new Block { Instructions = { new Branch(entryPoint) } }); } container.SortBlocks(deleteUnreachableBlocks: true); } context.StepEndGroup(); } private bool TransformYieldBreak(Block block) { // stfld disposeMode(ldloc this, ldc.i4 1) // br nextBlock if (block.Instructions.Count < 2) return false; if (!(block.Instructions.Last() is Branch branch)) return false; if (!block.Instructions[block.Instructions.Count - 2].MatchStFld(out var target, out var field, out var value)) return false; if (!target.MatchLdThis()) return false; if (field.MemberDefinition != disposeModeField) return false; if (!value.MatchLdcI4(1)) return false; // Detected a 'yield break;' context.Step($"TransformYieldBreak({block.StartILOffset:x4})", block); var breakTarget = FindYieldBreakTarget(branch.TargetBlock); if (breakTarget is Block targetBlock) { branch.TargetBlock = targetBlock; } else { Debug.Assert(breakTarget is BlockContainer); branch.ReplaceWith(new Leave((BlockContainer)breakTarget).WithILRange(branch)); } return true; } ILInstruction FindYieldBreakTarget(Block block) { // We'll follow the branch and evaluate the following instructions // under the assumption that disposeModeField==1, which lets us follow a series of jumps // to determine the final target. var visited = new HashSet(); var evalContext = new SymbolicEvaluationContext(disposeModeField); while (true) { for (int i = 0; i < block.Instructions.Count; i++) { ILInstruction inst = block.Instructions[i]; while (inst.MatchIfInstruction(out var condition, out var trueInst, out var falseInst)) { var condVal = evalContext.Eval(condition).AsBool(); if (condVal.Type == SymbolicValueType.IntegerConstant) { inst = condVal.Constant != 0 ? trueInst : falseInst; } else if (condVal.Type == SymbolicValueType.StateInSet) { inst = condVal.ValueSet.Contains(1) ? trueInst : falseInst; } else { return block; } } if (inst.MatchBranch(out var targetBlock)) { if (visited.Add(block)) { block = targetBlock; break; // continue with next block } else { return block; // infinite loop detected } } else if (inst is Leave leave && leave.Value.OpCode == OpCode.Nop) { return leave.TargetContainer; } else if (inst.OpCode == OpCode.Nop) { continue; // continue with next instruction in this block } else { return block; } } } } private bool SimplifyIfDisposeMode(Block block) { // Occasionally Roslyn optimizes out an "if (disposeMode)", but keeps the // disposeMode field access. Get rid of those field accesses: block.Instructions.RemoveAll(MatchLdDisposeMode); // if (logic.not(ldfld disposeMode(ldloc this))) br falseInst // br trueInst if (!block.MatchIfAtEndOfBlock(out var condition, out _, out var falseInst)) return false; if (!MatchLdDisposeMode(condition)) return false; context.Step($"SimplifyIfDisposeMode({block.StartILOffset:x4})", block); block.Instructions[block.Instructions.Count - 2] = falseInst; block.Instructions.RemoveAt(block.Instructions.Count - 1); return true; bool MatchLdDisposeMode(ILInstruction inst) { if (!inst.MatchLdFld(out var target, out var field)) return false; return target.MatchLdThis() && field.MemberDefinition == disposeModeField; } } bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state, out int yieldOffset) { awaiter = null; awaiterField = null; state = 0; yieldOffset = -1; int pos = block.Instructions.Count - 2; if (pos >= 0 && doFinallyBodies != null && block.Instructions[pos] is StLoc storeDoFinallyBodies) { if (!(storeDoFinallyBodies.Variable.Kind == VariableKind.Local && storeDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean) && storeDoFinallyBodies.Variable.Index == doFinallyBodies.Index)) { return false; } if (!storeDoFinallyBodies.Value.MatchLdcI4(0)) return false; pos--; } if (pos >= 0 && MatchCall(block.Instructions[pos], "AwaitUnsafeOnCompleted", out var callArgs)) { // call AwaitUnsafeOnCompleted(ldflda <>t__builder(ldloc this), ldloca awaiter, ldloc this) } else if (pos >= 0 && MatchCall(block.Instructions[pos], "AwaitOnCompleted", out callArgs)) { // call AwaitOnCompleted(ldflda <>t__builder(ldloc this), ldloca awaiter, ldloc this) // The C# compiler emits the non-unsafe call when the awaiter does not implement // ICriticalNotifyCompletion. } else { return false; } if (callArgs.Count != 3) return false; if (!IsBuilderFieldOnThis(callArgs[0])) return false; if (!callArgs[1].MatchLdLoca(out awaiter)) return false; if (callArgs[2].MatchLdThis()) { // OK (if state machine is a struct) pos--; } else if (callArgs[2].MatchLdLoca(out var tempVar)) { // Roslyn, non-optimized uses a class for the state machine. // stloc tempVar(ldloc this) // call AwaitUnsafeOnCompleted(ldflda <>t__builder](ldloc this), ldloca awaiter, ldloca tempVar) if (!(pos > 0 && block.Instructions[pos - 1].MatchStLoc(tempVar, out var tempVal))) return false; if (!tempVal.MatchLdThis()) return false; pos -= 2; } else { return false; } // stfld StateMachine.<>awaiter(ldloc this, ldloc awaiter) if (!block.Instructions[pos].MatchStFld(out var target, out awaiterField, out var value)) return false; if (!target.MatchLdThis()) return false; if (!value.MatchLdLoc(awaiter)) return false; pos--; // Store IL offset for debug info: yieldOffset = block.Instructions[pos].EndILOffset; // stloc S_10(ldloc this) // stloc S_11(ldc.i4 0) // stloc cachedStateVar(ldloc S_11) // stfld <>1__state(ldloc S_10, ldloc S_11) if (!block.Instructions[pos].MatchStFld(out target, out var field, out value)) return false; if (!StackSlotValue(target).MatchLdThis()) return false; if (field.MemberDefinition != stateField) return false; if (!StackSlotValue(value).MatchLdcI4(out state)) return false; if (pos > 0 && block.Instructions[pos - 1] is StLoc stloc && stloc.Variable.Kind == VariableKind.Local && stloc.Variable.Index == cachedStateVar.Index && StackSlotValue(stloc.Value).MatchLdcI4(state)) { // also delete the assignment to cachedStateVar pos--; } block.Instructions.RemoveRange(pos, block.Instructions.Count - pos); // delete preceding dead stores: while (pos > 0 && block.Instructions[pos - 1] is StLoc stloc2 && stloc2.Variable.IsSingleDefinition && stloc2.Variable.LoadCount == 0 && stloc2.Variable.Kind == VariableKind.StackSlot && SemanticHelper.IsPure(stloc2.Value.Flags)) { pos--; } block.Instructions.RemoveRange(pos, block.Instructions.Count - pos); return true; } static ILInstruction StackSlotValue(ILInstruction inst) { if (inst.MatchLdLoc(out var v) && v.Kind == VariableKind.StackSlot && v.IsSingleDefinition) { if (v.StoreInstructions[0] is StLoc stloc) { return stloc.Value; } } return inst; } private bool AnalyzeYieldReturn(Block block, out ILInstruction yieldValue, out int newState) { yieldValue = default; newState = default; Debug.Assert(block.Instructions.Last().MatchBranch(setResultYieldBlock)); // stfld current(ldloc this, ldstr "yieldValue") // stloc S_45(ldloc this) // stloc S_46(ldc.i4 -5) // stloc V_0(ldloc S_46) // stfld stateField(ldloc S_45, ldloc S_46) // br setResultYieldBlock int pos = block.Instructions.Count - 2; // Immediately before the 'yield return', there should be a state assignment: if (pos < 0 || !MatchStateAssignment(block.Instructions[pos], out newState)) return false; pos--; if (pos >= 0 && block.Instructions[pos].MatchStLoc(cachedStateVar, out var cachedStateNewValue)) { if (StackSlotValue(cachedStateNewValue).MatchLdcI4(newState)) { pos--; // OK, ignore V_0 store } else { return false; } } while (pos >= 0 && block.Instructions[pos] is StLoc stloc) { if (stloc.Variable.Kind != VariableKind.StackSlot) return false; if (!SemanticHelper.IsPure(stloc.Value.Flags)) return false; pos--; } if (pos < 0 || !MatchCurrentAssignment(block.Instructions[pos], out yieldValue)) return false; block.Instructions.RemoveRange(pos, block.Instructions.Count - pos); return true; } bool MatchCurrentAssignment(ILInstruction inst, out ILInstruction value) { if (!inst.MatchStFld(out var target, out var field, out value)) return false; if (!StackSlotValue(target).MatchLdThis()) return false; // TODO: check that we are accessing the current field (compare with get_Current) return true; } #endregion #region DetectAwaitPattern void DetectAwaitPattern(ILFunction function) { context.StepStartGroup("DetectAwaitPattern", function); foreach (var container in function.Descendants.OfType()) { foreach (var block in container.Blocks) { context.CancellationToken.ThrowIfCancellationRequested(); DetectAwaitPattern(block); } container.SortBlocks(deleteUnreachableBlocks: true); } context.StepEndGroup(keepIfEmpty: true); } void DetectAwaitPattern(Block block) { // block: // stloc awaiterVar(callvirt GetAwaiter(...)) // if (call get_IsCompleted(ldloca awaiterVar)) br completedBlock // br awaitBlock // awaitBlock: // .. // br resumeBlock // resumeBlock: // .. // br completedBlock if (block.Instructions.Count < 3) return; // stloc awaiterVar(callvirt GetAwaiter(...)) if (!(block.Instructions[block.Instructions.Count - 3] is StLoc stLocAwaiter)) return; ILVariable awaiterVar = stLocAwaiter.Variable; if (!(stLocAwaiter.Value is CallInstruction getAwaiterCall)) return; if (!(getAwaiterCall.Method.Name == "GetAwaiter" && (!getAwaiterCall.Method.IsStatic || getAwaiterCall.Method.IsExtensionMethod))) return; if (getAwaiterCall.Arguments.Count != 1) return; // if (call get_IsCompleted(ldloca awaiterVar)) br completedBlock if (!block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst)) return; if (!trueInst.MatchBranch(out var completedBlock)) return; // br awaitBlock if (!block.Instructions.Last().MatchBranch(out var awaitBlock)) return; // condition might be inverted, swap branches: if (condition.MatchLogicNot(out var negatedCondition)) { condition = negatedCondition; ExtensionMethods.Swap(ref completedBlock, ref awaitBlock); } // continue matching call get_IsCompleted(ldloca awaiterVar) if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) return; if (!UnwrapConvUnknown(isCompletedArgs[0]).MatchLdLocRef(awaiterVar)) return; // Check awaitBlock and resumeBlock: if (!awaitBlocks.TryGetValue(awaitBlock, out var awaitBlockData)) return; if (awaitBlockData.awaiterVar != awaiterVar) return; if (!CheckAwaitBlock(awaitBlock, out var resumeBlock, out var stackField)) return; if (!CheckResumeBlock(resumeBlock, awaiterVar, awaitBlockData.awaiterField, completedBlock, stackField)) return; // Check completedBlock. The first instruction involves the GetResult call, but it might have // been inlined into another instruction. var getResultCall = ILInlining.FindFirstInlinedCall(completedBlock.Instructions[0]); if (getResultCall == null) return; if (!MatchCall(getResultCall, "GetResult", out var getResultArgs) || getResultArgs.Count != 1) return; if (!UnwrapConvUnknown(getResultArgs[0]).MatchLdLocRef(awaiterVar)) return; // All checks successful, let's transform. context.Step("Transform await pattern", block); block.Instructions.RemoveAt(block.Instructions.Count - 3); // remove getAwaiter call block.Instructions.RemoveAt(block.Instructions.Count - 2); // remove if (isCompleted) ((Branch)block.Instructions.Last()).TargetBlock = completedBlock; // instead, directly jump to completed block Await awaitInst = new Await(UnwrapConvUnknown(getAwaiterCall.Arguments.Single())); awaitInst.GetResultMethod = getResultCall.Method; awaitInst.GetAwaiterMethod = getAwaiterCall.Method; getResultCall.ReplaceWith(awaitInst); // Remove useless reset of awaiterVar. if (completedBlock.Instructions.ElementAtOrDefault(1) is StObj stobj) { if (stobj.Target.MatchLdLoca(awaiterVar) && stobj.Value.OpCode == OpCode.DefaultValue) { completedBlock.Instructions.RemoveAt(1); } } } static ILInstruction UnwrapConvUnknown(ILInstruction inst) { if (inst is Conv conv && conv.TargetType == PrimitiveType.Unknown) { return conv.Argument; } return inst; } bool CheckAwaitBlock(Block block, out Block resumeBlock, out IField stackField) { // awaitBlock: // (pre-roslyn: save stack) // await(ldloca V_2) // br resumeBlock resumeBlock = null; stackField = null; if (block.Instructions.Count < 2) return false; int pos = 0; if (block.Instructions[pos] is StLoc stloc && stloc.Variable.IsSingleDefinition) { if (!block.Instructions[pos + 1].MatchStFld(out var target, out stackField, out var value)) return false; if (!target.MatchLdThis()) return false; pos += 2; } // await(ldloca awaiterVar) if (block.Instructions[pos].OpCode != OpCode.Await) return false; // br resumeBlock return block.Instructions[pos + 1].MatchBranch(out resumeBlock); } bool CheckResumeBlock(Block block, ILVariable awaiterVar, IField awaiterField, Block completedBlock, IField stackField) { int pos = 0; if (!RestoreStack(block, ref pos, stackField)) return false; // Visual Basic state machines use a different order of field assignements. if (isVisualBasicStateMachine) { // stloc S_28(ldc.i4 -1) // stloc cachedStateVar(ldloc S_28) // stfld <>1__state(ldloc this, ldloc S_28) if (!MatchStateFieldAssignement(block, ref pos)) return false; pos++; // stloc awaiterVar(ldfld awaiterField(ldloc this)) if (!MatchStoreToAwaiterVariable(block.Instructions[pos])) return false; pos++; // [optional] stfld awaiterField(ldloc this, default.value) MatchResetAwaiterField(block, ref pos); } else { // stloc awaiterVar(ldfld awaiterField(ldloc this)) if (!MatchStoreToAwaiterVariable(block.Instructions[pos])) return false; pos++; // [optional] stfld awaiterField(ldloc this, default.value) MatchResetAwaiterField(block, ref pos); // stloc S_28(ldc.i4 -1) // stloc cachedStateVar(ldloc S_28) // stfld <>1__state(ldloc this, ldloc S_28) if (!MatchStateFieldAssignement(block, ref pos)) return false; pos++; } return block.Instructions[pos].MatchBranch(completedBlock); bool MatchStoreToAwaiterVariable(ILInstruction instr) { // stloc awaiterVar(ldfld awaiterField(ldloc this)) if (!instr.MatchStLoc(awaiterVar, out var value)) return false; if (value is CastClass cast && cast.Type.Equals(awaiterVar.Type)) { // If the awaiter is a reference type, it might get stored in a field of type `object` // and cast back to the awaiter type in the resume block value = cast.Argument; } if (!value.MatchLdFld(out var target, out var field)) return false; if (!target.MatchLdThis()) return false; if (!field.Equals(awaiterField)) return false; return true; } void MatchResetAwaiterField(Block block, ref int pos) { // stfld awaiterField(ldloc this, default.value) if (block.Instructions[pos].MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && field.Equals(awaiterField) && (value.OpCode == OpCode.DefaultValue || value.OpCode == OpCode.LdNull)) { pos++; } else { // {stloc V_6(default.value System.Runtime.CompilerServices.TaskAwaiter)} // {stobj System.Runtime.CompilerServices.TaskAwaiter`1[[System.Int32]](ldflda <>u__$awaiter4(ldloc this), ldloc V_6) at IL_0163} if (block.Instructions[pos].MatchStLoc(out var variable, out value) && value.OpCode == OpCode.DefaultValue && block.Instructions[pos + 1].MatchStFld(out target, out field, out value) && field.Equals(awaiterField) && value.MatchLdLoc(variable)) { pos += 2; } } } bool MatchStateFieldAssignement(Block block, ref int pos) { // stloc S_28(ldc.i4 -1) // stloc cachedStateVar(ldloc S_28) // stfld <>1__state(ldloc this, ldloc S_28) ILVariable m1Var = null; if (block.Instructions[pos] is StLoc stlocM1 && stlocM1.Value.MatchLdcI4(initialState) && stlocM1.Variable.Kind == VariableKind.StackSlot) { m1Var = stlocM1.Variable; pos++; } if (block.Instructions[pos] is StLoc stlocCachedState) { if (stlocCachedState.Variable.Kind == VariableKind.Local && stlocCachedState.Variable.Index == cachedStateVar?.Index) { if (stlocCachedState.Value.MatchLdLoc(m1Var) || stlocCachedState.Value.MatchLdcI4(initialState)) pos++; } } if (!block.Instructions[pos].MatchStFld(out var target, out var field, out var value)) return false; if (!target.MatchLdThis()) return false; if (!field.MemberDefinition.Equals(stateField.MemberDefinition)) return false; if (!(value.MatchLdcI4(initialState) || value.MatchLdLoc(m1Var))) return false; return true; } } private bool RestoreStack(Block block, ref int pos, IField stackField) { if (stackField == null) { return true; // nothing to restore } // stloc temp(unbox.any T(ldfld <>t__stack(ldloc this))) if (!(block.Instructions[pos] is StLoc stloc)) return false; if (!stloc.Variable.IsSingleDefinition) return false; if (!(stloc.Value is UnboxAny unbox)) return false; if (!unbox.Argument.MatchLdFld(out var target, out var field)) return false; if (!target.MatchLdThis()) return false; if (!field.Equals(stackField)) return false; pos++; // restoring stack slots while (block.Instructions[pos].MatchStLoc(out var v) && v.Kind == VariableKind.StackSlot) { pos++; } // stfld <>t__stack(ldloc this, ldnull) if (block.Instructions[pos].MatchStFld(out target, out field, out var value)) { if (target.MatchLdThis() && field.Equals(stackField) && value.MatchLdNull()) { pos++; } } return true; } #endregion /// /// Eliminates usage of doFinallyBodies /// private void CleanDoFinallyBodies(ILFunction function) { if (doFinallyBodies == null) { return; // roslyn-compiled code doesn't use doFinallyBodies } context.StepStartGroup("CleanDoFinallyBodies", function); Block entryPoint = GetBodyEntryPoint(function.Body as BlockContainer); if (entryPoint != null && entryPoint.Instructions[0].MatchStLoc(doFinallyBodies, out var value) && value.MatchLdcI4(1)) { // Remove initial doFinallyBodies assignment, if it wasn't already removed when // we rearranged the control flow. entryPoint.Instructions.RemoveAt(0); } if (doFinallyBodies.StoreInstructions.Count != 0 || doFinallyBodies.AddressCount != 0) { // misdetected another variable as doFinallyBodies? // reintroduce the initial store of ldc.i4(1) context.Step("Re-introduce misdetected doFinallyBodies", function); ((BlockContainer)function.Body).EntryPoint.Instructions.Insert(0, new StLoc(doFinallyBodies, new LdcI4(1))); return; } foreach (var tryFinally in function.Descendants.OfType()) { entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); if (entryPoint?.Instructions[0] is IfInstruction ifInst) { if (ifInst.Condition.MatchLogicNot(out var logicNotArg) && logicNotArg.MatchLdLoc(doFinallyBodies)) { context.Step("Remove if(doFinallyBodies) from try-finally", tryFinally); // condition will always be false now that we're using 'await' instructions entryPoint.Instructions.RemoveAt(0); } } } // if there's any remaining loads (there shouldn't be), replace them with the constant 1 foreach (LdLoc load in doFinallyBodies.LoadInstructions.ToArray()) { load.ReplaceWith(new LdcI4(1).WithILRange(load)); } context.StepEndGroup(keepIfEmpty: true); } internal static Block GetBodyEntryPoint(BlockContainer body) { if (body == null) return null; Block entryPoint = body.EntryPoint; while (entryPoint.Instructions[0].MatchBranch(out var targetBlock) && targetBlock.IncomingEdgeCount == 1 && targetBlock.Parent == body) { entryPoint = targetBlock; } return entryPoint; } void TranslateCachedFieldsToLocals() { foreach (var (cachedVar, param) in cachedFieldToParameterMap) { Debug.Assert(cachedVar.StoreCount <= 1); foreach (var inst in cachedVar.LoadInstructions.ToArray()) { inst.Variable = param; } foreach (var inst in cachedVar.AddressInstructions.ToArray()) { inst.Variable = param; } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs ================================================ // 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 ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.ControlFlow { class AwaitInCatchTransform { readonly struct CatchBlockInfo { public readonly int Id; public readonly TryCatchHandler Handler; public readonly Block RealCatchBlockEntryPoint; public readonly ILInstruction NextBlockOrExitContainer; public readonly ILInstruction JumpTableEntry; public readonly ILVariable ObjectVariable; public CatchBlockInfo(int id, TryCatchHandler handler, Block realCatchBlockEntryPoint, ILInstruction nextBlockOrExitContainer, ILInstruction jumpTableEntry, ILVariable objectVariable) { Id = id; Handler = handler; RealCatchBlockEntryPoint = realCatchBlockEntryPoint; NextBlockOrExitContainer = nextBlockOrExitContainer; JumpTableEntry = jumpTableEntry; ObjectVariable = objectVariable; } } public static void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AwaitInCatchFinally) return; HashSet changedContainers = new HashSet(); HashSet removedBlocks = new HashSet(); // analyze all try-catch statements in the function foreach (var tryCatch in function.Descendants.OfType().ToArray()) { if (!(tryCatch.Parent?.Parent is BlockContainer container)) continue; // Detect all handlers that contain an await expression AnalyzeHandlers(tryCatch.Handlers, out var catchHandlerIdentifier, out var transformableCatchBlocks); var cfg = new ControlFlowGraph(container, context.CancellationToken); if (transformableCatchBlocks.Count > 0) changedContainers.Add(container); SwitchInstruction switchInstructionOpt = null; foreach (var result in transformableCatchBlocks) { removedBlocks.Clear(); var node = cfg.GetNode(result.RealCatchBlockEntryPoint); context.StepStartGroup($"Inline catch block with await (at {result.Handler.Variable.Name})", result.Handler); // Remove the IfInstruction from the jump table and eliminate all branches to the block. switch (result.JumpTableEntry) { case IfInstruction jumpTableEntry: var jumpTableBlock = (Block)jumpTableEntry.Parent; context.Step("Remove jump-table entry", result.JumpTableEntry); jumpTableBlock.Instructions.RemoveAt(result.JumpTableEntry.ChildIndex); foreach (var branch in tryCatch.Descendants.OfType()) { if (branch.TargetBlock == jumpTableBlock) { if (result.NextBlockOrExitContainer is BlockContainer exitContainer) { context.Step("branch jumpTableBlock => leave exitContainer", branch); branch.ReplaceWith(new Leave(exitContainer)); } else { context.Step("branch jumpTableBlock => branch nextBlock", branch); branch.ReplaceWith(new Branch((Block)result.NextBlockOrExitContainer)); } } } break; case SwitchSection jumpTableEntry: Debug.Assert(switchInstructionOpt == null || jumpTableEntry.Parent == switchInstructionOpt); switchInstructionOpt = (SwitchInstruction)jumpTableEntry.Parent; break; } // Add the real catch block entry-point to the block container var catchBlockHead = ((BlockContainer)result.Handler.Body).Blocks.Last(); result.RealCatchBlockEntryPoint.Remove(); ((BlockContainer)result.Handler.Body).Blocks.Insert(0, result.RealCatchBlockEntryPoint); // Remove the generated catch block catchBlockHead.Remove(); TransformAsyncThrowToThrow(context, removedBlocks, result.RealCatchBlockEntryPoint); // Inline all blocks that are dominated by the entrypoint of the real catch block foreach (var n in cfg.cfg) { Block block = (Block)n.UserData; if (node.Dominates(n)) { TransformAsyncThrowToThrow(context, removedBlocks, block); if (block.Parent == result.Handler.Body) continue; if (!removedBlocks.Contains(block)) { context.Step("Move block", result.Handler.Body); MoveBlock(block, (BlockContainer)result.Handler.Body); } } } // Remove unreachable pattern blocks // TODO : sanity check if (result.NextBlockOrExitContainer is Block nextBlock && nextBlock.IncomingEdgeCount == 0) { List dependentBlocks = new List(); Block current = nextBlock; do { foreach (var branch in current.Descendants.OfType()) { dependentBlocks.Add(branch.TargetBlock); } current.Remove(); dependentBlocks.Remove(current); current = dependentBlocks.FirstOrDefault(b => b.IncomingEdgeCount == 0); } while (current != null); } // Remove all assignments to the common object variable that stores the exception object. if (result.ObjectVariable != result.Handler.Variable) { foreach (var load in result.ObjectVariable.LoadInstructions.ToArray()) { if (!load.IsDescendantOf(result.Handler)) continue; if (load.Parent is CastClass cc && cc.Type.Equals(result.Handler.Variable.Type)) { cc.ReplaceWith(new LdLoc(result.Handler.Variable).WithILRange(cc).WithILRange(load)); } else { load.ReplaceWith(new LdLoc(result.Handler.Variable).WithILRange(load)); } } } context.StepEndGroup(keepIfEmpty: true); } if (switchInstructionOpt != null && switchInstructionOpt.Parent is Block b && b.IncomingEdgeCount > 0) { var defaultSection = switchInstructionOpt.GetDefaultSection(); foreach (var branch in container.Descendants.OfType()) { if (branch.TargetBlock != b) continue; branch.ReplaceWith(defaultSection.Body.Clone()); } } } // clean up all modified containers foreach (var container in changedContainers) container.SortBlocks(deleteUnreachableBlocks: true); } private static void TransformAsyncThrowToThrow(ILTransformContext context, HashSet removedBlocks, Block block) { ILVariable v = null; if (MatchExceptionCaptureBlock(context, block, ref v, out StLoc typedExceptionVariableStore, out Block captureBlock, out Block throwBlock)) { context.Step($"ExceptionDispatchInfo.Capture({v.Name}).Throw() => throw;", typedExceptionVariableStore); block.Instructions.RemoveRange(typedExceptionVariableStore.ChildIndex + 1, 2); captureBlock.Remove(); throwBlock.Remove(); removedBlocks.Add(captureBlock); removedBlocks.Add(throwBlock); typedExceptionVariableStore.ReplaceWith(new Rethrow()); } } static void MoveBlock(Block block, BlockContainer target) { block.Remove(); target.Blocks.Add(block); } /// /// Analyzes all catch handlers and returns every handler that follows the await catch handler pattern. /// static bool AnalyzeHandlers(InstructionCollection handlers, out ILVariable catchHandlerIdentifier, out List transformableCatchBlocks) { transformableCatchBlocks = new List(); catchHandlerIdentifier = null; foreach (var handler in handlers) { if (!MatchAwaitCatchHandler(handler, out int id, out var identifierVariable, out var realEntryPoint, out var nextBlockOrExitContainer, out var jumpTableEntry, out var objectVariable)) { continue; } if (id < 1 || (catchHandlerIdentifier != null && identifierVariable != catchHandlerIdentifier)) { continue; } catchHandlerIdentifier = identifierVariable; transformableCatchBlocks.Add(new(id, handler, realEntryPoint, nextBlockOrExitContainer, jumpTableEntry, objectVariable ?? handler.Variable)); } return transformableCatchBlocks.Count > 0; } /// /// Matches the await catch handler pattern: /// [stloc V_3(ldloc E_100) - copy exception variable to a temporary] /// stloc V_6(ldloc V_3) - store exception in 'global' object variable /// stloc V_5(ldc.i4 2) - store id of catch block in 'identifierVariable' /// br IL_0075 - jump out of catch block to the head of the catch-handler jump table /// static bool MatchAwaitCatchHandler(TryCatchHandler handler, out int id, out ILVariable identifierVariable, out Block realEntryPoint, out ILInstruction nextBlockOrExitContainer, out ILInstruction jumpTableEntry, out ILVariable objectVariable) { id = 0; identifierVariable = null; realEntryPoint = null; jumpTableEntry = null; objectVariable = null; nextBlockOrExitContainer = null; var exceptionVariable = handler.Variable; var catchBlock = ((BlockContainer)handler.Body).EntryPoint; ILInstruction value; switch (catchBlock.Instructions.Count) { case 3: if (!catchBlock.Instructions[0].MatchStLoc(out objectVariable, out value)) return false; if (!value.MatchLdLoc(exceptionVariable)) return false; break; case 4: if (!catchBlock.Instructions[0].MatchStLoc(out var temporaryVariable, out value)) return false; if (!value.MatchLdLoc(exceptionVariable)) return false; if (!catchBlock.Instructions[1].MatchStLoc(out objectVariable, out value)) return false; if (!value.MatchLdLoc(temporaryVariable)) return false; break; default: // if the exception variable is not used at all (e.g., catch (Exception)) // the "exception-variable-assignment" is omitted completely. // This can happen in optimized code. break; } if (!catchBlock.Instructions.Last().MatchBranch(out var jumpTableStartBlock)) return false; var identifierVariableAssignment = catchBlock.Instructions.SecondToLastOrDefault(); if (identifierVariableAssignment == null) return false; if (!identifierVariableAssignment.MatchStLoc(out identifierVariable, out value) || !value.MatchLdcI4(out id)) return false; // analyze jump table: switch (jumpTableStartBlock.Instructions.Count) { case 3: // stloc identifierVariableCopy(identifierVariable) // if (comp(identifierVariable == id)) br realEntryPoint // br jumpTableEntryBlock if (!jumpTableStartBlock.Instructions[0].MatchStLoc(out var identifierVariableCopy, out var identifierVariableLoad) || !identifierVariableLoad.MatchLdLoc(identifierVariable)) { return false; } return ParseIfJumpTable(id, jumpTableStartBlock, identifierVariableCopy, out realEntryPoint, out nextBlockOrExitContainer, out jumpTableEntry); case 2: // if (comp(identifierVariable == id)) br realEntryPoint // br jumpTableEntryBlock return ParseIfJumpTable(id, jumpTableStartBlock, identifierVariable, out realEntryPoint, out nextBlockOrExitContainer, out jumpTableEntry); case 1: if (jumpTableStartBlock.Instructions[0] is not SwitchInstruction switchInst) { return false; } return ParseSwitchJumpTable(id, switchInst, identifierVariable, out realEntryPoint, out nextBlockOrExitContainer, out jumpTableEntry); default: return false; } bool ParseSwitchJumpTable(int id, SwitchInstruction jumpTable, ILVariable identifierVariable, out Block realEntryPoint, out ILInstruction nextBlockOrExitContainer, out ILInstruction jumpTableEntry) { realEntryPoint = null; nextBlockOrExitContainer = null; jumpTableEntry = null; if (!jumpTable.Value.MatchLdLoc(identifierVariable)) return false; var defaultSection = jumpTable.GetDefaultSection(); foreach (var section in jumpTable.Sections) { if (!section.Labels.Contains(id)) continue; if (!section.Body.MatchBranch(out realEntryPoint)) return false; if (defaultSection.Body.MatchBranch(out var t)) nextBlockOrExitContainer = t; else if (defaultSection.Body.MatchLeave(out var t2)) nextBlockOrExitContainer = t2; jumpTableEntry = section; return true; } return false; } bool ParseIfJumpTable(int id, Block jumpTableEntryBlock, ILVariable identifierVariable, out Block realEntryPoint, out ILInstruction nextBlockOrExitContainer, out ILInstruction jumpTableEntry) { realEntryPoint = null; nextBlockOrExitContainer = null; jumpTableEntry = null; do { if (!(jumpTableEntryBlock.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) return false; ILInstruction lastInst = jumpTableEntryBlock.Instructions.Last(); if (ifInst.Condition.MatchCompEquals(out var left, out var right)) { if (!ifInst.TrueInst.MatchBranch(out realEntryPoint)) return false; if (!lastInst.MatchBranch(out jumpTableEntryBlock)) { if (!lastInst.MatchLeave((BlockContainer)lastInst.Parent.Parent)) return false; } } else if (ifInst.Condition.MatchCompNotEquals(out left, out right)) { if (!lastInst.MatchBranch(out realEntryPoint)) return false; if (!ifInst.TrueInst.MatchBranch(out jumpTableEntryBlock)) { if (!ifInst.TrueInst.MatchLeave((BlockContainer)lastInst.Parent.Parent)) return false; } } else { return false; } if (!left.MatchLdLoc(identifierVariable)) return false; if (right.MatchLdcI4(id)) { nextBlockOrExitContainer = jumpTableEntryBlock ?? lastInst.Parent.Parent; jumpTableEntry = ifInst; return true; } } while (jumpTableEntryBlock?.Instructions.Count == 2); return false; } } // Block beforeThrowBlock { // [before throw] // stloc typedExceptionVariable(isinst System.Exception(ldloc objectVariable)) // if (comp.o(ldloc typedExceptionVariable != ldnull)) br captureBlock // br throwBlock // } // // Block throwBlock { // throw(ldloc objectVariable) // } // // Block captureBlock { // callvirt Throw(call Capture(ldloc typedExceptionVariable)) // br nextBlock // } // => // throw(ldloc result.Handler.Variable) internal static bool MatchExceptionCaptureBlock(ILTransformContext context, Block block, ref ILVariable objectVariable, out StLoc typedExceptionVariableStore, out Block captureBlock, out Block throwBlock) { bool DerivesFromException(IType t) => t.GetAllBaseTypes().Any(ty => ty.IsKnownType(KnownTypeCode.Exception)); captureBlock = null; throwBlock = null; typedExceptionVariableStore = null; var typedExceptionVariableStLoc = block.Instructions.ElementAtOrDefault(block.Instructions.Count - 3) as StLoc; if (typedExceptionVariableStLoc == null || !typedExceptionVariableStLoc.Value.MatchIsInst(out var arg, out var type) || !DerivesFromException(type) || !arg.MatchLdLoc(out var v)) { return false; } if (objectVariable == null) { objectVariable = v; } else if (!objectVariable.Equals(v)) { return false; } typedExceptionVariableStore = typedExceptionVariableStLoc; if (!block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst)) return false; ILInstruction lastInstr = block.Instructions.Last(); if (!lastInstr.MatchBranch(out throwBlock)) return false; if (condition.MatchCompNotEqualsNull(out arg) && trueInst is Branch branchToCapture) { if (!arg.MatchLdLoc(typedExceptionVariableStore.Variable)) return false; captureBlock = branchToCapture.TargetBlock; } else { return false; } if (throwBlock.IncomingEdgeCount != 1 || throwBlock.Instructions.Count != 1 || !(throwBlock.Instructions[0].MatchThrow(out var ov) && ov.MatchLdLoc(objectVariable))) { return false; } if (captureBlock.IncomingEdgeCount != 1 || captureBlock.Instructions.Count != 2 || !MatchCaptureThrowCalls(captureBlock.Instructions[0])) { return false; } return true; bool MatchCaptureThrowCalls(ILInstruction inst) { var exceptionDispatchInfoType = context.TypeSystem.FindType(typeof(System.Runtime.ExceptionServices.ExceptionDispatchInfo)); if (inst is not CallVirt callVirt || callVirt.Arguments.Count != 1) return false; if (callVirt.Arguments[0] is not Call call || call.Arguments.Count != 1 || !call.Arguments[0].MatchLdLoc(typedExceptionVariableStLoc.Variable)) { return false; } return callVirt.Method.Name == "Throw" && callVirt.Method.DeclaringType.Equals(exceptionDispatchInfoType) && call.Method.Name == "Capture" && call.Method.DeclaringType.Equals(exceptionDispatchInfoType); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs ================================================ // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.ControlFlow { class AwaitInFinallyTransform { public static void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AwaitInCatchFinally) return; bool needsUnreachableCodeCleanup = false; // analyze all try-catch statements in the function foreach (var tryCatch in function.Descendants.OfType().ToArray()) { if (!(tryCatch.Parent?.Parent is BlockContainer container)) continue; // } catch exceptionVariable : 02000078 System.Object when (ldc.i4 1) BlockContainer { // Block IL_004a (incoming: 1) { // stloc objectVariable(ldloc exceptionVariable) // br finallyBlock // } // // } // } // // Block finallyBlock (incoming: 2) { // if (comp.o(ldloc b == ldnull)) br afterFinallyBlock // br finallyBlockContinuation // } // // Block finallyBlockContinuation (incoming: 1) { // await(addressof System.Threading.Tasks.ValueTask(callvirt DisposeAsync(ldloc b))) // br afterFinallyBlock // } // // Block afterFinallyBlock (incoming: 2) { // stloc V_1(ldloc objectVariable) // if (comp.o(ldloc V_1 == ldnull)) br IL_00ea // br IL_00cf // } // await in finally uses a single catch block with catch-type object if (tryCatch.Handlers.Count != 1) { continue; } var handler = tryCatch.Handlers[0]; var exceptionVariable = handler.Variable; if (handler.Body is not BlockContainer catchBlockContainer) continue; if (!exceptionVariable.Type.IsKnownType(KnownTypeCode.Object)) continue; // Matches the await finally pattern: // [stloc V_3(ldloc E_100) - copy exception variable to a temporary] // stloc V_6(ldloc V_3) - store exception in 'global' object variable // br IL_0075 - jump out of catch block to the head of the finallyBlock var catchBlockEntry = catchBlockContainer.EntryPoint; ILVariable objectVariable; switch (catchBlockEntry.Instructions.Count) { case 2: if (!catchBlockEntry.Instructions[0].MatchStLoc(out objectVariable, out var value)) continue; if (!value.MatchLdLoc(exceptionVariable)) continue; break; case 3: if (!catchBlockEntry.Instructions[0].MatchStLoc(out var temporaryVariable, out value)) continue; if (!value.MatchLdLoc(exceptionVariable)) continue; if (!catchBlockEntry.Instructions[1].MatchStLoc(out objectVariable, out value)) continue; if (!value.MatchLdLoc(temporaryVariable)) continue; break; default: continue; } if (!catchBlockEntry.Instructions[catchBlockEntry.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally)) continue; // globalCopyVar should only be used once, at the end of the finally-block if (objectVariable.LoadCount != 1 || objectVariable.StoreCount > 2) continue; var beforeExceptionCaptureBlock = Block.FindClosestBlock(objectVariable.LoadInstructions[0]); if (beforeExceptionCaptureBlock == null) continue; var (noThrowBlock, exceptionCaptureBlock, objectVariableCopy) = FindBlockAfterFinally(context, beforeExceptionCaptureBlock, objectVariable); if (noThrowBlock == null || exceptionCaptureBlock == null) continue; var initOfStateVariable = tryCatch.Parent.Children.ElementAtOrDefault(tryCatch.ChildIndex - 1) as StLoc; if (initOfStateVariable == null || !initOfStateVariable.Value.MatchLdcI4(0)) continue; var stateVariable = initOfStateVariable.Variable; if (!ValidateStateVariable(stateVariable, initOfStateVariable, tryCatch, entryPointOfFinally)) continue; StateRangeAnalysis sra = new StateRangeAnalysis(StateRangeAnalysisMode.AwaitInFinally, null, stateVariable); sra.AssignStateRanges(noThrowBlock, Util.LongSet.Universe); var mapping = sra.GetBlockStateSetMapping((BlockContainer)noThrowBlock.Parent); var mappingForLeave = sra.GetBlockStateSetMappingForLeave(); context.StepStartGroup("Inline finally block with await", tryCatch.Handlers[0]); var cfg = new ControlFlowGraph(container, context.CancellationToken); needsUnreachableCodeCleanup = true; var finallyContainer = new BlockContainer().WithILRange(catchBlockContainer); tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock)); context.Step("Move blocks into finally", finallyContainer); MoveDominatedBlocksToContainer(entryPointOfFinally, beforeExceptionCaptureBlock, cfg, finallyContainer); SimplifyEndOfFinally(context, objectVariable, beforeExceptionCaptureBlock, objectVariableCopy, finallyContainer); if (noThrowBlock.Instructions[0].MatchLdLoc(stateVariable)) { noThrowBlock.Instructions.RemoveAt(0); } foreach (var branch in tryCatch.TryBlock.Descendants.OfType()) { if (branch.TargetBlock == entryPointOfFinally) { if (!(branch.Parent is Block block && branch.ChildIndex > 0 && block.Instructions[branch.ChildIndex - 1].MatchStLoc(stateVariable, out var v) && v.MatchLdcI4(out int value))) { value = 0; } if (mapping.TryGetValue(value, out Block targetBlock)) { context.Step($"branch to finally with state {value} => branch to state target " + targetBlock.Label, branch); branch.TargetBlock = targetBlock; } else if (mappingForLeave.TryGetValue(value, out BlockContainer targetContainer)) { context.Step($"branch to finally with state {value} => leave to state target " + targetContainer, branch); branch.ReplaceWith(new Leave(targetContainer).WithILRange(branch)); } else { context.Step("branch to finally => branch after finally", branch); branch.TargetBlock = noThrowBlock; } } else { Debug.Assert(branch.TargetBlock.IsDescendantOf(tryCatch.TryBlock)); } } context.StepEndGroup(keepIfEmpty: true); } context.Step("Clean up", function); if (needsUnreachableCodeCleanup) { // Cleaning up only the modified containers is insufficient, deleting blocks in // any container can also cause other blocks in parent containers to become unreachable. // So we just clean up everything. foreach (var container in function.Body.Descendants.OfType()) { container.SortBlocks(deleteUnreachableBlocks: true); } } void MoveDominatedBlocksToContainer(Block newEntryPoint, Block endBlock, ControlFlowGraph graph, BlockContainer targetContainer) { var node = graph.GetNode(newEntryPoint); var endNode = endBlock == null ? null : graph.GetNode(endBlock); MoveBlock(newEntryPoint, targetContainer); foreach (var n in graph.cfg) { Block block = (Block)n.UserData; if (node.Dominates(n)) { if (endNode != null && endNode != n && endNode.Dominates(n)) continue; if (block.Parent == targetContainer) continue; MoveBlock(block, targetContainer); } } } void MoveBlock(Block block, BlockContainer target) { context.Step($"Move {block.Label} to container at IL_{target.StartILOffset:x4}", target); block.Remove(); target.Blocks.Add(block); } static void SimplifyEndOfFinally(ILTransformContext context, ILVariable objectVariable, Block beforeExceptionCaptureBlock, ILVariable objectVariableCopy, BlockContainer finallyContainer) { if (beforeExceptionCaptureBlock.Instructions.Count >= 3 && beforeExceptionCaptureBlock.Instructions.SecondToLastOrDefault().MatchIfInstruction(out var cond, out var brInst) && beforeExceptionCaptureBlock.Instructions.LastOrDefault() is Branch branch && beforeExceptionCaptureBlock.Instructions[beforeExceptionCaptureBlock.Instructions.Count - 3].MatchStLoc(objectVariableCopy, out var value) && value.MatchLdLoc(objectVariable)) { if (cond.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(objectVariableCopy)) { context.Step("Simplify end of finally", beforeExceptionCaptureBlock); beforeExceptionCaptureBlock.Instructions.RemoveRange(beforeExceptionCaptureBlock.Instructions.Count - 3, 2); branch.ReplaceWith(new Leave(finallyContainer).WithILRange(branch)); } else if (cond.MatchCompNotEqualsNull(out arg) && arg.MatchLdLoc(objectVariableCopy)) { context.Step("Simplify end of finally", beforeExceptionCaptureBlock); beforeExceptionCaptureBlock.Instructions.RemoveRange(beforeExceptionCaptureBlock.Instructions.Count - 3, 2); branch.ReplaceWith(new Leave(finallyContainer).WithILRange(branch)); } else { Debug.Fail("Broken beforeExceptionCaptureBlock"); } } else { Debug.Fail("Broken beforeExceptionCaptureBlock"); } } } private static bool ValidateStateVariable(ILVariable stateVariable, StLoc initializer, TryCatch tryCatch, Block entryPointOfFinally) { if (stateVariable.AddressCount > 0) return false; foreach (var store in stateVariable.StoreInstructions) { if (store == initializer) continue; if (store is not StLoc stloc) return false; if (!stloc.Value.MatchLdcI4(out _)) return false; if (!stloc.IsDescendantOf(tryCatch)) return false; if (stloc.Parent is not Block block) return false; if (block.Instructions.ElementAtOrDefault(stloc.ChildIndex + 1)?.MatchBranch(entryPointOfFinally) != true) return false; } return true; } static (Block, Block, ILVariable) FindBlockAfterFinally(ILTransformContext context, Block block, ILVariable objectVariable) { // Block IL_0327 (incoming: 2) { // stloc V_7(ldloc I_0) // if (comp.o(ldloc V_7 == ldnull)) br IL_034a // br IL_0333 // } int count = block.Instructions.Count; if (count < 3) return default; if (!block.Instructions[count - 3].MatchStLoc(out var objectVariableCopy, out var value)) return default; if (!value.MatchLdLoc(objectVariable)) return default; if (!block.Instructions[count - 2].MatchIfInstruction(out var cond, out var noThrowBlockBranch)) return default; if (!noThrowBlockBranch.MatchBranch(out var noThrowBlock)) return default; if (!block.Instructions[count - 1].MatchBranch(out var exceptionCaptureBlock)) return default; if (cond.MatchCompEqualsNull(out var arg)) { if (!arg.MatchLdLoc(objectVariableCopy)) return default; } else if (cond.MatchCompNotEqualsNull(out arg)) { if (!arg.MatchLdLoc(objectVariableCopy)) return default; (noThrowBlock, exceptionCaptureBlock) = (exceptionCaptureBlock, noThrowBlock); } else { return default; } if (!AwaitInCatchTransform.MatchExceptionCaptureBlock(context, exceptionCaptureBlock, ref objectVariableCopy, out _, out _, out _)) { return default; } return (noThrowBlock, exceptionCaptureBlock, objectVariableCopy); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Detects 'if' structure and other non-loop aspects of control flow. /// /// /// Order dependency: should run after loop detection. /// Blocks should be basic blocks prior to this transform. /// After this transform, they will be extended basic blocks. /// public class ConditionDetection : IBlockTransform { private enum Keyword { Break, Return, Continue, Other } private BlockTransformContext context; private ControlFlowNode cfgNode; private BlockContainer currentContainer; /// /// Builds structured control flow for the block associated with the control flow node. /// /// /// After a block was processed, it should use structured control flow /// and have just a single 'regular' exit point (last branch instruction in the block) /// public void Run(Block block, BlockTransformContext context) { this.context = context; currentContainer = (BlockContainer)block.Parent; // We only embed blocks into this block if they aren't referenced anywhere else, // so those blocks are dominated by this block. // BlockILTransform thus guarantees that the blocks being embedded are already // fully processed. cfgNode = context.ControlFlowNode; Debug.Assert(cfgNode.UserData == block); // Because this transform runs at the beginning of the block transforms, // we know that `block` is still a (non-extended) basic block. // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: if (block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst) HandleIfInstruction(block, ifInst); else InlineExitBranch(block); } /// /// Repeatedly inlines and simplifies, maintaining a good block exit and then attempting to match IL order /// private void HandleIfInstruction(Block block, IfInstruction ifInst) { while (InlineTrueBranch(block, ifInst) || InlineExitBranch(block)) { PickBetterBlockExit(block, ifInst); MergeCommonBranches(block, ifInst); SwapEmptyThen(ifInst); IntroduceShortCircuit(ifInst); } PickBetterBlockExit(block, ifInst); OrderIfBlocks(ifInst); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; } /// /// if (...) br trueBlock; /// -> /// if (...) { trueBlock... } /// /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1) /// private bool InlineTrueBranch(Block block, IfInstruction ifInst) { if (!CanInline(ifInst.TrueInst)) { if (block.Instructions.SecondToLastOrDefault() == ifInst && ifInst.FalseInst.MatchNop()) { var exitInst = block.Instructions.Last(); if (DetectExitPoints.CompatibleExitInstruction(ifInst.TrueInst, exitInst)) { // if (...) exitInst; exitInst; context.Step("Use empty block as then-branch", ifInst.TrueInst); ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst); // false, because we didn't inline a real block // this will cause HandleIfInstruction() to attempt to inline the exitInst. return false; } } return false; } context.Step("Inline block as then-branch", ifInst.TrueInst); // The targetBlock was already processed, and is ready to embed var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock; targetBlock.AddRef(); // Peformance: avoid temporarily disconnecting targetBlock targetBlock.Remove(); ifInst.TrueInst = targetBlock; targetBlock.ReleaseRef(); return true; } /// /// ...; br nextBlock; /// -> /// ...; { nextBlock... } /// /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1) /// private bool InlineExitBranch(Block block) { var exitInst = GetExit(block); if (!CanInline(exitInst)) return false; context.Step("Inline target block of unconditional branch", exitInst); // The targetBlock was already processed, and is ready to embed var targetBlock = ((Branch)exitInst).TargetBlock; block.Instructions.RemoveAt(block.Instructions.Count - 1); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Remove(); return true; } /// /// Gets whether potentialBranchInstruction is a branch to a block that is dominated by cfgNode. /// If this function returns true, we replace the branch instruction with the block itself. /// private bool CanInline(ILInstruction exitInst) { if (exitInst is Branch branch && branch.TargetBlock.Parent == currentContainer && branch.TargetBlock.IncomingEdgeCount == 1) { // if the incoming edge count is 1, then this must be the sole branch, and dominance is already ensured Debug.Assert(cfgNode.Dominates(context.ControlFlowGraph.GetNode(branch.TargetBlock))); // can't have "final instructions" in control flow blocks Debug.Assert(branch.TargetBlock.FinalInstruction is Nop); return true; } return false; } /// /// Looks for common exits in the inlined then and else branches of an if instruction /// and performs inversions and simplifications to merge them provided they don't /// isolate a higher priority block exit /// private void MergeCommonBranches(Block block, IfInstruction ifInst) { var thenExits = new List(); AddExits(ifInst.TrueInst, 0, thenExits); if (thenExits.Count == 0) return; // if there are any exits from the then branch, then the else is redundant and shouldn't exist Debug.Assert(IsEmpty(ifInst.FalseInst)); Debug.Assert(ifInst.Parent == block); var elseExits = new List(); int falseInstIndex = block.Instructions.IndexOf(ifInst) + 1; AddExits(block, falseInstIndex, elseExits); var commonExits = elseExits.Where(e1 => thenExits.Any(e2 => DetectExitPoints.CompatibleExitInstruction(e1, e2))); // find the common exit with the highest block exit priority ILInstruction commonExit = null; foreach (var exit in commonExits) { if (commonExit == null || CompareBlockExitPriority(exit, commonExit) > 0) commonExit = exit; } if (commonExit == null) return; // if the current block exit has higher priority than the exits to merge, // determine if this merge will isolate the current block exit // that is, no sequence of inversions can restore it to the block exit position var blockExit = block.Instructions.Last(); if (CompareBlockExitPriority(blockExit, commonExit, true) > 0 && !WillShortCircuit(block, ifInst, commonExit)) return; // could improve performance by directly implementing the || short-circuit when WillShortCircuit // currently the same general sequence of transformations introduces both operators context.StepStartGroup("Merge common branches " + commonExit, ifInst); ProduceExit(ifInst.TrueInst, 0, commonExit); ProduceExit(block, falseInstIndex, commonExit); // if (...) { ...; blockExit; } ...; blockExit; // -> if (...) { ...; blockExit; } else { ... } blockExit; if (ifInst != block.Instructions.SecondToLastOrDefault()) { context.Step("Embed else-block for goto removal", ifInst); Debug.Assert(IsEmpty(ifInst.FalseInst)); Block newBlock = new Block(); ifInst.FalseInst = newBlock; ExtractBlock(block, block.Instructions.IndexOf(ifInst) + 1, block.Instructions.Count - 1, newBlock); } // if (...) { ...; goto blockExit; } blockExit; // -> if (...) { ... } blockExit; // OR // if (...) { ...; goto blockExit; } else { ... } blockExit; // -> if (...) { ... } else { ... } blockExit; context.Step("Remove redundant 'goto blockExit;' in then-branch", ifInst); if (!(ifInst.TrueInst is Block trueBlock) || trueBlock.Instructions.Count == 1) ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst); else trueBlock.Instructions.RemoveAt(trueBlock.Instructions.Count - 1); context.StepEndGroup(); } /// /// Finds all exits which could be brought to the block root via inversion /// private void AddExits(ILInstruction searchInst, int startIndex, IList exits) { if (!TryGetExit(searchInst, out var exitInst)) return; exits.Add(exitInst); if (searchInst is Block block) { for (int i = startIndex; i < block.Instructions.Count; i++) { if (block.Instructions[i] is IfInstruction ifInst) AddExits(ifInst.TrueInst, 0, exits); } } } /// /// Recursively performs inversions to bring a desired exit to the root of the block /// for example: /// if (a) { /// ...; /// if (b) { /// ...; /// targetExit; /// } /// ...; /// exit1; /// } /// ...; /// exit2; /// -> /// if (!a) { /// ...; /// exit2; /// } /// ...; /// if (!b) { /// ...; /// exit1; /// } /// ...; /// targetExit; /// private bool ProduceExit(ILInstruction searchInst, int startIndex, ILInstruction targetExit) { if (!TryGetExit(searchInst, out var exitInst)) return false; if (DetectExitPoints.CompatibleExitInstruction(exitInst, targetExit)) return true; if (searchInst is Block block) { for (int i = startIndex; i < block.Instructions.Count; i++) { if (block.Instructions[i] is IfInstruction ifInst && ProduceExit(ifInst.TrueInst, 0, targetExit)) { InvertIf(block, ifInst); Debug.Assert(DetectExitPoints.CompatibleExitInstruction(GetExit(block), targetExit)); return true; } } } return false; } /// /// Anticipates the introduction of an || operator when merging ifInst and elseExit /// /// if (cond) commonExit; /// if (cond2) commonExit; /// ...; /// blockExit; /// will become: /// if (cond || cond2) commonExit; /// ...; /// blockExit; /// private bool WillShortCircuit(Block block, IfInstruction ifInst, ILInstruction elseExit) { bool ThenInstIsSingleExit(ILInstruction inst) => inst.MatchIfInstruction(out var _, out var trueInst) && (!(trueInst is Block trueBlock) || trueBlock.Instructions.Count == 1) && TryGetExit(trueInst, out var _); if (!ThenInstIsSingleExit(ifInst)) return false; // find the host if statement var elseIfInst = elseExit; while (elseIfInst.Parent != block) { elseIfInst = elseIfInst.Parent; } return block.Instructions.IndexOf(elseIfInst) == block.Instructions.IndexOf(ifInst) + 1 && ThenInstIsSingleExit(elseIfInst); } private void InvertIf(Block block, IfInstruction ifInst) => InvertIf(block, ifInst, context); /// /// if (cond) { then... } /// else...; /// exit; /// -> /// if (!cond) { else...; exit } /// then...; /// /// Assumes ifInst does not have an else block /// internal static void InvertIf(Block block, IfInstruction ifInst, ILTransformContext context) { Debug.Assert(ifInst.Parent == block); //assert then block terminates var exitInst = GetExit(block); context.Step($"InvertIf at IL_{ifInst.StartILOffset:x4}", ifInst); //if the then block terminates, else blocks are redundant, and should not exist Debug.Assert(IsEmpty(ifInst.FalseInst)); //save a copy var thenInst = ifInst.TrueInst; thenInst.AddRef(); if (ifInst != block.Instructions.SecondToLastOrDefault()) { // extract "else...; exit". // Note that this will only extract instructions that were previously inlined from another block // (via InlineExitBranch), so the instructions are already fully-transformed. // So it's OK to move them into a nested block again (which hides them from the following block transforms). var newBlock = new Block(); ifInst.TrueInst = newBlock; ExtractBlock(block, block.Instructions.IndexOf(ifInst) + 1, block.Instructions.Count, newBlock); } else { ifInst.TrueInst = exitInst; block.Instructions.RemoveAt(block.Instructions.Count - 1); } if (thenInst is Block thenBlock) { block.Instructions.AddRange(thenBlock.Instructions); } else { block.Instructions.Add(thenInst); } thenInst.ReleaseRef(); ifInst.Condition = Comp.LogicNot(ifInst.Condition); ExpressionTransforms.RunOnSingleStatement(ifInst, context); } /// /// if (cond) { } else { ... } /// -> /// if (!cond) { ... } /// private void SwapEmptyThen(IfInstruction ifInst) { if (!IsEmpty(ifInst.TrueInst)) return; context.Step("Swap empty then-branch with else-branch", ifInst); var oldTrue = ifInst.TrueInst; ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = new Nop().WithILRange(oldTrue); ifInst.Condition = Comp.LogicNot(ifInst.Condition); } /// /// if (cond) { if (nestedCond) { nestedThen... } } /// -> /// if (cond && nestedCond) { nestedThen... } /// private void IntroduceShortCircuit(IfInstruction ifInst) { if (IsEmpty(ifInst.FalseInst) && ifInst.TrueInst is Block trueBlock && trueBlock.Instructions.Count == 1 && trueBlock.FinalInstruction is Nop && trueBlock.Instructions[0].MatchIfInstruction(out var nestedCondition, out var nestedTrueInst)) { context.Step("Combine 'if (cond1 && cond2)' in then-branch", ifInst); ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition); ifInst.TrueInst = nestedTrueInst; } } /// /// if (cond) { lateBlock... } else { earlyBlock... } /// -> /// if (!cond) { earlyBlock... } else { lateBlock... } /// private void OrderIfBlocks(IfInstruction ifInst) { if (IsEmpty(ifInst.FalseInst) || GetStartILOffset(ifInst.TrueInst, out _) <= GetStartILOffset(ifInst.FalseInst, out _)) return; context.Step("Swap then-branch with else-branch to match IL order", ifInst); var oldTrue = ifInst.TrueInst; oldTrue.AddRef(); // Peformance: avoid temporarily disconnecting oldTrue ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = oldTrue; oldTrue.ReleaseRef(); ifInst.Condition = Comp.LogicNot(ifInst.Condition); } public static int GetStartILOffset(ILInstruction inst, out bool isEmpty) { // some compilers merge the leave instructions for different arguments using stack variables // these get split and inlined, but the ILRange of the value remains a better indicator of the actual location if (inst is Leave leave && !leave.Value.MatchNop()) { isEmpty = leave.Value.ILRangeIsEmpty; return leave.Value.StartILOffset; } isEmpty = inst.ILRangeIsEmpty; return inst.StartILOffset; } /// /// Compares the current block exit, and the exit of ifInst.ThenInst /// and inverts if necessary to pick the better exit /// /// Does nothing when ifInst has an else block (because inverting wouldn't affect the block exit) /// private void PickBetterBlockExit(Block block, IfInstruction ifInst) { var exitInst = GetExit(block); if (IsEmpty(ifInst.FalseInst) && TryGetExit(ifInst.TrueInst, out var trueExitInst) && CompareBlockExitPriority(trueExitInst, exitInst) > 0) InvertIf(block, ifInst); } /// /// Compares two exit instructions for block exit priority /// A higher priority exit should be kept as the last instruction in a block /// even if it prevents the merging of a two compatible lower priority exits /// /// leave from try containers must always be the final instruction, or a goto will be inserted /// loops will endeavour to leave at least one continue branch as the last instruction in the block /// /// The priority is: /// leave > branch > other-keyword > continue > return > break /// /// non-keyword leave instructions are ordered with the outer container having higher priority /// /// if the exits have equal priority, and the strongly flag is not provided /// then the exits are sorted by IL order (target block for branches) /// /// break has higher priority than other keywords in a switch block (for aesthetic reasons) /// /// {-1, 0, 1} if exit1 has {lower, equal, higher} priority an exit2 private int CompareBlockExitPriority(ILInstruction exit1, ILInstruction exit2, bool strongly = false) { // keywords have lower priority than non-keywords bool isKeyword1 = IsKeywordExit(exit1, out var keyword1); bool isKeyword2 = IsKeywordExit(exit2, out var keyword2); if (isKeyword1 != isKeyword2) return isKeyword1 ? -1 : 1; if (isKeyword1) { //for keywords if (currentContainer.Kind == ContainerKind.Switch) { // breaks have highest priority in a switch if ((keyword1 == Keyword.Break) != (keyword2 == Keyword.Break)) return keyword1 == Keyword.Break ? 1 : -1; } else { // breaks have lowest priority if ((keyword1 == Keyword.Break) != (keyword2 == Keyword.Break)) return keyword1 == Keyword.Break ? -1 : 1; // continue has highest priority (to prevent having to jump to the end of a loop block) if ((keyword1 == Keyword.Continue) != (keyword2 == Keyword.Continue)) return keyword1 == Keyword.Continue ? 1 : -1; } } else {// for non-keywords (only Branch or Leave) // branches have lower priority than non-keyword leaves bool isBranch1 = exit1 is Branch; bool isBranch2 = exit2 is Branch; if (isBranch1 != isBranch2) return isBranch1 ? -1 : 1; // two leaves that both want end of block priority if (exit1.MatchLeave(out var container1) && exit2.MatchLeave(out var container2) && container1 != container2) { // choose the outer one return container2.IsDescendantOf(container1) ? 1 : -1; } } if (strongly) return 0; // prefer arranging stuff in IL order if (exit1.MatchBranch(out var block1) && exit2.MatchBranch(out var block2)) return block1.StartILOffset.CompareTo(block2.StartILOffset); // use the IL offsets of the arguments of leave instructions instead of the leaves themselves if possible if (exit1.MatchLeave(out var _, out var arg1) && exit2.MatchLeave(out var _, out var arg2)) return arg1.StartILOffset.CompareTo(arg2.StartILOffset); return exit1.StartILOffset.CompareTo(exit2.StartILOffset); } /// /// Determines if an exit instruction has a corresponding keyword and thus doesn't strictly need merging /// Branches can be 'continue' or goto (non-keyword) /// Leave can be 'return', 'break' or a pinned container exit (try/using/lock etc) /// All other instructions (throw, using, etc) are returned as Keyword.Other /// private bool IsKeywordExit(ILInstruction exitInst, out Keyword keyword) { keyword = Keyword.Other; switch (exitInst) { case Branch branch: if (IsContinueBlock(branch.TargetContainer, branch.TargetBlock)) { keyword = Keyword.Continue; return true; } return false; case Leave leave: if (leave.IsLeavingFunction) { keyword = Keyword.Return; return true; } if (leave.TargetContainer.Kind != ContainerKind.Normal) { keyword = Keyword.Break; return true; } return false; default: return true; } } /// /// Determine if the specified instruction necessarily exits (EndPointUnreachable) /// and if so return last (or single) exit instruction /// private static bool TryGetExit(ILInstruction inst, out ILInstruction exitInst) { if (inst is Block block && block.Instructions.Count > 0) inst = block.Instructions.Last(); if (inst.HasFlag(InstructionFlags.EndPointUnreachable)) { exitInst = inst; return true; } exitInst = null; return false; } /// /// Gets the final instruction from a block (or a single instruction) assuming that all blocks /// or instructions in this position have unreachable endpoints /// private static ILInstruction GetExit(ILInstruction inst) { ILInstruction exitInst = inst is Block block ? block.Instructions.Last() : inst; // Last instruction is one with unreachable endpoint // (guaranteed by combination of BlockContainer and Block invariants) Debug.Assert(exitInst.HasFlag(InstructionFlags.EndPointUnreachable)); return exitInst; } /// /// Returns true if inst is Nop or a Block with no instructions. /// private static bool IsEmpty(ILInstruction inst) => inst is Nop || inst is Block block && block.Instructions.Count == 0 && block.FinalInstruction is Nop; /// /// Import some pattern matching from HighLevelLoopTransform to guess the continue block of loop containers. /// Used to identify branches targetting this block as continue statements, for ordering priority. /// /// private static bool IsContinueBlock(BlockContainer container, Block block) { if (container.Kind != ContainerKind.Loop) return false; // increment blocks have exactly 2 incoming edges if (container.EntryPoint.IncomingEdgeCount == 2) { var forIncrement = HighLevelLoopTransform.GetIncrementBlock(container, container.EntryPoint); if (forIncrement != null) return block == forIncrement; } return block == container.EntryPoint; } /// /// Removes a subrange of instructions from a block and returns them in a new Block /// internal static void ExtractBlock(Block block, int startIndex, int endIndex, Block extractedBlock) { for (int i = startIndex; i < endIndex; i++) { var inst = block.Instructions[i]; extractedBlock.Instructions.Add(inst); extractedBlock.AddILRange(inst); } block.Instructions.RemoveRange(startIndex, endIndex - startIndex); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs ================================================ using System.Collections.Generic; using System.Diagnostics; using System.Threading; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Holds the control flow graph. /// A separate graph is computed for each BlockContainer at the start of the block transforms /// (before loop detection). /// public class ControlFlowGraph { readonly BlockContainer container; /// /// The container for which the ControlFlowGraph was created. /// /// This may differ from the container currently holding a block, /// because a transform could have moved the block since the CFG was created. /// public BlockContainer Container { get { return container; } } /// /// Nodes array, indexed by original block index. /// /// Originally cfg[i].UserData == container.Blocks[i], /// but the ILAst blocks may be moved/reordered by transforms. /// internal readonly ControlFlowNode[] cfg; /// public IReadOnlyList Nodes => cfg; /// /// Dictionary from Block to ControlFlowNode. /// Unlike the cfg array, this can be used to discover control flow nodes even after /// blocks were moved/reordered by transforms. /// readonly Dictionary dict = new Dictionary(); /// /// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a /// branch/leave instruction leaving the container. /// readonly BitSet nodeHasDirectExitOutOfContainer; /// /// nodeHasReachableExit[i] == true iff there is a path from cfg[i] to a node not dominated by cfg[i], /// or if there is a path from cfg[i] to a branch/leave instruction leaving the container. /// readonly BitSet nodeHasReachableExit; /// /// Constructs a control flow graph for the blocks in the given block container. /// /// Return statements, exceptions, or branches leaving the block container are not /// modeled by the control flow graph. /// public ControlFlowGraph(BlockContainer container, CancellationToken cancellationToken = default(CancellationToken)) { this.container = container; this.cfg = new ControlFlowNode[container.Blocks.Count]; this.nodeHasDirectExitOutOfContainer = new BitSet(cfg.Length); for (int i = 0; i < cfg.Length; i++) { Block block = container.Blocks[i]; cfg[i] = new ControlFlowNode { UserIndex = i, UserData = block }; dict.Add(block, cfg[i]); } CreateEdges(cancellationToken); Dominance.ComputeDominance(cfg[0], cancellationToken); this.nodeHasReachableExit = Dominance.MarkNodesWithReachableExits(cfg); this.nodeHasReachableExit.UnionWith(FindNodesWithExitsOutOfContainer()); } void CreateEdges(CancellationToken cancellationToken) { for (int i = 0; i < container.Blocks.Count; i++) { cancellationToken.ThrowIfCancellationRequested(); var block = container.Blocks[i]; var sourceNode = cfg[i]; foreach (var node in block.Descendants) { if (node is Branch branch) { if (branch.TargetBlock.Parent == container) { sourceNode.AddEdgeTo(cfg[container.Blocks.IndexOf(branch.TargetBlock)]); } else if (branch.TargetBlock.IsDescendantOf(container)) { // Internal control flow within a nested container. } else { // Branch out of this container into a parent container. // Like return statements and exceptional exits, // we ignore this for the CFG and the dominance calculation. // However, it's relevant for HasReachableExit(). nodeHasDirectExitOutOfContainer.Set(i); } } else if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) { // Leave instructions (like other exits out of the container) // are ignored for the CFG and dominance, // but is relevant for HasReachableExit(). // However, a 'leave' that exits the whole function represents a return, // and is not considered a reachable exit. if (!leave.IsLeavingFunction) { nodeHasDirectExitOutOfContainer.Set(i); } } } } } BitSet FindNodesWithExitsOutOfContainer() { // Also mark the nodes that exit the block container altogether. // Invariant: leaving[n.UserIndex] == true implies leaving[n.ImmediateDominator.UserIndex] == true var leaving = new BitSet(cfg.Length); foreach (var node in cfg) { if (leaving[node.UserIndex]) continue; if (nodeHasDirectExitOutOfContainer[node.UserIndex]) { for (ControlFlowNode p = node; p != null; p = p.ImmediateDominator) { if (leaving[p.UserIndex]) { // we can stop marking when we've reached an already-marked node break; } leaving.Set(p.UserIndex); } } } return leaving; } /// /// Gets the ControlFlowNode for the block. /// /// Precondition: the block belonged to the container at the start of the block transforms /// (when the control flow graph was created). /// public ControlFlowNode GetNode(Block block) { return dict[block]; } /// /// Returns true iff there is a control flow path from node to one of the following: /// * branch or leave instruction leaving this.Container /// * branch instruction within this container to another node that is not dominated by node. /// /// If this function returns false, the only way control flow can leave the set of nodes /// dominated by node is by executing a return or throw instruction. /// public bool HasReachableExit(ControlFlowNode node) { Debug.Assert(cfg[node.UserIndex] == node); return nodeHasReachableExit[node.UserIndex]; } /// /// Gets whether the control flow node directly contains a branch/leave instruction /// exiting the container. /// public bool HasDirectExitOutOfContainer(ControlFlowNode node) { Debug.Assert(cfg[node.UserIndex] == node); return nodeHasDirectExitOutOfContainer[node.UserIndex]; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// This transform 'optimizes' the control flow logic in the IL code: /// it replaces constructs that are generated by the C# compiler in debug mode /// with shorter constructs that are more straightforward to analyze. /// /// /// The transformations performed are: /// * 'nop' instructions are removed /// * branches that lead to other branches are replaced with branches that directly jump to the destination /// * branches that lead to a 'return block' are replaced with a return instruction /// * basic blocks are combined where possible /// public class ControlFlowSimplification : IILTransform { internal bool aggressivelyDuplicateReturnBlocks; public void Run(ILFunction function, ILTransformContext context) { foreach (var block in function.Descendants.OfType()) { context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); RemoveDeadStackStores(block, context); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() // starts duplicating return instructions. SwitchDetection.SimplifySwitchInstruction(block, context); } SimplifyBranchChains(function, context); CleanUpEmptyBlocks(function, context); } private static void RemoveNopInstructions(Block block) { // Move ILRanges of special nop instructions to the previous non-nop instruction. for (int i = block.Instructions.Count - 1; i > 0; i--) { if (block.Instructions[i] is Nop nop && nop.Kind == NopKind.Pop) { block.Instructions[i - 1].AddILRange(nop); } } // Remove 'nop' instructions block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); } private static void RemoveDeadStackStores(Block block, ILTransformContext context) { bool aggressive = context.Settings.RemoveDeadStores; // Previously copy propagation did this; // ideally the ILReader would already do this, // for now do this here (even though it's not control-flow related). for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (block.Instructions[i] is StLoc stloc && stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 0 && stloc.Variable.Kind == VariableKind.StackSlot) { context.Step($"Remove dead stack store {stloc.Variable.Name}", stloc); if (aggressive ? SemanticHelper.IsPure(stloc.Value.Flags) : IsSimple(stloc.Value)) { Debug.Assert(SemanticHelper.IsPure(stloc.Value.Flags)); block.Instructions.RemoveAt(i++); } else { stloc.Value.AddILRange(stloc); stloc.ReplaceWith(stloc.Value); } } } bool IsSimple(ILInstruction inst) { switch (inst.OpCode) { case OpCode.LdLoc: case OpCode.LdStr: // C# 1.0 compiler sometimes emits redundant ldstr in switch-on-string pattern return true; default: return false; } } } void InlineVariableInReturnBlock(Block block, ILTransformContext context) { // In debug mode, the C#-compiler generates 'return blocks' that // unnecessarily store the return value to a local and then load it again: // v = // ret(v) // (where 'v' has no other uses) // Simplify these to a simple `ret()` so that they match the release build version. // if (block.Instructions.Count == 2 && block.Instructions[1].MatchReturn(out ILInstruction value)) { var ret = (Leave)block.Instructions[1]; if (value.MatchLdLoc(out ILVariable v) && v.IsSingleDefinition && v.LoadCount == 1 && block.Instructions[0].MatchStLoc(v, out ILInstruction inst)) { context.Step("Inline variable in return block", block); inst.AddILRange(ret.Value); inst.AddILRange(block.Instructions[0]); ret.Value = inst; block.Instructions.RemoveAt(0); } } } void SimplifyBranchChains(ILFunction function, ILTransformContext context) { List<(Block Block, BlockContainer TargetContainer)> blocksToMove = new List<(Block, BlockContainer)>(); HashSet visitedBlocks = new HashSet(); foreach (var branch in function.Descendants.OfType()) { // Resolve chained branches to the final target: var targetBlock = branch.TargetBlock; visitedBlocks.Clear(); while (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].OpCode == OpCode.Branch) { if (!visitedBlocks.Add(targetBlock)) { // prevent infinite loop when branch chain is cyclic break; } context.Step("Simplify branch to branch", branch); var nextBranch = (Branch)targetBlock.Instructions[0]; branch.TargetBlock = nextBranch.TargetBlock; branch.AddILRange(nextBranch); if (targetBlock.IncomingEdgeCount == 0) targetBlock.Instructions.Clear(); // mark the block for deletion targetBlock = branch.TargetBlock; } if (IsBranchToReturnBlock(branch)) { if (aggressivelyDuplicateReturnBlocks) { // Replace branches to 'return blocks' with the return instruction context.Step("Replace branch to return with return", branch); branch.ReplaceWith(targetBlock.Instructions[0].Clone()); } else if (branch.TargetContainer != branch.Ancestors.OfType().First() && targetBlock.IncomingEdgeCount == 1) { // We don't want to always inline the return directly, because this // might force us to place the return within a loop, when it's better // placed outside. // But we do want to move the return block into the correct try-finally scope, // so that loop detection at least has the option to put it inside // the loop body. context.Step("Move return block into try block", branch); BlockContainer localContainer = branch.Ancestors.OfType().First(); blocksToMove.Add((targetBlock, localContainer)); } } else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0] is Leave leave && leave.Value.MatchNop()) { context.Step("Replace branch to leave with leave", branch); // Replace branches to 'leave' instruction with the leave instruction var leave2 = leave.Clone(); if (!branch.ILRangeIsEmpty) // use the ILRange of the branch if possible leave2.AddILRange(branch); branch.ReplaceWith(leave2); } if (targetBlock.IncomingEdgeCount == 0) targetBlock.Instructions.Clear(); // mark the block for deletion } foreach ((Block block, BlockContainer targetContainer) in blocksToMove) { block.Remove(); targetContainer.Blocks.Add(block); } } void CleanUpEmptyBlocks(ILFunction function, ILTransformContext context) { foreach (var container in function.Descendants.OfType()) { foreach (var block in container.Blocks) { if (block.Instructions.Count == 0) continue; // block is already marked for deletion while (CombineBlockWithNextBlock(container, block, context)) { // repeat combining blocks until it is no longer possible // (this loop terminates because a block is deleted in every iteration) } } // Remove return blocks that are no longer reachable: container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0 && b.Instructions.Count == 0); } } bool IsBranchToReturnBlock(Branch branch) { var targetBlock = branch.TargetBlock; if (targetBlock.Instructions.Count != 1) return false; if (!targetBlock.Instructions[0].MatchReturn(out var value)) return false; if (!value.MatchLdLoc(out var returnVar)) return false; var container = branch.TargetContainer; for (ILInstruction inst = branch; inst != container; inst = inst.Parent) { if (inst.Parent is TryFinally tryFinally && inst.SlotInfo == TryFinally.TryBlockSlot) { // The branch will trigger the finally block. // Moving the return block into the try is only possible if the finally block doesn't touch the return variable. if (returnVar.IsUsedWithin(tryFinally.FinallyBlock)) return false; } } return true; } static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context) { Debug.Assert(container == block.Parent); // Ensure the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection. if (block.Instructions.Count > 1 && block.Instructions[block.Instructions.Count - 2].HasFlag(InstructionFlags.MayBranch)) return false; Branch br = block.Instructions.Last() as Branch; // Check whether we can combine the target block with this block if (br == null || br.TargetBlock.Parent != container || br.TargetBlock.IncomingEdgeCount != 1) return false; if (br.TargetBlock == block) return false; // don't inline block into itself context.Step("CombineBlockWithNextBlock", br); var targetBlock = br.TargetBlock; if (targetBlock.StartILOffset < block.StartILOffset && IsDeadTrueStore(block)) { // The C# compiler generates a dead store for the condition of while (true) loops. block.Instructions.RemoveAt(block.Instructions.Count - 2); } if (block.ILRangeIsEmpty) block.AddILRange(targetBlock); block.Instructions.Remove(br); block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Instructions.Clear(); // mark targetBlock for deletion return true; } /// /// Returns true if the last two instructions before the branch are storing the value 'true' into an unused variable. /// private static bool IsDeadTrueStore(Block block) { if (block.Instructions.Count < 2) return false; if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore)) return false; if (!(deadStore.Variable.LoadCount == 0 && deadStore.Variable.AddressCount == 0)) return false; return deadStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices.ComTypes; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// IL uses 'pinned locals' to prevent the GC from moving objects. /// /// C#: /// /// fixed (int* s = &arr[index]) { use(s); use(s); } /// /// /// Gets translated into IL: /// /// pinned local P : System.Int32& /// /// stloc(P, ldelema(arr, index)) /// call use(conv ref->i(ldloc P)) /// call use(conv ref->i(ldloc P)) /// stloc(P, conv i4->u(ldc.i4 0)) /// /// /// In C#, the only way to pin something is to use a fixed block /// (or to mess around with GCHandles). /// But fixed blocks are scoped, so we need to detect the region affected by the pin. /// To ensure we'll be able to collect all blocks in that region, we perform this transform /// early, before building any other control flow constructs that aren't as critical for correctness. /// /// This means this transform must run before LoopDetection. /// To make our detection job easier, we must run after variable inlining. /// public class DetectPinnedRegions : IILTransform { ILTransformContext context; public void Run(ILFunction function, ILTransformContext context) { this.context = context; foreach (var container in function.Descendants.OfType()) { context.CancellationToken.ThrowIfCancellationRequested(); DetectNullSafeArrayToPointerOrCustomRefPin(container); SplitBlocksAtWritesToPinnedLocals(container); foreach (var block in container.Blocks) DetectPinnedRegion(block); container.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks } // Sometimes there's leftover writes to the original pinned locals foreach (var block in function.Descendants.OfType()) { context.CancellationToken.ThrowIfCancellationRequested(); for (int i = 0; i < block.Instructions.Count; i++) { var stloc = block.Instructions[i] as StLoc; if (stloc != null && stloc.Variable.Kind == VariableKind.PinnedLocal && stloc.Variable.LoadCount == 0 && stloc.Variable.AddressCount == 0) { if (SemanticHelper.IsPure(stloc.Value.Flags)) { block.Instructions.RemoveAt(i--); } else { stloc.ReplaceWith(stloc.Value); } } } } this.context = null; } /// /// Ensures that every write to a pinned local is followed by a branch instruction. /// This ensures the 'pinning region' does not involve any half blocks, which makes it easier to extract. /// void SplitBlocksAtWritesToPinnedLocals(BlockContainer container) { for (int i = 0; i < container.Blocks.Count; i++) { var block = container.Blocks[i]; for (int j = 0; j < block.Instructions.Count - 1; j++) { var inst = block.Instructions[j]; if (inst.MatchStLoc(out ILVariable v, out var value) && v.Kind == VariableKind.PinnedLocal) { if (block.Instructions[j + 1].OpCode != OpCode.Branch) { // split block after j: context.Step("Split block after pinned local write", inst); var newBlock = new Block(); for (int k = j + 1; k < block.Instructions.Count; k++) { newBlock.Instructions.Add(block.Instructions[k]); } newBlock.AddILRange(newBlock.Instructions[0]); block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count); block.Instructions.Add(new Branch(newBlock)); container.Blocks.Insert(i + 1, newBlock); } // in case of re-pinning (e.g. C++/CLI assignment to pin_ptr variable), // it's possible for the new value to be dependent on the old. if (v.IsUsedWithin(value)) { // In this case, we need to un-inline the uses of the pinned local // so that they are split off into the block prior to the pinned local write var temp = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type); block.Instructions.Insert(j++, new StLoc(temp, new LdLoc(v))); foreach (var descendant in value.Descendants) { if (descendant.MatchLdLoc(v)) { descendant.ReplaceWith(new LdLoc(temp).WithILRange(descendant)); } } } if (j > 0) { // split block before j: context.Step("Split block before pinned local write", inst); var newBlock = new Block(); newBlock.Instructions.Add(block.Instructions[j]); newBlock.Instructions.Add(block.Instructions[j + 1]); newBlock.AddILRange(newBlock.Instructions[0]); Debug.Assert(block.Instructions.Count == j + 2); block.Instructions.RemoveRange(j, 2); block.Instructions.Insert(j, new Branch(newBlock)); container.Blocks.Insert(i + 1, newBlock); } } } } } #region null-safe array to pointer void DetectNullSafeArrayToPointerOrCustomRefPin(BlockContainer container) { bool modified = false; for (int i = 0; i < container.Blocks.Count; i++) { var block = container.Blocks[i]; if (IsNullSafeArrayToPointerPattern(block, out ILVariable v, out ILVariable p, out Block targetBlock)) { context.Step("NullSafeArrayToPointerPattern", block); ILInstruction arrayToPointer = new GetPinnableReference(new LdLoc(v), null); if (p.StackType != StackType.Ref) { arrayToPointer = new Conv(arrayToPointer, p.StackType.ToPrimitiveType(), false, Sign.None); } block.Instructions[block.Instructions.Count - 2] = new StLoc(p, arrayToPointer) .WithILRange(block.Instructions[block.Instructions.Count - 2]); ((Branch)block.Instructions.Last()).TargetBlock = targetBlock; modified = true; } else if (IsCustomRefPinPattern(block, out ILInstruction ldlocMem, out var callGPR, out v, out var stlocPtr, out targetBlock, out var nullBlock, out var notNullBlock)) { context.Step("CustomRefPinPattern", block); ILInstruction gpr; if (context.Settings.PatternBasedFixedStatement) { gpr = new GetPinnableReference(ldlocMem, callGPR.Method); } else { gpr = new IfInstruction( condition: new Comp(ComparisonKind.Inequality, Sign.None, ldlocMem, new LdNull()), trueInst: callGPR, falseInst: new Conv(new LdcI4(0), PrimitiveType.Ref, checkForOverflow: false, inputSign: Sign.None) ); } block.Instructions[block.Instructions.Count - 2] = new StLoc(v, gpr) .WithILRange(block.Instructions[block.Instructions.Count - 2]); if (stlocPtr != null) { block.Instructions.Insert(block.Instructions.Count - 1, stlocPtr); } ((Branch)block.Instructions.Last()).TargetBlock = targetBlock; // clear out internal blocks that are now unreachable, so that // targetBlock.IncomingEdgeCount is accurate at this point. nullBlock?.Instructions.Clear(); notNullBlock.Instructions.Clear(); if (targetBlock.IncomingEdgeCount == 1 && targetBlock.Parent == block.Parent) { block.Instructions.RemoveLast(); block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Instructions.Clear(); if (stlocPtr != null) { ILInlining.InlineOneIfPossible(block, stlocPtr.ChildIndex, InliningOptions.None, context); } } modified = true; } } if (modified) { container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0); // remove blocks made unreachable } } // Detect the following pattern: // if (comp.o(ldloc mem != ldnull)) br notNullBlock // br nullBlock // } // // Block nullBlock (incoming: 1) { // stloc ptr(conv i4->u (ldc.i4 0)) // br targetBlock // } // // Block notNullBlock (incoming: 1) { // stloc V_1(call GetPinnableReference(ldloc mem)) // stloc ptr(conv ref->u (ldloc V_1)) // br targetBlock // } // It will be replaced with: // stloc V_1(get.pinnable.reference(ldloc mem)) // stloc ptr(conv ref->u (ldloc V_1)) // br targetBlock private bool IsCustomRefPinPattern(Block block, out ILInstruction ldlocMem, out CallInstruction callGPR, out ILVariable v, out StLoc ptrAssign, out Block targetBlock, out Block nullBlock, out Block notNullBlock) { ldlocMem = null; callGPR = null; v = null; ptrAssign = null; targetBlock = null; nullBlock = null; notNullBlock = null; // if (comp.o(ldloc mem != ldnull)) br on_not_null // br on_null if (!block.MatchIfAtEndOfBlock(out var ifCondition, out var trueInst, out var falseInst)) return false; if (!ifCondition.MatchCompNotEqualsNull(out ldlocMem)) { if (ifCondition.MatchCompEqualsNull(out ldlocMem)) { (trueInst, falseInst) = (falseInst, trueInst); } else { return false; } } if (!SemanticHelper.IsPure(ldlocMem.Flags)) return false; if (!trueInst.MatchBranch(out notNullBlock) || notNullBlock.Parent != block.Parent) return false; if (!falseInst.MatchBranch(out nullBlock) || nullBlock.Parent != block.Parent) return false; // Block notNullBlock (incoming: 1) { // stloc V_1(call GetPinnableReference(ldloc mem)) // stloc ptr(conv ref->u (ldloc V_1)) // br targetBlock // } if (notNullBlock.IncomingEdgeCount != 1) return false; if (notNullBlock.Instructions.Count < 2) return false; // stloc V_1(call GetPinnableReference(ldloc mem)) if (!notNullBlock.Instructions[0].MatchStLoc(out v, out var value)) return false; if (v.Kind != VariableKind.PinnedLocal) return false; callGPR = value as CallInstruction; if (callGPR == null || callGPR.Arguments.Count != 1) return false; if (callGPR.Method.Name != "GetPinnableReference") return false; if (!ldlocMem.Match(callGPR.Arguments[0]).Success) return false; // stloc ptr(conv ref->u (ldloc V_1)) ptrAssign = notNullBlock.Instructions[1] as StLoc; if (ptrAssign != null) { if (!ptrAssign.Value.UnwrapConv(ConversionKind.StopGCTracking).MatchLdLoc(v)) return false; // br targetBlock if (!notNullBlock.Instructions[2].MatchBranch(out targetBlock)) return false; // Block nullBlock (incoming: 1) { // stloc ptr(conv i4->u (ldc.i4 0)) // br targetBlock // } if (nullBlock.IncomingEdgeCount != 1) return false; if (nullBlock.Instructions.Count != 2) return false; if (!nullBlock.Instructions[0].MatchStLoc(ptrAssign.Variable, out var nullPointerInst)) return false; if (!nullPointerInst.MatchLdcI(0)) return false; if (!nullBlock.Instructions[1].MatchBranch(targetBlock)) return false; } else { // br targetBlock if (!notNullBlock.Instructions[1].MatchBranch(out targetBlock)) return false; if (targetBlock != nullBlock) return false; // nullBlock must be set to null, so that // we do not clear out targetBlock in the caller. nullBlock = null; } return true; } // Detect the following pattern: // ... // stloc V(ldloc S) // if (comp(ldloc S == ldnull)) br B_null_or_empty // br B_not_null // } // Block B_not_null { // if (conv i->i4 (ldlen(ldloc V))) br B_not_null_and_not_empty // br B_null_or_empty // } // Block B_not_null_and_not_empty { // stloc P(ldelema(ldloc V, ldc.i4 0, ...)) // br B_target // } // Block B_null_or_empty { // stloc P(conv i4->u(ldc.i4 0)) // br B_target // } // And convert the whole thing into: // ... // stloc P(array.to.pointer(V)) // br B_target bool IsNullSafeArrayToPointerPattern(Block block, out ILVariable v, out ILVariable p, out Block targetBlock) { v = null; p = null; targetBlock = null; // ... // if (comp(ldloc V == ldnull)) br B_null_or_empty // br B_not_null var ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; if (ifInst == null) return false; var condition = ifInst.Condition as Comp; if (!(condition != null && condition.Kind == ComparisonKind.Equality && condition.Left.MatchLdLoc(out v) && condition.Right.MatchLdNull())) return false; bool usingPreviousVar = false; if (v.Kind == VariableKind.StackSlot) { // If the variable is a stack slot, that might be due to an inline assignment, // so check the previous instruction: var previous = block.Instructions.ElementAtOrDefault(block.Instructions.Count - 3) as StLoc; if (previous != null && previous.Value.MatchLdLoc(v)) { // stloc V(ldloc S) // if (comp(ldloc S == ldnull)) ... v = previous.Variable; usingPreviousVar = true; } } if (!ifInst.TrueInst.MatchBranch(out Block nullOrEmptyBlock)) return false; if (!ifInst.FalseInst.MatchNop()) return false; if (nullOrEmptyBlock.Parent != block.Parent) return false; if (!IsNullSafeArrayToPointerNullOrEmptyBlock(nullOrEmptyBlock, out p, out targetBlock)) return false; if (!(p.Kind == VariableKind.PinnedLocal || (usingPreviousVar && v.Kind == VariableKind.PinnedLocal))) return false; if (!block.Instructions.Last().MatchBranch(out Block notNullBlock)) return false; if (notNullBlock.Parent != block.Parent) return false; return IsNullSafeArrayToPointerNotNullBlock(notNullBlock, v, p, nullOrEmptyBlock, targetBlock); } bool IsNullSafeArrayToPointerNotNullBlock(Block block, ILVariable v, ILVariable p, Block nullOrEmptyBlock, Block targetBlock) { // Block B_not_null { // if (conv i->i4 (ldlen(ldloc V))) br B_not_null_and_not_empty // br B_null_or_empty // } if (block.Instructions.Count != 2) return false; if (!block.Instructions[0].MatchIfInstruction(out ILInstruction condition, out ILInstruction trueInst)) return false; var falseInst = block.Instructions[1]; if (condition is Comp comp && comp.Right.MatchLdcI(0)) { if (comp.Kind == ComparisonKind.Equality) { // if (len == 0): effectively negates the condition condition = comp.Left; ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (comp.Kind == ComparisonKind.Inequality) { // if (len != 0): comparison is redundant (equivalent to implicit non-zero check) condition = comp.Left; } else { return false; } } condition = condition.UnwrapConv(ConversionKind.Truncate); if (condition.MatchLdLen(StackType.I, out ILInstruction array)) { // OK } else if (condition is CallInstruction call && call.Method.Name == "get_Length") { // Used instead of ldlen for multi-dimensional arrays if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.Array)) return false; if (call.Arguments.Count != 1) return false; array = call.Arguments[0]; } else { return false; } if (!array.MatchLdLoc(v)) return false; if (!trueInst.MatchBranch(out Block notNullAndNotEmptyBlock)) return false; if (notNullAndNotEmptyBlock.Parent != block.Parent) return false; if (!IsNullSafeArrayToPointerNotNullAndNotEmptyBlock(notNullAndNotEmptyBlock, v, p, targetBlock)) return false; return falseInst.MatchBranch(nullOrEmptyBlock); } bool IsNullSafeArrayToPointerNotNullAndNotEmptyBlock(Block block, ILVariable v, ILVariable p, Block targetBlock) { // Block B_not_null_and_not_empty { // stloc P(ldelema(ldloc V, ldc.i4 0, ...)) // br B_target // } if (block.Instructions.Count != 2) return false; if (!block.Instructions[0].MatchStLoc(out var p2, out ILInstruction value)) return false; if (p != p2) { // If the pointer is unused, the variable P might have been split. if (p.LoadCount == 0 && p.AddressCount == 0 && p2.LoadCount == 0 && p2.AddressCount == 0) { if (!ILVariableEqualityComparer.Instance.Equals(p, p2)) return false; } else { return false; } } if (v.Kind == VariableKind.PinnedLocal) { value = value.UnwrapConv(ConversionKind.StopGCTracking); } if (!(value is LdElema ldelema)) return false; if (!ldelema.Array.MatchLdLoc(v)) return false; if (!ldelema.Indices.All(i => i.MatchLdcI4(0))) return false; return block.Instructions[1].MatchBranch(targetBlock); } bool IsNullSafeArrayToPointerNullOrEmptyBlock(Block block, out ILVariable p, out Block targetBlock) { p = null; targetBlock = null; // Block B_null_or_empty { // stloc P(conv i4->u(ldc.i4 0)) // br B_target // } ILInstruction value; return block.Instructions.Count == 2 && block.Instructions[0].MatchStLoc(out p, out value) && (p.Kind == VariableKind.PinnedLocal || p.Kind == VariableKind.Local) && IsNullOrZero(value) && block.Instructions[1].MatchBranch(out targetBlock); } #endregion #region CreatePinnedRegion bool DetectPinnedRegion(Block block) { // After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block // can be a write to a pinned local. var stLoc = block.Instructions.SecondToLastOrDefault() as StLoc; if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal) return false; // stLoc is a store to a pinned local. if (IsNullOrZero(stLoc.Value)) { return false; // ignore unpin instructions } if (stLoc.Variable.Type.IsReferenceType == false) { // `pinned` flag has no effect on value types (#2148) return false; } // stLoc is a store that starts a new pinned region context.StepStartGroup($"DetectPinnedRegion {stLoc.Variable.Name}", block); try { return CreatePinnedRegion(block, stLoc); } finally { context.StepEndGroup(keepIfEmpty: true); } } bool CreatePinnedRegion(Block block, StLoc stLoc) { // Collect the blocks to be moved into the region: BlockContainer sourceContainer = (BlockContainer)block.Parent; int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count]; Queue workList = new Queue(); Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock; if (entryBlock.Parent != sourceContainer) { // we didn't find a single block to be added to the pinned region return false; } if (entryBlock.Instructions[0].MatchStLoc(stLoc.Variable, out _)) { // pinned region has empty body } else { reachedEdgesPerBlock[entryBlock.ChildIndex]++; workList.Enqueue(entryBlock); } while (workList.Count > 0) { Block workItem = workList.Dequeue(); foreach (var branch in workItem.Descendants.OfType()) { if (branch.TargetBlock.Parent == sourceContainer) { if (branch.TargetBlock.Instructions[0].MatchStLoc(stLoc.Variable, out _)) { // Found unpin instruction continue; } Debug.Assert(branch.TargetBlock != block); if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { // detected first edge to that block: add block as work item workList.Enqueue(branch.TargetBlock); } } } } // Validate that all uses of a block consistently are inside or outside the pinned region. // (we cannot do this anymore after we start moving blocks around) bool cloneBlocks = false; for (int i = 0; i < sourceContainer.Blocks.Count; i++) { if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) { // Don't abort in this case, we still need to somehow represent the pinned variable with a fixed statement. // We'll duplicate the code so that it can be both inside and outside the pinned region. cloneBlocks = true; break; } } context.Step("CreatePinnedRegion", block); BlockContainer body = new BlockContainer(); Block[] clonedBlocks = cloneBlocks ? new Block[sourceContainer.Blocks.Count] : null; for (int i = 0; i < sourceContainer.Blocks.Count; i++) { if (reachedEdgesPerBlock[i] > 0) { var innerBlock = sourceContainer.Blocks[i]; if (cloneBlocks) { innerBlock = (Block)innerBlock.Clone(); clonedBlocks[i] = innerBlock; } if (innerBlock.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst)) { HandleBranchLeavingPinnedRegion(trueInst, reachedEdgesPerBlock, sourceContainer, stLoc.Variable); HandleBranchLeavingPinnedRegion(falseInst, reachedEdgesPerBlock, sourceContainer, stLoc.Variable); } else { HandleBranchLeavingPinnedRegion(innerBlock.Instructions.LastOrDefault(), reachedEdgesPerBlock, sourceContainer, stLoc.Variable); } // move block into body if (sourceContainer.Blocks[i] == entryBlock) { // ensure entry point comes first body.Blocks.Insert(0, innerBlock); } else { body.Blocks.Add(innerBlock); } if (!cloneBlocks) { sourceContainer.Blocks[i] = new Block(); // replace with dummy block // we'll delete the dummy block later } } } if (body.Blocks.Count == 0) { // empty body, the entryBlock itself doesn't belong into the pinned region Debug.Assert(reachedEdgesPerBlock[entryBlock.ChildIndex] == 0); var bodyBlock = new Block(); bodyBlock.SetILRange(stLoc); bodyBlock.Instructions.Add(new Branch(entryBlock)); body.Blocks.Add(bodyBlock); } var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body).WithILRange(stLoc); stLoc.ReplaceWith(pinnedRegion); block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body if (cloneBlocks) { // Adjust branches between cloned blocks. foreach (var branch in body.Descendants.OfType()) { if (branch.TargetContainer == sourceContainer) { int i = branch.TargetBlock.ChildIndex; if (clonedBlocks[i] != null) { branch.TargetBlock = clonedBlocks[i]; } } } // Replace unreachable blocks in sourceContainer with dummy blocks: bool[] isAlive = new bool[sourceContainer.Blocks.Count]; List duplicatedBlockStartOffsets = new List(); foreach (var remainingBlock in sourceContainer.TopologicalSort(deleteUnreachableBlocks: true)) { isAlive[remainingBlock.ChildIndex] = true; if (clonedBlocks[remainingBlock.ChildIndex] != null) { duplicatedBlockStartOffsets.Add(remainingBlock.StartILOffset); } } for (int i = 0; i < isAlive.Length; i++) { if (!isAlive[i]) sourceContainer.Blocks[i] = new Block(); } // we'll delete the dummy blocks later Debug.Assert(duplicatedBlockStartOffsets.Count > 0); duplicatedBlockStartOffsets.Sort(); context.Function.Warnings.Add("The blocks " + string.Join(", ", duplicatedBlockStartOffsets.Select(o => $"IL_{o:x4}")) + $" are reachable both inside and outside the pinned region starting at IL_{stLoc.StartILOffset:x4}." + " ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement."); } ProcessPinnedRegion(pinnedRegion); return true; } static void HandleBranchLeavingPinnedRegion(ILInstruction potentialBranch, int[] reachedEdgesPerBlock, BlockContainer sourceContainer, ILVariable pinnedRegionVar) { if (potentialBranch is Branch branch && branch.TargetBlock.IncomingEdgeCount == 1 && branch.TargetContainer == sourceContainer && reachedEdgesPerBlock[branch.TargetBlock.ChildIndex] == 0) { // branch that leaves body. // The target block should have an instruction that resets the pin; delete that instruction: StLoc unpin = branch.TargetBlock.Instructions.First() as StLoc; if (unpin != null && unpin.Variable == pinnedRegionVar && IsNullOrZero(unpin.Value)) { branch.TargetBlock.Instructions.RemoveAt(0); } } } static bool IsNullOrZero(ILInstruction inst) { while (inst is Conv conv) { inst = conv.Argument; } return inst.MatchLdcI4(0) || inst.MatchLdNull(); } #endregion #region ProcessPinnedRegion /// /// After a pinned region was detected; process its body; replacing the pin variable /// with a native pointer as far as possible. /// void ProcessPinnedRegion(PinnedRegion pinnedRegion) { if (pinnedRegion.Variable.Type.Kind == TypeKind.ByReference) { // C# doesn't support a "by reference" variable, so replace it with a native pointer context.Step("Replace pinned ref-local with native pointer", pinnedRegion); ILVariable oldVar = pinnedRegion.Variable; IType elementType = ((ByReferenceType)oldVar.Type).ElementType; if (elementType.Kind == TypeKind.Pointer && pinnedRegion.Init.MatchLdFlda(out _, out var field) && ((PointerType)elementType).ElementType.Equals(field.Type)) { // Roslyn 2.6 (C# 7.2) uses type "int*&" for the pinned local referring to a // fixed field of type "int". // Remove the extra level of indirection. elementType = ((PointerType)elementType).ElementType; } ILVariable newVar = new ILVariable( VariableKind.PinnedRegionLocal, new PointerType(elementType), oldVar.Index); newVar.Name = oldVar.Name; newVar.HasGeneratedName = oldVar.HasGeneratedName; oldVar.Function.Variables.Add(newVar); ReplacePinnedVar(oldVar, newVar, pinnedRegion); UseExistingVariableForPinnedRegion(pinnedRegion); } else if (pinnedRegion.Variable.Type.Kind == TypeKind.Array) { context.Step("Replace pinned array with native pointer", pinnedRegion); MoveArrayToPointerToPinnedRegionInit(pinnedRegion); UseExistingVariableForPinnedRegion(pinnedRegion); } else if (pinnedRegion.Variable.Type.IsKnownType(KnownTypeCode.String)) { // fixing a string HandleStringToPointer(pinnedRegion); } else if (pinnedRegion.Init is Conv { Kind: ConversionKind.StopGCTracking, Argument: var convArg }) { // If pinnedRegion.Variable was already a pointer type, the input IL has a StopGCTracking conversion. // We can simply remove this conversion, as it is not needed. context.Step("Remove StopGCTracking conversion", pinnedRegion); pinnedRegion.Init = convArg; } // Detect nested pinned regions: BlockContainer body = (BlockContainer)pinnedRegion.Body; foreach (var block in body.Blocks) DetectPinnedRegion(block); body.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks body.SetILRange(body.EntryPoint); if (pinnedRegion.Variable.Kind != VariableKind.PinnedRegionLocal) { Debug.Assert(pinnedRegion.Variable.Kind == VariableKind.PinnedLocal); pinnedRegion.Variable.Kind = VariableKind.PinnedRegionLocal; } } private void MoveArrayToPointerToPinnedRegionInit(PinnedRegion pinnedRegion) { // Roslyn started marking the array variable as pinned, // and then uses array.to.pointer immediately within the region. Debug.Assert(pinnedRegion.Variable.Type.Kind == TypeKind.Array); // Find the single load of the variable within the pinnedRegion: LdLoc ldloc = null; foreach (var inst in pinnedRegion.Descendants.OfType()) { if (inst.Variable == pinnedRegion.Variable && inst != pinnedRegion) { if (ldloc != null) return; // more than 1 variable access ldloc = inst as LdLoc; if (ldloc == null) return; // variable access that is not LdLoc } } if (ldloc == null) return; if (!(ldloc.Parent is GetPinnableReference arrayToPointer)) return; if (!(arrayToPointer.Parent is Conv conv && conv.Kind == ConversionKind.StopGCTracking)) return; Debug.Assert(arrayToPointer.IsDescendantOf(pinnedRegion)); ILVariable oldVar = pinnedRegion.Variable; ILVariable newVar = new ILVariable( VariableKind.PinnedRegionLocal, new PointerType(((ArrayType)oldVar.Type).ElementType), oldVar.Index); newVar.Name = oldVar.Name; newVar.HasGeneratedName = oldVar.HasGeneratedName; oldVar.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, arrayToPointer.Method).WithILRange(arrayToPointer); conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv)); } void ReplacePinnedVar(ILVariable oldVar, ILVariable newVar, ILInstruction inst) { Debug.Assert(newVar.StackType == StackType.I); if (inst is Conv conv && conv.Kind == ConversionKind.StopGCTracking && conv.Argument.MatchLdLoc(oldVar) && conv.ResultType == newVar.StackType) { // conv ref->i (ldloc oldVar) // => ldloc newVar conv.AddILRange(conv.Argument); conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv)); return; } if (inst is IInstructionWithVariableOperand iwvo && iwvo.Variable == oldVar) { iwvo.Variable = newVar; if (inst is StLoc stloc && oldVar.Type.Kind == TypeKind.ByReference) { stloc.Value = new Conv(stloc.Value, PrimitiveType.I, false, Sign.None); } if ((inst is LdLoc || inst is StLoc) && !IsSlotAcceptingBothManagedAndUnmanagedPointers(inst.SlotInfo) && oldVar.StackType != StackType.I) { // wrap inst in Conv, so that the stack types match up var children = inst.Parent.Children; children[inst.ChildIndex] = new Conv(inst, oldVar.StackType.ToPrimitiveType(), false, Sign.None); } } else if (inst.MatchLdStr(out var val) && val == "Is this ILSpy?") { inst.ReplaceWith(new LdStr("This is ILSpy!")); // easter egg ;) return; } foreach (var child in inst.Children) { ReplacePinnedVar(oldVar, newVar, child); } } private bool IsSlotAcceptingBothManagedAndUnmanagedPointers(SlotInfo slotInfo) { return slotInfo == Block.InstructionSlot || slotInfo == LdObj.TargetSlot || slotInfo == StObj.TargetSlot; } bool IsBranchOnNull(ILInstruction condBranch, ILVariable nativeVar, out Block targetBlock) { targetBlock = null; // if (comp(ldloc nativeVar == conv i4->i (ldc.i4 0))) br targetBlock ILInstruction condition, trueInst, left, right; return condBranch.MatchIfInstruction(out condition, out trueInst) && condition.MatchCompEquals(out left, out right) && left.MatchLdLoc(nativeVar) && IsNullOrZero(right) && trueInst.MatchBranch(out targetBlock); } void HandleStringToPointer(PinnedRegion pinnedRegion) { Debug.Assert(pinnedRegion.Variable.Type.IsKnownType(KnownTypeCode.String)); BlockContainer body = (BlockContainer)pinnedRegion.Body; if (body.EntryPoint.IncomingEdgeCount != 1) return; // stloc nativeVar(conv o->i (ldloc pinnedVar)) // if (comp(ldloc nativeVar == conv i4->i (ldc.i4 0))) br targetBlock // br adjustOffsetToStringData ILVariable newVar; if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) { // potentially a special case with legacy csc and an unused pinned variable: if (pinnedRegion.Variable.AddressCount == 0 && pinnedRegion.Variable.LoadCount == 0) { var charPtr = new PointerType(context.TypeSystem.FindType(KnownTypeCode.Char)); newVar = new ILVariable(VariableKind.PinnedRegionLocal, charPtr, pinnedRegion.Variable.Index); newVar.Name = pinnedRegion.Variable.Name; newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName; pinnedRegion.Variable.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null); } return; } if (nativeVar.Type.GetStackType() != StackType.I) return; Block targetBlock; Block adjustOffsetToStringData = null; if (body.EntryPoint.Instructions.Count == 2) { if (!initInst.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out ILInstruction left, out ILInstruction right)) return; if (!left.UnwrapConv(ConversionKind.StopGCTracking).MatchLdLoc(pinnedRegion.Variable)) return; if (!IsOffsetToStringDataCall(right)) return; if (!body.EntryPoint.Instructions[1].MatchBranch(out targetBlock)) return; } else if (body.EntryPoint.Instructions.Count == 3) { if (!initInst.UnwrapConv(ConversionKind.StopGCTracking).MatchLdLoc(pinnedRegion.Variable)) return; if (!IsBranchOnNull(body.EntryPoint.Instructions[1], nativeVar, out targetBlock)) return; if (!body.EntryPoint.Instructions[2].MatchBranch(out adjustOffsetToStringData)) return; if (!(adjustOffsetToStringData.Parent == body && adjustOffsetToStringData.IncomingEdgeCount == 1 && IsOffsetToStringDataBlock(adjustOffsetToStringData, nativeVar, targetBlock))) return; } else return; context.Step("Handle pinned string (with adjustOffsetToStringData)", pinnedRegion); if (targetBlock.Parent == body) { // remove old entry point body.Blocks.RemoveAt(0); if (adjustOffsetToStringData is not null) body.Blocks.RemoveAt(adjustOffsetToStringData.ChildIndex); // make targetBlock the new entry point body.Blocks.RemoveAt(targetBlock.ChildIndex); body.Blocks.Insert(0, targetBlock); } else { // pinned region has empty body, immediately jumps to targetBlock which is outside body.Blocks[0].Instructions.Clear(); body.Blocks.RemoveRange(1, body.Blocks.Count - 1); body.Blocks[0].Instructions.Add(new Branch(targetBlock)); } pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null); ILVariable otherVar; ILInstruction otherVarInit; // In optimized builds, the 'nativeVar' may end up being a stack slot, // and only gets assigned to a real variable after the offset adjustment. if (nativeVar.Kind == VariableKind.StackSlot && nativeVar.LoadCount == 1 && body.EntryPoint.Instructions[0].MatchStLoc(out otherVar, out otherVarInit) && otherVarInit.MatchLdLoc(nativeVar) && otherVar.IsSingleDefinition) { body.EntryPoint.Instructions.RemoveAt(0); nativeVar = otherVar; } if (nativeVar.Kind == VariableKind.Local) { newVar = new ILVariable(VariableKind.PinnedRegionLocal, nativeVar.Type, nativeVar.Index); newVar.Name = nativeVar.Name; newVar.HasGeneratedName = nativeVar.HasGeneratedName; nativeVar.Function.Variables.Add(newVar); ReplacePinnedVar(nativeVar, newVar, pinnedRegion); } else { newVar = nativeVar; } ReplacePinnedVar(pinnedRegion.Variable, newVar, pinnedRegion); } bool IsOffsetToStringDataBlock(Block block, ILVariable nativeVar, Block targetBlock) { // stloc nativeVar(add(ldloc nativeVar, conv i4->i (call [Accessor System.Runtime.CompilerServices.RuntimeHelpers.get_OffsetToStringData():System.Int32]()))) // br IL_0011 if (block.Instructions.Count != 2) return false; ILInstruction value; if (nativeVar.IsSingleDefinition && nativeVar.LoadCount == 2) { // If there are no loads (except for the two in the string-to-pointer pattern), // then we might have split nativeVar: if (!block.Instructions[0].MatchStLoc(out var otherVar, out value)) return false; if (!(otherVar.IsSingleDefinition && otherVar.LoadCount == 0)) return false; } else if (nativeVar.StoreCount == 2) { // normal case with non-split variable if (!block.Instructions[0].MatchStLoc(nativeVar, out value)) return false; } else { return false; } if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out ILInstruction left, out ILInstruction right)) return false; if (!left.MatchLdLoc(nativeVar)) return false; if (!IsOffsetToStringDataCall(right)) return false; return block.Instructions[1].MatchBranch(targetBlock); } bool IsOffsetToStringDataCall(ILInstruction inst) { Call call = inst.UnwrapConv(ConversionKind.SignExtend) as Call; return call != null && call.Method.FullName == "System.Runtime.CompilerServices.RuntimeHelpers.get_OffsetToStringData"; } /// /// Modifies a pinned region to eliminate an extra local variable that roslyn tends to generate. /// void UseExistingVariableForPinnedRegion(PinnedRegion pinnedRegion) { // PinnedRegion V_1(..., BlockContainer { // Block IL_0000(incoming: 1) { // stloc V_0(ldloc V_1) // ... if (!(pinnedRegion.Body is BlockContainer body)) return; if (pinnedRegion.Variable.LoadCount != 1) return; if (!body.EntryPoint.Instructions[0].MatchStLoc(out var v, out var init)) return; if (!init.MatchLdLoc(pinnedRegion.Variable)) return; if (!(v.IsSingleDefinition && v.Type.Equals(pinnedRegion.Variable.Type))) return; if (v.Kind != VariableKind.Local) return; // replace V_1 with V_0 v.Kind = VariableKind.PinnedRegionLocal; pinnedRegion.Variable = v; body.EntryPoint.Instructions.RemoveAt(0); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.Decompiler.IL.Transforms; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Detect suitable exit points for BlockContainers. /// /// An "exit point" is an instruction that causes control flow /// to leave the container (a branch or leave instruction). /// /// If an "exit point" instruction is placed immediately following a /// block container, each equivalent exit point within the container /// can be replaced with a "leave container" instruction. /// /// This transform performs this replacement: any exit points /// equivalent to the exit point following the container are /// replaced with a leave instruction. /// Additionally, if the container is not yet followed by an exit point, /// but has room to introduce such an exit point (i.e. iff the container's /// end point is currently unreachable), we pick one of the non-return /// exit points within the container, move it to the position following the /// container, and replace all instances within the container with a leave /// instruction. /// /// This makes it easier for the following transforms to construct /// control flow that falls out of blocks instead of using goto/break statements. /// public class DetectExitPoints : ILVisitor, IILTransform { static readonly Nop ExitNotYetDetermined = new Nop { Comment = "ExitNotYetDetermined" }; static readonly Nop NoExit = new Nop { Comment = "NoExit" }; /// /// Gets the next instruction after is executed. /// Returns NoExit when the next instruction cannot be identified; /// returns null when the end of a Block is reached (so that we could insert an arbitrary instruction) /// internal static ILInstruction GetExit(ILInstruction inst) { SlotInfo? slot = inst.SlotInfo; if (slot == Block.InstructionSlot) { Block block = (Block)inst.Parent!; return block.Instructions.ElementAtOrDefault(inst.ChildIndex + 1) ?? ExitNotYetDetermined; } else if (slot == TryInstruction.TryBlockSlot || slot == TryCatchHandler.BodySlot || slot == TryCatch.HandlerSlot || slot == PinnedRegion.BodySlot || slot == UsingInstruction.BodySlot || slot == LockInstruction.BodySlot) { return GetExit(inst.Parent!); } return NoExit; } /// /// Returns true iff exit1 and exit2 are both exit instructions /// (branch or leave) and both represent the same exit. /// internal static bool CompatibleExitInstruction(ILInstruction exit1, ILInstruction exit2) { if (exit1 == null || exit2 == null || exit1.OpCode != exit2.OpCode) return false; switch (exit1.OpCode) { case OpCode.Branch: Branch br1 = (Branch)exit1; Branch br2 = (Branch)exit2; return br1.TargetBlock == br2.TargetBlock; case OpCode.Leave: Leave leave1 = (Leave)exit1; Leave leave2 = (Leave)exit2; return leave1.TargetContainer == leave2.TargetContainer && leave1.Value.MatchNop() && leave2.Value.MatchNop(); default: return false; } } class ContainerContext { public readonly BlockContainer Container; /// /// The instruction that will be executed next after leaving the Container. /// ExitNotYetDetermined means the container is last in its parent block, and thus does not /// yet have any leave instructions. This means we can move any exit instruction of /// our choice our of the container and replace it with a leave instruction. /// public readonly ILInstruction CurrentExit; /// /// If currentExit==ExitNotYetDetermined, holds the list of potential exit instructions. /// After the currentContainer was visited completely, one of these will be selected as exit instruction. /// public readonly List? PotentialExits = null; public ContainerContext(BlockContainer container, ILInstruction currentExit) { this.Container = container; this.CurrentExit = currentExit; this.PotentialExits = (currentExit == ExitNotYetDetermined ? new List() : null); } public void HandleExit(ILInstruction inst) { if (this.CurrentExit == ExitNotYetDetermined && this.Container.LeaveCount == 0) { this.PotentialExits!.Add(inst); } else if (CompatibleExitInstruction(inst, this.CurrentExit)) { inst.ReplaceWith(new Leave(this.Container).WithILRange(inst)); } } } CancellationToken cancellationToken; readonly List blocksPotentiallyMadeUnreachable = new List(); readonly Stack containerStack = new Stack(); public void Run(ILFunction function, ILTransformContext context) { cancellationToken = context.CancellationToken; blocksPotentiallyMadeUnreachable.Clear(); containerStack.Clear(); function.AcceptVisitor(this); // It's possible that there are unreachable code blocks which we only // detect as such during exit point detection. // Clean them up. foreach (var block in blocksPotentiallyMadeUnreachable) { if (block.IncomingEdgeCount == 0 || block.IncomingEdgeCount == 1 && IsInfiniteLoop(block)) { block.Remove(); } } blocksPotentiallyMadeUnreachable.Clear(); containerStack.Clear(); } static bool IsInfiniteLoop(Block block) { return block.Instructions.Count == 1 && block.Instructions[0] is Branch b && b.TargetBlock == block; } protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) child.AcceptVisitor(this); } protected internal override void VisitBlockContainer(BlockContainer container) { var thisExit = GetExit(container); var stackEntry = new ContainerContext(container, thisExit); containerStack.Push(stackEntry); base.VisitBlockContainer(container); if (stackEntry.PotentialExits?.Any(i => i.IsConnected) ?? false) { // This transform determined an exit point. var newExit = ChooseExit(stackEntry.PotentialExits.Where(i => i.IsConnected)); Debug.Assert(!newExit.MatchLeave(container)); foreach (var exit in stackEntry.PotentialExits) { if (exit.IsConnected && CompatibleExitInstruction(newExit, exit)) { exit.ReplaceWith(new Leave(container).WithILRange(exit)); } } ILInstruction inst = container; // traverse up to the block (we'll always find one because GetExit // only returns ExitNotYetDetermined if there's a block) while (inst.Parent!.OpCode != OpCode.Block) inst = inst.Parent; Block block = (Block)inst.Parent; if (block.HasFlag(InstructionFlags.EndPointUnreachable)) { // Special case: despite replacing the exits with leave(currentContainer), // we still have an unreachable endpoint. // The appended currentExit instruction would not be reachable! // This happens in test case ExceptionHandling.ThrowInFinally() if (newExit is Branch b) { blocksPotentiallyMadeUnreachable.Add(b.TargetBlock); } } else { block.Instructions.Add(newExit); } } if (containerStack.Pop() != stackEntry) { Debug.Fail("containerStack got imbalanced"); } } static ILInstruction ChooseExit(IEnumerable potentialExits) { using var enumerator = potentialExits.GetEnumerator(); enumerator.MoveNext(); ILInstruction first = enumerator.Current; if (first is Leave { IsLeavingFunction: true }) { while (enumerator.MoveNext()) { var exit = enumerator.Current; if (!(exit is Leave { IsLeavingFunction: true })) return exit; } } return first; } protected internal override void VisitBlock(Block block) { cancellationToken.ThrowIfCancellationRequested(); // Don't use foreach loop, because the children might add to the block for (int i = 0; i < block.Instructions.Count; i++) { block.Instructions[i].AcceptVisitor(this); } } protected internal override void VisitBranch(Branch inst) { foreach (var entry in containerStack) { if (inst.TargetBlock.IsDescendantOf(entry.Container)) break; entry.HandleExit(inst); } } protected internal override void VisitLeave(Leave inst) { base.VisitLeave(inst); if (!inst.Value.MatchNop()) return; foreach (var entry in containerStack) { if (inst.TargetContainer == entry.Container) break; if (inst.IsLeavingFunction || inst.TargetContainer.Kind != ContainerKind.Normal) { if (entry.Container.Kind == ContainerKind.Normal) { // Don't transform a `return`/`break` into a leave for try-block containers (or similar). // It's possible that those can be turned into fallthrough later, but // it might not work out and then we would be left with a `goto`. // But continue searching the container stack, it might be possible to // turn the `return` into a `break` instead. } else { // return; could turn to break; entry.HandleExit(inst); break; // but only for the innermost loop/switch } } else { entry.HandleExit(inst); } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Detect loops in IL AST. /// /// /// Transform ordering: /// * LoopDetection should run before other control flow structures are detected. /// * Blocks should be basic blocks (not extended basic blocks) so that the natural loops /// don't include more instructions than strictly necessary. /// * Loop detection should run after the 'return block' is duplicated (ControlFlowSimplification). /// public class LoopDetection : IBlockTransform { BlockTransformContext context; /// Block container corresponding to the current cfg. BlockContainer currentBlockContainer; /// /// Enabled during DetectSwitchBody, used by ExtendLoop and children /// private bool isSwitch; /// /// Used when isSwitch == true, to determine appropriate exit points within loops /// private SwitchDetection.LoopContext loopContext; /// /// Check whether 'block' is a loop head; and construct a loop instruction /// (nested BlockContainer) if it is. /// public void Run(Block block, BlockTransformContext context) { this.context = context; // LoopDetection runs early enough so that block should still // be in the original container at this point. Debug.Assert(block.Parent == context.ControlFlowGraph.Container); this.currentBlockContainer = context.ControlFlowGraph.Container; // Because this is a post-order block transform, we can assume that // any nested loops within this loop have already been constructed. if (block.Instructions.Last() is SwitchInstruction switchInst) { // Switch instructions support "break;" just like loops DetectSwitchBody(block, switchInst); } ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); List loop = null; foreach (var t in h.Predecessors) { if (h.Dominates(t)) { // h->t is a back edge, and h is a loop header // Add the natural loop of t->h to the loop. // Definitions: // * A back edge is an edge t->h so that h dominates t. // * The natural loop of the back edge is the smallest set of nodes // that includes the back edge and has no predecessors outside the set // except for the predecessor of the header. if (loop == null) { loop = new List(); loop.Add(h); // Mark loop header as visited so that the pre-order traversal // stops at the loop header. h.Visited = true; } t.TraversePreOrder(n => n.Predecessors, loop.Add); } } if (loop != null) { var headBlock = (Block)h.UserData; context.Step($"Construct loop with head {headBlock.Label}", headBlock); // loop now is the union of all natural loops with loop head h. // Ensure any block included into nested loops is also considered part of this loop: IncludeNestedContainers(loop); // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(loop[0] == h); foreach (var node in loop) { node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); } } /// /// For each block in the input loop that is the head of a nested loop or switch, /// include all blocks from the nested container into the loop. /// /// This ensures that all blocks that were included into inner loops are also /// included into the outer loop, thus keeping our loops well-nested. /// /// /// More details for why this is necessary are here: /// https://github.com/icsharpcode/ILSpy/issues/915 /// /// Pre+Post-Condition: node.Visited iff loop.Contains(node) /// void IncludeNestedContainers(List loop) { for (int i = 0; i < loop.Count; i++) { IncludeBlock((Block)loop[i].UserData); } void IncludeBlock(Block block) { foreach (var nestedContainer in block.Instructions.OfType()) { // Just in case the block has multiple nested containers (e.g. due to loop and switch), // also check the entry point: IncludeBlock(nestedContainer.EntryPoint); // Use normal processing for all non-entry-point blocks // (the entry-point itself doesn't have a CFG node, because it's newly created by this transform) for (int i = 1; i < nestedContainer.Blocks.Count; i++) { var node = context.ControlFlowGraph.GetNode(nestedContainer.Blocks[i]); Debug.Assert(loop[0].Dominates(node)); if (!node.Visited) { node.Visited = true; loop.Add(node); // note: this block will be re-visited when the "i < loop.Count" // gets around to the new entry } } } } } #region ExtendLoop /// /// Given a natural loop, add additional CFG nodes to the loop in order /// to reduce the number of exit points out of the loop. /// We do this because C# only allows reaching a single exit point (with 'break' /// statements or when the loop condition evaluates to false), so we'd have /// to introduce 'goto' statements for any additional exit points. /// /// /// Definition: /// A "reachable exit" is a branch/leave target that is reachable from the loop, /// but not dominated by the loop head. A reachable exit may or may not have a /// corresponding CFG node (depending on whether it is a block in the current block container). /// -> reachable exits are leaving the code region dominated by the loop /// /// Definition: /// A loop "exit point" is a CFG node that is not itself part of the loop, /// but has at least one predecessor which is part of the loop. /// -> exit points are leaving the loop itself /// /// Nodes can only be added to the loop if they are dominated by the loop head. /// When adding a node to the loop, we must also add all of that node's predecessors /// to the loop. (this ensures that the loop keeps its single entry point) /// /// Goal: If possible, find a set of nodes that can be added to the loop so that there /// remains only a single exit point. /// Add as little code as possible to the loop to reach this goal. /// /// This means we need to partition the set of nodes dominated by the loop entry point /// into two sets (in-loop and out-of-loop). /// Constraints: /// * the loop head itself is in-loop /// * there must not be any edge from an out-of-loop node to an in-loop node /// -> all predecessors of in-loop nodes are also in-loop /// -> all nodes in a cycle are part of the same partition /// Optimize: /// * use only a single exit point if at all possible /// * minimize the amount of code in the in-loop partition /// (thus: maximize the amount of code in the out-of-loop partition) /// /// Observations: /// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point) /// * If there are no exits reachable from a node (i.e. all paths from that node lead to a return/throw instruction), /// it is valid to put the group of nodes dominated by that node into either partition independently of /// any other nodes except for the ancestors in the dominator tree. /// (exception: the loop head itself must always be in-loop) /// /// There are two different cases we need to consider: /// 1) There are no exits reachable at all from the loop head. /// -> it is possible to create a loop with zero exit points by adding all nodes /// dominated by the loop to the loop. /// -> the only way to exit the loop is by "return;" or "throw;" /// 2) There are some exits reachable from the loop head. /// /// In case 1, we can pick a single exit point freely by picking any node that has no reachable exits /// (other than the loop head). /// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop. /// See PickExitPoint() for the heuristic that picks the exit point in this case. /// /// In case 2, we need to pick our exit point so that all paths from the loop head /// to the reachable exits run through that exit point. /// /// This is a form of postdominance where the reachable exits are considered exit nodes, /// while "return;" or "throw;" instructions are not considered exit nodes. /// /// Using this form of postdominance, we are looking for an exit point that post-dominates all nodes in the natural loop. /// --> a common ancestor in post-dominator tree. /// To minimize the amount of code in-loop, we pick the lowest common ancestor. /// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop. /// (using normal dominance as in case 1, not post-dominance!) /// /// If it is impossible to use a single exit point for the loop, the lowest common ancestor will be the fake "exit node" /// used by the post-dominance analysis. In this case, we fall back to the old heuristic algorithm. /// /// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop. /// void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint) { exitPoint = FindExitPoint(loopHead, loop); Debug.Assert(!loop.Contains(exitPoint), "Cannot pick an exit point that is part of the natural loop"); if (exitPoint != null) { // Either we are in case 1 and just picked an exit that maximizes the amount of code // outside the loop, or we are in case 2 and found an exit point via post-dominance. // Note that if exitPoint == NoExitPoint, we end up adding all dominated blocks to the loop. var ep = exitPoint; foreach (var node in TreeTraversal.PreOrder(loopHead, n => DominatorTreeChildren(n, ep))) { if (!node.Visited) { node.Visited = true; loop.Add(node); } } // The loop/switch can only be entered through the entry point. if (isSwitch) { // In the case of a switch, false positives in the "continue;" detection logic // can lead to falsely excludes some blocks from the body. // Fix that by including all predecessors of included blocks. Debug.Assert(loop[0] == loopHead); for (int i = 1; i < loop.Count; i++) { foreach (var p in loop[i].Predecessors) { if (!p.Visited) { p.Visited = true; loop.Add(p); } } } } Debug.Assert(loop.All(n => n == loopHead || n.Predecessors.All(p => p.Visited))); } else { // We are in case 2, but could not find a suitable exit point. // Heuristically try to minimize the number of exit points // (but we'll always end up with more than 1 exit and will require goto statements). ExtendLoopHeuristic(loopHead, loop, loopHead); } } /// /// Special control flow node (not part of any graph) that signifies that we want to construct a loop /// without any exit point. /// static readonly ControlFlowNode NoExitPoint = new ControlFlowNode(); /// /// Finds a suitable single exit point for the specified loop. /// /// /// 1) If a suitable exit point was found: the control flow block that should be reached when breaking from the loop /// 2) If the loop should not have any exit point (extend by all dominated blocks): NoExitPoint /// 3) otherwise (exit point unknown, heuristically extend loop): null /// /// This method must not write to the Visited flags on the CFG. internal ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop) { bool hasReachableExit = HasReachableExit(loopHead); if (!hasReachableExit) { // Case 1: // There are no nodes n so that loopHead dominates a predecessor of n but not n itself // -> we could build a loop with zero exit points. if (IsPossibleForeachLoop((Block)loopHead.UserData, out var exitBranch)) { if (exitBranch != null) { // let's see if the target of the exit branch is a suitable exit point var cfgNode = loopHead.Successors.FirstOrDefault(n => n.UserData == exitBranch.TargetBlock); if (cfgNode != null && loopHead.Dominates(cfgNode) && !context.ControlFlowGraph.HasReachableExit(cfgNode)) { return cfgNode; } } return NoExitPoint; } ControlFlowNode exitPoint = null; int exitPointILOffset = -1; ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset); foreach (var node in loopHead.DominatorTreeChildren) { PickExitPoint(node, ref exitPoint, ref exitPointILOffset); } return exitPoint; } else { // Case 2: // We need to pick our exit point so that all paths from the loop head // to the reachable exits run through that exit point. var cfg = context.ControlFlowGraph.cfg; var revCfg = PrepareReverseCFG(loopHead, out int exitNodeArity); //ControlFlowNode.ExportGraph(cfg).Show("cfg"); //ControlFlowNode.ExportGraph(revCfg).Show("rev"); ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex]; Debug.Assert(commonAncestor.IsReachable); foreach (ControlFlowNode cfgNode in naturalLoop) { ControlFlowNode revNode = revCfg[cfgNode.UserIndex]; if (revNode.IsReachable) { commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode); } } // All paths from within the loop to a reachable exit run through 'commonAncestor'. // However, this doesn't mean that 'commonAncestor' is valid as an exit point. // We walk up the post-dominator tree until we've got a valid exit point: ControlFlowNode exitPoint; while (commonAncestor.UserIndex >= 0) { exitPoint = cfg[commonAncestor.UserIndex]; Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint)); // It's possible that 'commonAncestor' is itself part of the natural loop. // If so, it's not a valid exit point. if (!exitPoint.Visited && ValidateExitPoint(loopHead, exitPoint)) { // we found an exit point return exitPoint; } commonAncestor = commonAncestor.ImmediateDominator; } // least common post-dominator is the artificial exit node // This means we're in one of two cases: // * The loop might have multiple exit points. // -> we should return null // * The loop has a single exit point that wasn't considered during post-dominance analysis. // (which means the single exit isn't dominated by the loop head) // -> we should return NoExitPoint so that all code dominated by the loop head is included into the loop if (exitNodeArity > 1) return null; // Exit node is on the very edge of the tree, and isn't important for determining inclusion // Still necessary for switch detection to insert correct leave statements if (exitNodeArity == 1 && isSwitch) return loopContext.GetBreakTargets(loopHead).Distinct().Single(); // If exitNodeArity == 0, we should maybe look test if our exits out of the block container are all compatible? // but I don't think it hurts to have a bit too much code inside the loop in this rare case. return NoExitPoint; } } /// /// Validates an exit point. /// /// An exit point is invalid iff there is a node reachable from the exit point that /// is dominated by the loop head, but not by the exit point. /// (i.e. this method returns false iff the exit point's dominance frontier contains /// a node dominated by the loop head. but we implement this the slow way because /// we don't have dominance frontiers precomputed) /// /// /// We need this because it's possible that there's a return block (thus reverse-unreachable node ignored by post-dominance) /// that is reachable both directly from the loop, and from the exit point. /// bool ValidateExitPoint(ControlFlowNode loopHead, ControlFlowNode exitPoint) { var cfg = context.ControlFlowGraph; return IsValid(exitPoint); bool IsValid(ControlFlowNode node) { if (!cfg.HasReachableExit(node)) { // Optimization: if the dominance frontier is empty, we don't need // to check every node. return true; } foreach (var succ in node.Successors) { if (loopHead != succ && loopHead.Dominates(succ) && !exitPoint.Dominates(succ)) return false; } foreach (var child in node.DominatorTreeChildren) { if (!IsValid(child)) return false; } return true; } } /// /// Extension of ControlFlowGraph.HasReachableExit /// Uses loopContext.GetBreakTargets().Any() when analyzing switches to avoid /// classifying continue blocks as reachable exits. /// bool HasReachableExit(ControlFlowNode node) => isSwitch ? loopContext.GetBreakTargets(node).Any() : context.ControlFlowGraph.HasReachableExit(node); /// /// Returns the children in a loop dominator tree, with an optional exit point /// Avoids returning continue statements when analysing switches (because increment blocks can be dominated) /// IEnumerable DominatorTreeChildren(ControlFlowNode n, ControlFlowNode exitPoint) => n.DominatorTreeChildren.Where(c => c != exitPoint && (!isSwitch || !loopContext.MatchContinue(c))); /// /// Pick exit point by picking any node that has no reachable exits. /// /// In the common case where the code was compiled with a compiler that emits IL code /// in source order (like the C# compiler), we can find the "real" exit point /// by simply picking the block with the highest IL offset. /// So let's do that instead of maximizing amount of code. /// /// Code amount in and its dominated nodes. /// This method must not write to the Visited flags on the CFG. void PickExitPoint(ControlFlowNode node, ref ControlFlowNode exitPoint, ref int exitPointILOffset) { if (isSwitch && loopContext.MatchContinue(node)) return; Block block = (Block)node.UserData; if (block.StartILOffset > exitPointILOffset && !HasReachableExit(node) && ((Block)node.UserData).Parent == currentBlockContainer) { // HasReachableExit(node) == false // -> there are no nodes n so that `node` dominates a predecessor of n but not n itself // -> there is no control flow out of `node` back into the loop, so it's usable as exit point // Additionally, we require that the block wasn't already moved into a nested loop, // since there's no way to jump into the middle of that loop when we need to exit. // NB: this is the only reason why we detect nested loops before outer loops: // If we detected the outer loop first, the outer loop might pick an exit point // that prevents us from finding a nice exit for the inner loops, causing // unnecessary gotos. exitPoint = node; exitPointILOffset = block.StartILOffset; return; // don't visit children, they are likely to have even later IL offsets and we'd end up // moving almost all of the code into the loop. } ConsiderReturnAsExitPoint(block, ref exitPoint, ref exitPointILOffset); foreach (var child in node.DominatorTreeChildren) { PickExitPoint(child, ref exitPoint, ref exitPointILOffset); } } private static void ConsiderReturnAsExitPoint(Block block, ref ControlFlowNode exitPoint, ref int exitPointILOffset) { // It's possible that the real exit point of the loop is a "return;" that has been combined (by ControlFlowSimplification) // with the condition block. if (!block.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst)) return; if (trueInst.StartILOffset > exitPointILOffset && trueInst is Leave { IsLeavingFunction: true, Value: Nop _ }) { // By using NoExitPoint, everything (including the "return;") becomes part of the loop body // Then DetectExitPoint will move the "return;" out of the loop body. exitPoint = NoExitPoint; exitPointILOffset = trueInst.StartILOffset; } if (falseInst.StartILOffset > exitPointILOffset && falseInst is Leave { IsLeavingFunction: true, Value: Nop _ }) { exitPoint = NoExitPoint; exitPointILOffset = falseInst.StartILOffset; } } /// /// Constructs a new control flow graph. /// Each node cfg[i] has a corresponding node rev[i]. /// Edges are only created for nodes dominated by loopHead, and are in reverse from their direction /// in the primary CFG. /// An artificial exit node is used for edges that leave the set of nodes dominated by loopHead, /// or that leave the block Container. /// /// Entry point of the loop. /// out: The number of different CFG nodes. /// Possible values: /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); /// 1 = a single CFG node (not dominated by loopHead) was used as an exit node; /// 2 = more than one CFG node (not dominated by loopHead) was used as an exit node. /// /// ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, out int exitNodeArity) { ControlFlowNode[] cfg = context.ControlFlowGraph.cfg; ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1]; for (int i = 0; i < cfg.Length; i++) { rev[i] = new ControlFlowNode { UserIndex = i, UserData = cfg[i].UserData }; } ControlFlowNode nodeTreatedAsExitNode = null; bool multipleNodesTreatedAsExitNodes = false; ControlFlowNode exitNode = new ControlFlowNode { UserIndex = -1 }; rev[cfg.Length] = exitNode; for (int i = 0; i < cfg.Length; i++) { if (!loopHead.Dominates(cfg[i]) || isSwitch && cfg[i] != loopHead && loopContext.MatchContinue(cfg[i])) continue; // Add reverse edges for all edges in cfg foreach (var succ in cfg[i].Successors) { // edges to outer loops still count as exits (labelled continue not implemented) if (isSwitch && loopContext.MatchContinue(succ, 1)) continue; if (loopHead.Dominates(succ)) { rev[succ.UserIndex].AddEdgeTo(rev[i]); } else { if (nodeTreatedAsExitNode == null) nodeTreatedAsExitNode = succ; if (nodeTreatedAsExitNode != succ) multipleNodesTreatedAsExitNodes = true; exitNode.AddEdgeTo(rev[i]); } } if (context.ControlFlowGraph.HasDirectExitOutOfContainer(cfg[i])) { exitNode.AddEdgeTo(rev[i]); } } if (multipleNodesTreatedAsExitNodes) exitNodeArity = 2; // more than 1 else if (nodeTreatedAsExitNode != null) exitNodeArity = 1; else exitNodeArity = 0; Dominance.ComputeDominance(exitNode, context.CancellationToken); return rev; } static bool IsPossibleForeachLoop(Block loopHead, out Branch exitBranch) { exitBranch = null; var container = (BlockContainer)loopHead.Parent; if (!(container.SlotInfo == TryInstruction.TryBlockSlot && container.Parent is TryFinally)) return false; if (loopHead.Instructions.Count != 2) return false; if (!loopHead.Instructions[0].MatchIfInstruction(out var condition, out var trueInst)) return false; var falseInst = loopHead.Instructions[1]; while (condition.MatchLogicNot(out var arg)) { condition = arg; ExtensionMethods.Swap(ref trueInst, ref falseInst); } if (!(condition is CallInstruction call && call.Method.Name == "MoveNext")) return false; if (!(call.Arguments.Count == 1 && call.Arguments[0].MatchLdLocRef(out var enumeratorVar))) return false; exitBranch = falseInst as Branch; // Check that loopHead is entry-point of try-block: Block entryPoint = container.EntryPoint; while (entryPoint.IncomingEdgeCount == 1 && entryPoint.Instructions.Count == 1 && entryPoint.Instructions[0].MatchBranch(out var targetBlock)) { // skip blocks that only branch to another block entryPoint = targetBlock; } return entryPoint == loopHead; } #endregion #region ExtendLoop (fall-back heuristic) /// /// This function implements a heuristic algorithm that tries to reduce the number of exit /// points. It is only used as fall-back when it is impossible to use a single exit point. /// /// /// This heuristic loop extension algorithm traverses the loop head's dominator tree in pre-order. /// For each candidate node, we detect whether adding it to the loop reduces the number of exit points. /// If it does, the candidate is added to the loop. /// /// Adding a node to the loop has two effects on the the number of exit points: /// * exit points that were added to the loop are no longer exit points, thus reducing the total number of exit points /// * successors of the newly added nodes might be new, additional exit points /// /// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop. /// void ExtendLoopHeuristic(ControlFlowNode loopHead, List loop, ControlFlowNode candidate) { Debug.Assert(candidate.Visited == loop.Contains(candidate)); if (!candidate.Visited) { // This node not yet part of the loop, but might be added List additionalNodes = new List(); // Find additionalNodes nodes and mark them as visited. candidate.TraversePreOrder(n => n.Predecessors, additionalNodes.Add); // This means Visited now represents the candidate extended loop. // Determine new exit points that are reachable from the additional nodes // (note: some of these might have previously been exit points, too) var newExitPoints = additionalNodes.SelectMany(n => n.Successors).Where(n => !n.Visited).ToHashSet(); // Make visited represent the unextended loop, so that we can measure the exit points // in the old state. foreach (var node in additionalNodes) node.Visited = false; // Measure number of added and removed exit points int removedExitPoints = additionalNodes.Count(IsExitPoint); int addedExitPoints = newExitPoints.Count(n => !IsExitPoint(n)); if (removedExitPoints > addedExitPoints) { // We can reduce the number of exit points by adding the candidate node to the loop. candidate.TraversePreOrder(n => n.Predecessors, loop.Add); } } // Pre-order traversal of dominator tree foreach (var node in candidate.DominatorTreeChildren) { ExtendLoopHeuristic(loopHead, loop, node); } } /// /// Gets whether 'node' is an exit point for the loop marked by the Visited flag. /// bool IsExitPoint(ControlFlowNode node) { if (node.Visited) return false; // nodes in the loop are not exit points foreach (var pred in node.Predecessors) { if (pred.Visited) return true; } return false; } #endregion /// /// Move the blocks associated with the loop into a new block container. /// void ConstructLoop(List loop, ControlFlowNode exitPoint) { Block oldEntryPoint = (Block)loop[0].UserData; Block exitTargetBlock = (Block)exitPoint?.UserData; BlockContainer loopContainer = new BlockContainer(ContainerKind.Loop); Block newEntryPoint = new Block(); loopContainer.Blocks.Add(newEntryPoint); // Move contents of oldEntryPoint to newEntryPoint // (we can't move the block itself because it might be the target of branch instructions outside the loop) newEntryPoint.Instructions.ReplaceList(oldEntryPoint.Instructions); newEntryPoint.AddILRange(oldEntryPoint); oldEntryPoint.Instructions.ReplaceList(new[] { loopContainer }); if (exitTargetBlock != null) oldEntryPoint.Instructions.Add(new Branch(exitTargetBlock)); loopContainer.AddILRange(newEntryPoint); MoveBlocksIntoContainer(loop, loopContainer); // Rewrite branches within the loop from oldEntryPoint to newEntryPoint: foreach (var branch in loopContainer.Descendants.OfType()) { if (branch.TargetBlock == oldEntryPoint) { branch.TargetBlock = newEntryPoint; } else if (branch.TargetBlock == exitTargetBlock) { branch.ReplaceWith(new Leave(loopContainer).WithILRange(branch)); } } } private void MoveBlocksIntoContainer(List loop, BlockContainer loopContainer) { // Move other blocks into the loop body: they're all dominated by the loop header, // and thus cannot be the target of branch instructions outside the loop. for (int i = 1; i < loop.Count; i++) { Block block = (Block)loop[i].UserData; // some blocks might already be in use by nested loops that were detected earlier; // don't move those (they'll be implicitly moved when the block containing the // nested loop container is moved). if (block.Parent == currentBlockContainer) { Debug.Assert(block.ChildIndex != 0); int oldChildIndex = block.ChildIndex; loopContainer.Blocks.Add(block); currentBlockContainer.Blocks.SwapRemoveAt(oldChildIndex); } } for (int i = 1; i < loop.Count; i++) { // Verify that we moved all loop blocks into the loop container. // If we wanted to move any blocks already in use by a nested loop, // this means we check that the whole nested loop got moved. Block block = (Block)loop[i].UserData; Debug.Assert(block.IsDescendantOf(loopContainer)); } } private void DetectSwitchBody(Block block, SwitchInstruction switchInst) { Debug.Assert(block.Instructions.Last() == switchInst); ControlFlowNode h = context.ControlFlowNode; // CFG node for our switch head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); isSwitch = true; loopContext = new SwitchDetection.LoopContext(context.ControlFlowGraph, h); var nodesInSwitch = new List(); nodesInSwitch.Add(h); h.Visited = true; ExtendLoop(h, nodesInSwitch, out var exitPoint); if (exitPoint != null && h.Dominates(exitPoint) && exitPoint.Predecessors.Count == 1 && !HasReachableExit(exitPoint)) { // If the exit point is reachable from just one single "break;", // it's better to move the code into the switch. // (unlike loops which should not be nested unless necessary, // nesting switches makes it clearer in which cases a piece of code is reachable) nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren)); foreach (var node in nodesInSwitch) { node.Visited = true; } exitPoint = null; } context.Step("Create BlockContainer for switch", switchInst); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(nodesInSwitch[0] == h); foreach (var node in nodesInSwitch) { node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node), "The switch body must be dominated by the switch head"); } BlockContainer switchContainer = new BlockContainer(ContainerKind.Switch); Block newEntryPoint = new Block(); newEntryPoint.AddILRange(switchInst); switchContainer.Blocks.Add(newEntryPoint); newEntryPoint.Instructions.Add(switchInst); block.Instructions[block.Instructions.Count - 1] = switchContainer; Block exitTargetBlock = (Block)exitPoint?.UserData; if (exitTargetBlock != null) { block.Instructions.Add(new Branch(exitTargetBlock)); } switchContainer.AddILRange(newEntryPoint); MoveBlocksIntoContainer(nodesInSwitch, switchContainer); // Rewrite branches within the loop from oldEntryPoint to newEntryPoint: foreach (var branch in switchContainer.Descendants.OfType()) { if (branch.TargetBlock == exitTargetBlock) { branch.ReplaceWith(new Leave(switchContainer).WithILRange(branch)); } } isSwitch = false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/RemoveRedundantReturn.cs ================================================ // Copyright (c) Daniel Grunwald // // 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. #nullable enable using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// Similar to , but acts only on leave instructions /// leaving the whole function (return/yield break) that can be made implicit /// without using goto. /// class RemoveRedundantReturn : IILTransform { public void Run(ILFunction function, ILTransformContext context) { foreach (var lambda in function.Descendants.OfType()) { if (lambda.Body is BlockContainer c && ((lambda.AsyncReturnType ?? lambda.ReturnType).Kind == TypeSystem.TypeKind.Void || lambda.IsIterator)) { Block lastBlock = c.Blocks.Last(); if (lastBlock.Instructions.Last() is Leave { IsLeavingFunction: true }) { ConvertReturnToFallthrough(lastBlock.Instructions.SecondToLastOrDefault()); } else { if (ConvertReturnToFallthrough(lastBlock.Instructions.Last())) { lastBlock.Instructions.Add(new Leave(c)); } } } } } private static bool ConvertReturnToFallthrough(ILInstruction? inst) { bool result = false; switch (inst) { case BlockContainer c when c.Kind == ContainerKind.Normal: // body of try block, or similar: recurse into last instruction in container // Note: no need to handle loops/switches here; those already were handled by DetectExitPoints Block lastBlock = c.Blocks.Last(); if (lastBlock.Instructions.Last() is Leave { IsLeavingFunction: true, Value: Nop } leave) { leave.TargetContainer = c; result = true; } else if (ConvertReturnToFallthrough(lastBlock.Instructions.Last())) { lastBlock.Instructions.Add(new Leave(c)); result = true; } break; case TryCatch tryCatch: result |= ConvertReturnToFallthrough(tryCatch.TryBlock); foreach (var h in tryCatch.Handlers) { result |= ConvertReturnToFallthrough(h.Body); } break; case TryFinally tryFinally: result |= ConvertReturnToFallthrough(tryFinally.TryBlock); break; case LockInstruction lockInst: result |= ConvertReturnToFallthrough(lockInst.Body); break; case UsingInstruction usingInst: result |= ConvertReturnToFallthrough(usingInst.Body); break; case PinnedRegion pinnedRegion: result |= ConvertReturnToFallthrough(pinnedRegion.Body); break; case IfInstruction ifInstruction: result |= ConvertReturnToFallthrough(ifInstruction.TrueInst); result |= ConvertReturnToFallthrough(ifInstruction.FalseInst); break; case Block block when block.Kind == BlockKind.ControlFlow: { var lastInst = block.Instructions.LastOrDefault(); if (lastInst is Leave { IsLeavingFunction: true, Value: Nop }) { block.Instructions.RemoveAt(block.Instructions.Count - 1); result = true; lastInst = block.Instructions.LastOrDefault(); } result |= ConvertReturnToFallthrough(lastInst); break; } } return result; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs ================================================ // Copyright (c) 2012 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { enum StateRangeAnalysisMode { IteratorMoveNext, IteratorDispose, AsyncMoveNext, AwaitInFinally } /// /// Symbolically executes code to determine which blocks are reachable for which values /// of the 'state' field. /// /// /// Assumption: there are no loops/backward jumps /// We 'run' the code, with "state" being a symbolic variable /// so it can form expressions like "state + x" (when there's a sub instruction) /// /// For each block, we maintain the set of values for state for which the block is reachable. /// This is (int.MinValue, int.MaxValue) for the first instruction. /// These ranges are propagated depending on the conditional jumps performed by the code. /// class StateRangeAnalysis { public CancellationToken CancellationToken; readonly StateRangeAnalysisMode mode; readonly IField? stateField; readonly bool legacyVisualBasic; readonly SymbolicEvaluationContext evalContext; readonly Dictionary ranges = new Dictionary(); readonly Dictionary? rangesForLeave; // used only for AwaitInFinally readonly internal Dictionary? finallyMethodToStateRange; // used only for IteratorDispose internal ILVariable? doFinallyBodies; internal ILVariable? skipFinallyBodies; public StateRangeAnalysis(StateRangeAnalysisMode mode, IField? stateField, ILVariable? cachedStateVar = null, bool legacyVisualBasic = false) { this.mode = mode; this.stateField = stateField; this.legacyVisualBasic = legacyVisualBasic; if (mode == StateRangeAnalysisMode.IteratorDispose) { finallyMethodToStateRange = new Dictionary(); } if (mode == StateRangeAnalysisMode.AwaitInFinally) { rangesForLeave = new Dictionary(); } evalContext = new SymbolicEvaluationContext(stateField, legacyVisualBasic); if (cachedStateVar != null) evalContext.AddStateVariable(cachedStateVar); } public IEnumerable CachedStateVars { get => evalContext.StateVariables; } /// /// Creates a new StateRangeAnalysis with the same settings, including any cached state vars /// discovered by this analysis. /// However, the new analysis has a fresh set of result ranges. /// internal StateRangeAnalysis CreateNestedAnalysis() { var sra = new StateRangeAnalysis(mode, stateField, legacyVisualBasic: legacyVisualBasic); sra.doFinallyBodies = this.doFinallyBodies; sra.skipFinallyBodies = this.skipFinallyBodies; foreach (var v in this.evalContext.StateVariables) { sra.evalContext.AddStateVariable(v); } return sra; } /// /// Assign state ranges for all blocks within 'inst'. /// /// /// The set of states for which the exit point of the instruction is reached. /// This must be a subset of the input set. /// /// Returns an empty set for unsupported instructions. /// public LongSet AssignStateRanges(ILInstruction inst, LongSet stateRange) { CancellationToken.ThrowIfCancellationRequested(); switch (inst) { case BlockContainer blockContainer: AddStateRange(blockContainer.EntryPoint, stateRange); foreach (var block in blockContainer.Blocks) { // We assume that there are no jumps to blocks already processed. // TODO: is SortBlocks() guaranteeing this, even if the user code has loops? if (ranges.TryGetValue(block, out stateRange)) { AssignStateRanges(block, stateRange); } } // Since we don't track 'leave' edges, we can only conservatively // return LongSet.Empty. return LongSet.Empty; case Block block: foreach (var instInBlock in block.Instructions) { if (stateRange.IsEmpty) break; var oldStateRange = stateRange; stateRange = AssignStateRanges(instInBlock, stateRange); // End-point can only be reachable in a subset of the states where the start-point is reachable. Debug.Assert(stateRange.IsSubsetOf(oldStateRange)); // If the end-point is unreachable, it must be reachable in no states. Debug.Assert(stateRange.IsEmpty || !instInBlock.HasFlag(InstructionFlags.EndPointUnreachable)); } return stateRange; case TryFinally tryFinally when mode == StateRangeAnalysisMode.IteratorDispose: var afterTry = AssignStateRanges(tryFinally.TryBlock, stateRange); // really finally should start with 'stateRange.UnionWith(afterTry)', but that's // equal to 'stateRange'. Debug.Assert(afterTry.IsSubsetOf(stateRange)); var afterFinally = AssignStateRanges(tryFinally.FinallyBlock, stateRange); return afterTry.IntersectWith(afterFinally); case SwitchInstruction switchInst: SymbolicValue val = evalContext.Eval(switchInst.Value); if (val.Type != SymbolicValueType.State) goto default; List exitIntervals = new List(); foreach (var section in switchInst.Sections) { // switch (state + Constant) // matches 'case VALUE:' // iff (state + Constant == value) // iff (state == value - Constant) var effectiveLabels = section.Labels.AddOffset(unchecked(-val.Constant)); var result = AssignStateRanges(section.Body, stateRange.IntersectWith(effectiveLabels)); exitIntervals.AddRange(result.Intervals); } // exitIntervals = union of exits of all sections return new LongSet(exitIntervals); case IfInstruction ifInst: val = evalContext.Eval(ifInst.Condition).AsBool(); if (val.Type != SymbolicValueType.StateInSet) { goto default; } LongSet trueRanges = val.ValueSet; var afterTrue = AssignStateRanges(ifInst.TrueInst, stateRange.IntersectWith(trueRanges)); var afterFalse = AssignStateRanges(ifInst.FalseInst, stateRange.ExceptWith(trueRanges)); return afterTrue.UnionWith(afterFalse); case Branch br: AddStateRange(br.TargetBlock, stateRange); return LongSet.Empty; case Leave leave when mode == StateRangeAnalysisMode.AwaitInFinally: AddStateRangeForLeave(leave.TargetContainer, stateRange); return LongSet.Empty; case Nop nop: return stateRange; case StLoc stloc when stloc.Variable == doFinallyBodies || stloc.Variable == skipFinallyBodies: // pre-roslyn async/await uses a generated 'bool doFinallyBodies'; // do not treat this as user code. // Mono also does this for yield-return. return stateRange; case StLoc stloc: val = evalContext.Eval(stloc.Value); if (val.Type == SymbolicValueType.State && val.Constant == 0) { evalContext.AddStateVariable(stloc.Variable); return stateRange; } else { goto default; // user code } case Call call when mode == StateRangeAnalysisMode.IteratorDispose: // Call to finally method. // Usually these are in finally blocks, but sometimes (e.g. foreach over array), // the C# compiler puts the call to a finally method outside the try-finally block. finallyMethodToStateRange!.Add((IMethod)call.Method.MemberDefinition, stateRange); return LongSet.Empty; // return Empty since we executed user code (the finally method) case StObj stobj when mode == StateRangeAnalysisMode.IteratorMoveNext: { if (stobj.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && field.MemberDefinition == stateField && value.MatchLdcI4(-1)) { // Mono resets the state field during MoveNext(); // don't consider this user code. return stateRange; } else { goto default; } } case StObj stobj when mode == StateRangeAnalysisMode.IteratorDispose: { if (stobj.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis()) { if (field.MemberDefinition == stateField && value.MatchLdcI4(-2)) { // Roslyn 4.13 sets the state field in Dispose() to mark the iterator as disposed, // don't consider this user code. return stateRange; } else if (value.MatchDefaultOrNullOrZero()) { // Roslyn 4.13 clears any local hoisted local variables in Dispose(), // don't consider this user code. return stateRange; } else { goto default; } } else { goto default; } } default: // User code - abort analysis if (mode == StateRangeAnalysisMode.IteratorDispose && !(inst is Leave l && l.IsLeavingFunction)) { throw new SymbolicAnalysisFailedException("Unexpected instruction in Iterator.Dispose()"); } return LongSet.Empty; } } private void AddStateRange(Block block, LongSet stateRange) { if (ranges.TryGetValue(block, out var existingRange)) ranges[block] = stateRange.UnionWith(existingRange); else ranges.Add(block, stateRange); } private void AddStateRangeForLeave(BlockContainer target, LongSet stateRange) { if (rangesForLeave!.TryGetValue(target, out var existingRange)) rangesForLeave[target] = stateRange.UnionWith(existingRange); else rangesForLeave.Add(target, stateRange); } /// /// Gets a mapping from states to blocks. /// /// Within the given container (which should be the container that was analyzed), /// the mapping prefers the last block. /// Blocks outside of the given container are preferred over blocks within the container. /// public LongDict GetBlockStateSetMapping(BlockContainer container) { return LongDict.Create(GetMapping()); IEnumerable<(LongSet, Block)> GetMapping() { // First, consider container exits: foreach (var (block, states) in ranges) { if (block.Parent != container) yield return (states, block); } // Then blocks within the container: foreach (var block in container.Blocks.Reverse()) { if (ranges.TryGetValue(block, out var states)) { yield return (states, block); } } } } public LongDict GetBlockStateSetMappingForLeave() { Debug.Assert(mode == StateRangeAnalysisMode.AwaitInFinally); return LongDict.Create(rangesForLeave!.Select(kv => (kv.Value, kv.Key))); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// C# switch statements are not necessarily compiled into IL switch instructions. /// For example, when the label values are not contiguous, the C# compiler /// will generate if statements similar to a binary search. /// /// This class analyses such sequences of if statements to reconstruct the original switch. /// /// /// This analysis expects to be run on basic blocks (not extended basic blocks). /// class SwitchAnalysis { /// /// The variable that is used to represent the switch expression. /// null while analyzing the first block. /// ILVariable switchVar; /// /// The variable to be used as the argument of the switch instruction. /// public ILVariable SwitchVariable => switchVar; /// /// Whether at least one of the analyzed blocks contained an IL switch constructors. /// public bool ContainsILSwitch { get; private set; } /// /// Gets the sections that were detected by the previous AnalyzeBlock() call. /// public readonly List> Sections = new List>(); /// /// Used to de-duplicate sections with a branch instruction. /// Invariant: (Sections[targetBlockToSectionIndex[branch.TargetBlock]].Instruction as Branch).TargetBlock == branch.TargetBlock /// readonly Dictionary targetBlockToSectionIndex = new Dictionary(); /// /// Used to de-duplicate sections with a value-less leave instruction. /// Invariant: (Sections[targetBlockToSectionIndex[leave.TargetContainer]].Instruction as Leave).TargetContainer == leave.TargetContainer /// readonly Dictionary targetContainerToSectionIndex = new Dictionary(); /// /// Blocks that can be deleted if the tail of the initial block is replaced with a switch instruction. /// public readonly List InnerBlocks = new List(); public Block RootBlock { get; private set; } /// /// Gets/sets whether to allow unreachable cases in switch instructions. /// public bool AllowUnreachableCases { get; set; } /// /// Analyze the last two statements in the block and see if they can be turned into a /// switch instruction. /// /// true if the block could be analyzed successfully; false otherwise public bool AnalyzeBlock(Block block) { switchVar = null; RootBlock = block; targetBlockToSectionIndex.Clear(); targetContainerToSectionIndex.Clear(); Sections.Clear(); InnerBlocks.Clear(); ContainsILSwitch = false; return AnalyzeBlock(block, LongSet.Universe, tailOnly: true); } /// /// Analyzes the tail end (last two instructions) of a block. /// /// /// Sets switchVar and defaultInstruction if they are null, /// and adds found sections to sectionLabels and sectionInstructions. /// /// If the function returns false, sectionLabels and sectionInstructions are unmodified. /// /// The block to analyze. /// The possible values of the "interesting" variable /// when control flow reaches this block. /// If true, analyze only the tail (last two instructions). /// If false, analyze the whole block. bool AnalyzeBlock(Block block, LongSet inputValues, bool tailOnly = false) { if (block.Instructions.Count == 0) { // might happen if the block was already marked for deletion in SwitchDetection return false; } if (tailOnly) { Debug.Assert(block == RootBlock); } else { Debug.Assert(switchVar != null); // switchVar should always be determined by the top-level call if (block.IncomingEdgeCount != 1 || block == RootBlock) return false; // for now, let's only consider if-structures that form a tree if (block.Parent != RootBlock.Parent) return false; // all blocks should belong to the same container } LongSet trueValues; if (block.Instructions.Count >= 2 && block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst) && AnalyzeCondition(condition, out trueValues) ) { if (!(tailOnly || block.Instructions.Count == 2)) return false; trueValues = trueValues.IntersectWith(inputValues); if (trueValues.SetEquals(inputValues) || trueValues.IsEmpty) return false; Block trueBlock; if (trueInst.MatchBranch(out trueBlock) && AnalyzeBlock(trueBlock, trueValues)) { // OK, true block was further analyzed. InnerBlocks.Add(trueBlock); } else { // Create switch section for trueInst. AddSection(trueValues, trueInst); } } else if (block.Instructions.Last() is SwitchInstruction switchInst) { if (!(tailOnly || block.Instructions.Count == 1)) return false; if (AnalyzeSwitch(switchInst, inputValues)) { ContainsILSwitch = true; // OK return true; } else { // switch analysis failed (e.g. switchVar mismatch) return false; } } else { // unknown inst return false; } var remainingValues = inputValues.ExceptWith(trueValues); ILInstruction falseInst = block.Instructions.Last(); Block falseBlock; if (falseInst.MatchBranch(out falseBlock) && AnalyzeBlock(falseBlock, remainingValues)) { // OK, false block was further analyzed. InnerBlocks.Add(falseBlock); } else { // Create switch section for falseInst. AddSection(remainingValues, falseInst); } return true; } private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues) { Debug.Assert(!inst.IsLifted); long offset; if (MatchSwitchVar(inst.Value)) { offset = 0; } else if (inst.Value is BinaryNumericInstruction bop) { if (bop.CheckForOverflow) return false; if (MatchSwitchVar(bop.Left) && bop.Right.MatchLdcI(out long val)) { switch (bop.Operator) { case BinaryNumericOperator.Add: offset = unchecked(-val); break; case BinaryNumericOperator.Sub: offset = val; break; default: // unknown bop.Operator return false; } } else { // unknown bop.Left return false; } } else { // unknown inst.Value return false; } foreach (var section in inst.Sections) { var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues); if (!AllowUnreachableCases && matchValues.IsEmpty) return false; if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) { InnerBlocks.Add(targetBlock); } else { AddSection(matchValues, section.Body); } } return true; } /// /// Adds a new section to the Sections list. /// /// If the instruction is a branch instruction, unify the new section with an existing section /// that also branches to the same target. /// void AddSection(LongSet values, ILInstruction inst) { if (values.IsEmpty) { return; } if (inst.MatchBranch(out Block targetBlock)) { if (targetBlockToSectionIndex.TryGetValue(targetBlock, out int index)) { Sections[index] = new KeyValuePair( Sections[index].Key.UnionWith(values), inst ); } else { targetBlockToSectionIndex.Add(targetBlock, Sections.Count); Sections.Add(new KeyValuePair(values, inst)); } } else if (inst.MatchLeave(out BlockContainer targetContainer)) { if (targetContainerToSectionIndex.TryGetValue(targetContainer, out int index)) { Sections[index] = new KeyValuePair( Sections[index].Key.UnionWith(values), inst ); } else { targetContainerToSectionIndex.Add(targetContainer, Sections.Count); Sections.Add(new KeyValuePair(values, inst)); } } else { Sections.Add(new KeyValuePair(values, inst)); } } bool MatchSwitchVar(ILInstruction inst) { if (switchVar != null) return inst.MatchLdLoc(switchVar); else return inst.MatchLdLoc(out switchVar); } bool MatchSwitchVar(ILInstruction inst, out long sub) { if (inst is BinaryNumericInstruction bn && bn.Operator == BinaryNumericOperator.Sub && !bn.CheckForOverflow && !bn.IsLifted && bn.Right.MatchLdcI(out sub)) { return MatchSwitchVar(bn.Left); } sub = 0; return MatchSwitchVar(inst); } /// /// Analyzes the boolean condition, returning the set of values of the interesting /// variable for which the condition evaluates to true. /// private bool AnalyzeCondition(ILInstruction condition, out LongSet trueValues) { if (condition is Comp comp && MatchSwitchVar(comp.Left, out var sub) && comp.Right.MatchLdcI(out long val)) { // if (comp((V - sub) OP val)) trueValues = MakeSetWhereComparisonIsTrue(comp.Kind, val, comp.Sign); trueValues = trueValues.AddOffset(sub); return true; } else if (MatchSwitchVar(condition)) { // if (ldloc V) --> branch for all values except 0 trueValues = new LongSet(0).Invert(); return true; } else if (condition.MatchLogicNot(out ILInstruction arg)) { // if (logic.not(X)) --> branch for all values where if (X) does not branch bool res = AnalyzeCondition(arg, out LongSet falseValues); trueValues = falseValues.Invert(); return res; } else { trueValues = LongSet.Empty; return false; } } /// /// Create the LongSet that contains a value x iff x compared with value is true. /// internal static LongSet MakeSetWhereComparisonIsTrue(ComparisonKind kind, long val, Sign sign) { switch (kind) { case ComparisonKind.Equality: return new LongSet(val); case ComparisonKind.Inequality: return new LongSet(val).Invert(); case ComparisonKind.LessThan: return MakeGreaterThanOrEqualSet(val, sign).Invert(); case ComparisonKind.LessThanOrEqual: return MakeLessThanOrEqualSet(val, sign); case ComparisonKind.GreaterThan: return MakeLessThanOrEqualSet(val, sign).Invert(); case ComparisonKind.GreaterThanOrEqual: return MakeGreaterThanOrEqualSet(val, sign); default: throw new ArgumentException("Invalid ComparisonKind"); } } private static LongSet MakeGreaterThanOrEqualSet(long val, Sign sign) { if (sign == Sign.Signed) { return new LongSet(LongInterval.Inclusive(val, long.MaxValue)); } else { Debug.Assert(sign == Sign.Unsigned); if (val >= 0) { // The range val to ulong.MaxValue expressed with signed longs // is not a single contiguous range, but two ranges: return new LongSet(LongInterval.Inclusive(val, long.MaxValue)) .UnionWith(new LongSet(new LongInterval(long.MinValue, 0))); } else { return new LongSet(new LongInterval(val, 0)); } } } private static LongSet MakeLessThanOrEqualSet(long val, Sign sign) { if (sign == Sign.Signed) { return new LongSet(LongInterval.Inclusive(long.MinValue, val)); } else { Debug.Assert(sign == Sign.Unsigned); if (val >= 0) { return new LongSet(LongInterval.Inclusive(0, val)); } else { // The range 0 to (ulong)val expressed with signed longs // is not a single contiguous range, but two ranges: return new LongSet(LongInterval.Inclusive(0, long.MaxValue)) .UnionWith(new LongSet(LongInterval.Inclusive(long.MinValue, val))); } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// C# switch statements are not necessarily compiled into /// IL switch instructions (e.g. when the integer values are non-contiguous). /// /// Detect sequences of conditional branches that all test a single integer value, /// and simplify them into a ILAst switch instruction (which like C# does not require contiguous values). /// public class SwitchDetection : IILTransform { private readonly SwitchAnalysis analysis = new SwitchAnalysis(); private ILTransformContext context; private BlockContainer currentContainer; private ControlFlowGraph controlFlowGraph; private LoopContext loopContext; /// /// When detecting a switch, it is important to distinguish Branch instructions which will /// eventually decompile to continue; statements. /// /// A LoopContext is constructed for a node and its dominator tree, as for a Branch to be a continue; /// statement, it must be contained within the target-loop /// /// This class also supplies the depth of the loop targetted by a continue; statement relative to the /// context node, to avoid (or eventually support) labelled continues to outer loops /// public class LoopContext { private readonly IDictionary continueDepth = new Dictionary(); public LoopContext(ControlFlowGraph cfg, ControlFlowNode contextNode) { var loopHeads = new List(); void Analyze(ControlFlowNode n) { if (n.Visited) return; n.Visited = true; if (n.Dominates(contextNode)) loopHeads.Add(n); else n.Successors.ForEach(Analyze); } contextNode.Successors.ForEach(Analyze); ResetVisited(cfg.cfg); int l = 1; foreach (var loopHead in loopHeads.OrderBy(n => n.PostOrderNumber)) continueDepth[FindContinue(loopHead)] = l++; } private static ControlFlowNode FindContinue(ControlFlowNode loopHead) { // potential continue target var pred = loopHead.Predecessors.OnlyOrDefault(p => p != loopHead && loopHead.Dominates(p)); if (pred == null) return loopHead; // match for loop increment block if (pred.Successors.Count == 1) { if (HighLevelLoopTransform.MatchIncrementBlock((Block)pred.UserData, out var target) && target == loopHead.UserData) return pred; } // match do-while condition if (pred.Successors.Count <= 2) { if (HighLevelLoopTransform.MatchDoWhileConditionBlock((Block)pred.UserData, out var t1, out var t2) && (t1 == loopHead.UserData || t2 == loopHead.UserData)) return pred; } return loopHead; } public bool MatchContinue(ControlFlowNode node) => MatchContinue(node, out var _); public bool MatchContinue(ControlFlowNode node, int depth) => MatchContinue(node, out int _depth) && depth == _depth; public bool MatchContinue(ControlFlowNode node, out int depth) => continueDepth.TryGetValue(node, out depth); public int GetContinueDepth(ControlFlowNode node) => MatchContinue(node, out var depth) ? depth : 0; /// /// Lists all potential targets for break; statements from a domination tree, /// assuming the domination tree must be exited via either break; or continue; /// /// First list all nodes in the dominator tree (excluding continue nodes) /// Then return the all successors not contained within said tree. /// /// Note that node will be returned once for each outgoing edge. /// Labelled continue statements (depth > 1) are counted as break targets /// internal IEnumerable GetBreakTargets(ControlFlowNode dominator) => TreeTraversal.PreOrder(dominator, n => n.DominatorTreeChildren.Where(c => !MatchContinue(c))) .SelectMany(n => n.Successors) .Where(n => !dominator.Dominates(n) && !MatchContinue(n, 1)); } public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.SparseIntegerSwitch) return; this.context = context; analysis.AllowUnreachableCases = context.Settings.RemoveDeadCode; foreach (var container in function.Descendants.OfType()) { currentContainer = container; controlFlowGraph = null; bool blockContainerNeedsCleanup = false; foreach (var block in container.Blocks) { context.CancellationToken.ThrowIfCancellationRequested(); ProcessBlock(block, ref blockContainerNeedsCleanup); } if (blockContainerNeedsCleanup) { Debug.Assert(container.Blocks.All(b => b.Instructions.Count != 0 || b.IncomingEdgeCount == 0)); // if the original code has an unreachable switch-like condition // eg. if (i >= 0) { ... } else if (i == 2) { unreachable } // then the 'i == 2' block head gets consumed and the unreachable code needs deleting if (context.Settings.RemoveDeadCode) container.SortBlocks(deleteUnreachableBlocks: true); else container.Blocks.RemoveAll(b => b.Instructions.Count == 0); } } } void ProcessBlock(Block block, ref bool blockContainerNeedsCleanup) { bool analysisSuccess = analysis.AnalyzeBlock(block); if (analysisSuccess && UseCSharpSwitch(out _)) { // complex multi-block switch that can be combined into a single SwitchInstruction ILInstruction switchValue = new LdLoc(analysis.SwitchVariable); Debug.Assert(switchValue.ResultType.IsIntegerType() || switchValue.ResultType == StackType.Unknown); if (!(switchValue.ResultType == StackType.I4 || switchValue.ResultType == StackType.I8)) { // switchValue must have a result type of either I4 or I8 switchValue = new Conv(switchValue, PrimitiveType.I8, false, Sign.Signed); } var sw = new SwitchInstruction(switchValue); foreach (var section in analysis.Sections) { sw.Sections.Add(new SwitchSection { Labels = section.Key, Body = section.Value }); } if (block.Instructions.Last() is SwitchInstruction) { // we'll replace the switch } else { Debug.Assert(block.Instructions.SecondToLastOrDefault() is IfInstruction); // Remove branch/leave after if; it's getting moved into a section. block.Instructions.RemoveAt(block.Instructions.Count - 1); } sw.AddILRange(block.Instructions[block.Instructions.Count - 1]); block.Instructions[block.Instructions.Count - 1] = sw; // mark all inner blocks that were converted to the switch statement for deletion foreach (var innerBlock in analysis.InnerBlocks) { Debug.Assert(innerBlock.Parent == block.Parent); Debug.Assert(innerBlock != ((BlockContainer)block.Parent).EntryPoint); innerBlock.Instructions.Clear(); } controlFlowGraph = null; // control flow graph is no-longer valid blockContainerNeedsCleanup = true; SortSwitchSections(sw); } else { // 2nd pass of SimplifySwitchInstruction (after duplicating return blocks), // (1st pass was in ControlFlowSimplification) SimplifySwitchInstruction(block, context); } } internal static void SimplifySwitchInstruction(Block block, ILTransformContext context) { // due to our of of basic blocks at this point, // switch instructions can only appear as last insturction var sw = block.Instructions.LastOrDefault() as SwitchInstruction; if (sw == null) return; // ControlFlowSimplification runs early (before any other control flow transforms). // Any switch instructions will only have branch instructions in the sections. // Combine sections with identical branch target: var dict = new Dictionary(); // branch target -> switch section sw.Sections.RemoveAll( section => { if (section.Body.MatchBranch(out Block target)) { if (dict.TryGetValue(target, out SwitchSection primarySection)) { primarySection.Labels = primarySection.Labels.UnionWith(section.Labels); primarySection.HasNullLabel |= section.HasNullLabel; return true; // remove this section } else { dict.Add(target, section); } } return false; }); AdjustLabels(sw, context); SortSwitchSections(sw); } static void SortSwitchSections(SwitchInstruction sw) { sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body switch { Branch b => b.TargetILOffset, Leave l => l.StartILOffset, _ => (int?)null }).ThenBy(s => s.Labels.Values.FirstOrDefault())); } static void AdjustLabels(SwitchInstruction sw, ILTransformContext context) { if (sw.Value is BinaryNumericInstruction bop && !bop.CheckForOverflow && bop.Right.MatchLdcI(out long val)) { // Move offset into labels: context.Step("Move offset into switch labels", bop); long offset; switch (bop.Operator) { case BinaryNumericOperator.Add: offset = unchecked(-val); break; case BinaryNumericOperator.Sub: offset = val; break; default: // unknown bop.Operator return; } sw.Value = bop.Left; foreach (var section in sw.Sections) { section.Labels = section.Labels.AddOffset(offset); } } } const ulong MaxValuesPerSection = 100; /// /// Tests whether we should prefer a switch statement over an if statement. /// private bool UseCSharpSwitch(out KeyValuePair defaultSection) { if (!analysis.InnerBlocks.Any()) { defaultSection = default; return false; } defaultSection = analysis.Sections.FirstOrDefault(s => s.Key.Count() > MaxValuesPerSection); if (defaultSection.Value == null) { // no default section found? // This should never happen, as we'd need 2^64/MaxValuesPerSection sections to hit this case... return false; } var defaultSectionKey = defaultSection.Key; if (analysis.Sections.Any(s => !s.Key.SetEquals(defaultSectionKey) && s.Key.Count() > MaxValuesPerSection)) { // Only the default section is allowed to have tons of keys. // C# doesn't support "case 1 to 100000000", and we don't want to generate // gigabytes of case labels. return false; } // good enough indicator that the surrounding code also forms a switch statement if (analysis.ContainsILSwitch || MatchRoslynSwitchOnString()) return true; // heuristic to determine if a block would be better represented as an if statement rather than switch int ifCount = analysis.InnerBlocks.Count + 1; int intervalCount = analysis.Sections.Where(s => !s.Key.SetEquals(defaultSectionKey)).Sum(s => s.Key.Intervals.Length); if (ifCount < intervalCount) return false; (var flowNodes, var caseNodes) = AnalyzeControlFlow(); // don't create switch statements with only one non-default label when the corresponding condition tree is flat // it may be important that the switch-like conditions be inlined // for example, a loop condition: while (c == '\n' || c == '\r') if (analysis.Sections.Count == 2 && IsSingleCondition(flowNodes, caseNodes)) return false; // if there is no ILSwitch, there's still many control flow patterns that // match a switch statement but were originally just regular if statements, // and converting them to switches results in poor quality code with goto statements // // If a single break target cannot be identified, then the equivalent switch statement would require goto statements. // These goto statements may be "goto case x" or "goto default", but these are a hint that the original code was not a switch, // and that the switch statement may be very poor quality. // Thus the rule of thumb is no goto statements if the original code didn't include them if (SwitchUsesGoto(flowNodes, caseNodes, out var breakBlock)) return false; // valid switch construction, all code can be inlined if (breakBlock == null) return true; // The switch has a single break target and there is one more hint // The break target cannot be inlined, and should have the highest IL offset of everything targetted by the switch return breakBlock.StartILOffset >= analysis.Sections.Select(s => s.Value.MatchBranch(out var b) ? b.StartILOffset : -1).Max(); } /// /// stloc switchValueVar(call ComputeStringHash(switchValue)) /// private bool MatchRoslynSwitchOnString() { var insns = analysis.RootBlock.Instructions; return insns.Count >= 3 && SwitchOnStringTransform.MatchComputeStringOrReadOnlySpanHashCall(insns[insns.Count - 3], analysis.SwitchVariable, out _); } /// /// Builds the control flow graph for the current container (if necessary), establishes loopContext /// and returns the ControlFlowNodes corresponding to the inner flow and case blocks of the potential switch /// private (List flowNodes, List caseNodes) AnalyzeControlFlow() { if (controlFlowGraph == null) controlFlowGraph = new ControlFlowGraph(currentContainer, context.CancellationToken); var switchHead = controlFlowGraph.GetNode(analysis.RootBlock); loopContext = new LoopContext(controlFlowGraph, switchHead); var flowNodes = new List { switchHead }; flowNodes.AddRange(analysis.InnerBlocks.Select(controlFlowGraph.GetNode)); // grab the control flow nodes for blocks targetted by each section var caseNodes = new List(); foreach (var s in analysis.Sections) { if (!s.Value.MatchBranch(out var block)) continue; if (block.Parent == currentContainer) { var node = controlFlowGraph.GetNode(block); if (!loopContext.MatchContinue(node)) caseNodes.Add(node); } } AddNullCase(flowNodes, caseNodes); Debug.Assert(flowNodes.SelectMany(n => n.Successors) .All(n => flowNodes.Contains(n) || caseNodes.Contains(n) || loopContext.MatchContinue(n))); return (flowNodes, caseNodes); } /// /// Determines if the analysed switch can be constructed without any gotos /// private bool SwitchUsesGoto(List flowNodes, List caseNodes, out Block breakBlock) { // cases with predecessors that aren't part of the switch logic // must either require "goto case" statements, or consist of a single "break;" var externalCases = caseNodes.Where(c => c.Predecessors.Any(n => !flowNodes.Contains(n))).ToList(); breakBlock = null; if (externalCases.Count > 1) return true; // cannot have more than one break case without gotos // check that case nodes flow through a single point var breakTargets = caseNodes.Except(externalCases).SelectMany(n => loopContext.GetBreakTargets(n)).ToHashSet(); // if there are multiple break targets, then gotos are required // if there are none, then the external case (if any) can be the break target if (breakTargets.Count != 1) return breakTargets.Count > 1; breakBlock = (Block)breakTargets.Single().UserData; // external case must consist of a single "break;" return externalCases.Count == 1 && breakBlock != externalCases.Single().UserData; } /// /// Does some of the analysis of SwitchOnNullableTransform to add the null case control flow /// to the results of SwitchAnalysis /// private void AddNullCase(List flowNodes, List caseNodes) { if (analysis.RootBlock.IncomingEdgeCount != 1) return; // if (comp(logic.not(call get_HasValue(ldloca nullableVar))) br NullCase // br RootBlock var nullableBlock = (Block)controlFlowGraph.GetNode(analysis.RootBlock).Predecessors.SingleOrDefault()?.UserData; if (nullableBlock == null || nullableBlock.Instructions.Count < 2 || !nullableBlock.Instructions.Last().MatchBranch(analysis.RootBlock) || !nullableBlock.Instructions.SecondToLastOrDefault().MatchIfInstruction(out var cond, out var trueInst) || !cond.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction nullableInst)) return; // could check that nullableInst is ldloc or ldloca and that the switch variable matches a GetValueOrDefault // but the effect of adding an incorrect block to the flowBlock list would only be disasterous if it branched directly // to a candidate case block // must branch to a case label, otherwise we can proceed fine and let SwitchOnNullableTransform do all the work if (!trueInst.MatchBranch(out var nullBlock) || !caseNodes.Exists(n => n.UserData == nullBlock)) return; //add the null case logic to the incoming flow blocks flowNodes.Add(controlFlowGraph.GetNode(nullableBlock)); } /// /// Pattern matching for short circuit expressions /// p /// |\ /// | n /// |/ \ /// s c /// /// where /// p: if (a) goto n; goto s; /// n: if (b) goto c; goto s; /// /// Can simplify to /// p|n /// / \ /// s c /// /// where: /// p|n: if (a && b) goto c; goto s; /// /// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression /// has a target (c) with no corresponding block (leave) /// /// A node with 2 successors /// The successor index to consider n (the other successor will be the common sibling) private static bool IsShortCircuit(ControlFlowNode parent, int side) { var node = parent.Successors[side]; var sibling = parent.Successors[side ^ 1]; if (!IsFlowNode(node) || node.Successors.Count > 2 || node.Predecessors.Count != 1) return false; return node.Successors.Contains(sibling); } /// /// A flow node contains only two instructions, the first of which is an IfInstruction /// A short circuit expression is comprised of a root block ending in an IfInstruction and one or more flow nodes /// static bool IsFlowNode(ControlFlowNode n) => ((Block)n.UserData).Instructions.FirstOrDefault() is IfInstruction; /// /// Determines whether the flowNodes are can be reduced to a single condition via short circuit operators /// private bool IsSingleCondition(List flowNodes, List caseNodes) { if (flowNodes.Count == 1) return true; var rootNode = controlFlowGraph.GetNode(analysis.RootBlock); rootNode.Visited = true; // search down the tree, marking nodes as visited while they continue the current condition var n = rootNode; while (n.Successors.Count > 0 && (n == rootNode || IsFlowNode(n))) { if (n.Successors.Count == 1) { // if there is more than one case node, then a flow node with only one successor is not part of the initial condition if (caseNodes.Count > 1) break; n = n.Successors[0]; } else { // 2 successors if (IsShortCircuit(n, 0)) n = n.Successors[0]; else if (IsShortCircuit(n, 1)) n = n.Successors[1]; else break; } n.Visited = true; if (loopContext.MatchContinue(n)) break; } var ret = flowNodes.All(f => f.Visited); ResetVisited(controlFlowGraph.cfg); return ret; } private static void ResetVisited(IEnumerable nodes) { foreach (var n in nodes) n.Visited = false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs ================================================ // 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 // 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; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { /// /// This exception is thrown when we find something else than we expect from the C# compiler. /// This aborts the analysis and makes the whole transform fail. /// class SymbolicAnalysisFailedException : Exception { public SymbolicAnalysisFailedException() { } public SymbolicAnalysisFailedException(string message) : base(message) { } } enum SymbolicValueType { /// /// Unknown value /// Unknown, /// /// int: Constant (result of ldc.i4) /// IntegerConstant, /// /// int: State + Constant /// State, /// /// This pointer (result of ldarg.0) /// This, /// /// bool: ValueSet.Contains(State) /// StateInSet, } struct SymbolicValue { public readonly int Constant; public readonly SymbolicValueType Type; public readonly LongSet ValueSet; public SymbolicValue(SymbolicValueType type, int constant = 0) { this.Type = type; this.Constant = constant; } public SymbolicValue(SymbolicValueType type, LongSet valueSet) { this.Type = type; this.Constant = 0; this.ValueSet = valueSet; } public SymbolicValue AsBool() { if (Type == SymbolicValueType.State) { // convert state integer to bool: // if (state + c) = if (state + c != 0) = if (state != -c) return new SymbolicValue(SymbolicValueType.StateInSet, new LongSet(unchecked(-Constant)).Invert()); } return this; } public override string ToString() { return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant); } } class SymbolicEvaluationContext { readonly IField stateField; readonly bool legacyVisualBasic; readonly List stateVariables = new List(); public SymbolicEvaluationContext(IField stateField, bool legacyVisualBasic = false) { this.legacyVisualBasic = legacyVisualBasic; this.stateField = stateField; } public void AddStateVariable(ILVariable v) { if (!stateVariables.Contains(v)) stateVariables.Add(v); } public IEnumerable StateVariables { get => stateVariables; } static readonly SymbolicValue Failed = new SymbolicValue(SymbolicValueType.Unknown); public SymbolicValue Eval(ILInstruction inst) { if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub && (legacyVisualBasic || !bni.CheckForOverflow)) { var left = Eval(bni.Left); var right = Eval(bni.Right); if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant) return Failed; if (right.Type != SymbolicValueType.IntegerConstant) return Failed; return new SymbolicValue(left.Type, unchecked(left.Constant - right.Constant)); } else if (inst.MatchLdFld(out var target, out var field)) { if (Eval(target).Type != SymbolicValueType.This) return Failed; if (field.MemberDefinition != stateField) return Failed; return new SymbolicValue(SymbolicValueType.State); } else if (inst.MatchLdLoc(out var loadedVariable)) { if (stateVariables.Contains(loadedVariable)) return new SymbolicValue(SymbolicValueType.State); else if (loadedVariable.Kind == VariableKind.Parameter && loadedVariable.Index < 0) return new SymbolicValue(SymbolicValueType.This); else return Failed; } else if (inst.MatchLdcI4(out var value)) { return new SymbolicValue(SymbolicValueType.IntegerConstant, value); } else if (inst is Comp comp) { var left = Eval(comp.Left); var right = Eval(comp.Right); if (left.Type == SymbolicValueType.State && right.Type == SymbolicValueType.IntegerConstant) { // bool: (state + left.Constant == right.Constant) LongSet trueSums = SwitchAnalysis.MakeSetWhereComparisonIsTrue(comp.Kind, right.Constant, comp.Sign); // symbolic value is true iff trueSums.Contains(state + left.Constant) LongSet trueStates = trueSums.AddOffset(unchecked(-left.Constant)); // symbolic value is true iff trueStates.Contains(state) return new SymbolicValue(SymbolicValueType.StateInSet, trueStates); } else if (left.Type == SymbolicValueType.StateInSet && right.Type == SymbolicValueType.IntegerConstant) { if (comp.Kind == ComparisonKind.Equality && right.Constant == 0) { // comp((x in set) == 0) ==> x not in set return new SymbolicValue(SymbolicValueType.StateInSet, left.ValueSet.Invert()); } else if (comp.Kind == ComparisonKind.Inequality && right.Constant != 0) { // comp((x in set) != 0) => x in set return new SymbolicValue(SymbolicValueType.StateInSet, left.ValueSet); } else { return Failed; } } else { return Failed; } } else { return Failed; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs ================================================ // 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 // 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.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.ControlFlow { public class YieldReturnDecompiler : IILTransform { // For a description on the code generated by the C# compiler for yield return: // http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx // The idea here is: // - Figure out whether the current method is instanciating an enumerator // - Figure out which of the fields is the state field // - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is. // See http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/03/06/ilspy-yield-return.aspx // for a description of this step. ILTransformContext context; MetadataReader metadata; /// The type that contains the function being decompiled. TypeDefinitionHandle currentType; /// The compiler-generated enumerator class. /// Set in MatchEnumeratorCreationPattern() TypeDefinitionHandle enumeratorType; /// The constructor of the compiler-generated enumerator class. /// Set in MatchEnumeratorCreationPattern() MethodDefinitionHandle enumeratorCtor; /// 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; /// The field in the compiler-generated class holding the current state of the state machine /// Set in AnalyzeCtor() for MS, MatchEnumeratorCreationPattern() or AnalyzeMoveNext() for Mono IField stateField; /// The backing field of the 'Current' property in the compiler-generated class /// Set in AnalyzeCurrentProperty() IField currentField; /// The disposing field of the compiler-generated enumerator class. /// Set in ConstructExceptionTable() for assembly compiled with Mono IField disposingField; /// Maps the fields of the compiler-generated class to the original parameters. /// Set in MatchEnumeratorCreationPattern() and ResolveIEnumerableIEnumeratorFieldMapping() readonly Dictionary fieldToParameterMap = new Dictionary(); /// This dictionary stores the information extracted from the Dispose() method: /// for each "Finally Method", it stores the set of states for which the method is being called. /// Set in ConstructExceptionTable() Dictionary finallyMethodToStateRange; /// /// For each finally method, stores the target state when entering the finally block, /// and the decompiled code of the finally method body. /// readonly Dictionary decompiledFinallyMethods = new Dictionary(); /// /// Temporary stores for 'yield break'. /// readonly List returnStores = new List(); /// /// Local bool variable in MoveNext() that signifies whether to skip finally bodies. /// 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. /// HashSet cachedStateVars; #region Run() method public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.YieldReturn) return; // abort if enumerator decompilation is disabled this.context = context; this.metadata = context.PEFile.Metadata; 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; this.fieldToParameterMap.Clear(); this.finallyMethodToStateRange = null; this.decompiledFinallyMethods.Clear(); this.returnStores.Clear(); this.skipFinallyBodies = null; this.doFinallyBodies = null; this.cachedStateVars = null; if (!MatchEnumeratorCreationPattern(function)) return; BlockContainer newBody; try { AnalyzeCtor(); AnalyzeCurrentProperty(); ResolveIEnumerableIEnumeratorFieldMapping(); ConstructExceptionTable(); newBody = AnalyzeMoveNext(function); } catch (SymbolicAnalysisFailedException ex) { function.Warnings.Add($"yield-return decompiler failed: {ex.Message}"); return; } 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 function.Variables.AddRange(newBody.Descendants.OfType().Select(inst => inst.Variable).Distinct()); PrintFinallyMethodStateRanges(newBody); // Add state machine field meta-data to parameter ILVariables. foreach (var (f, p) in fieldToParameterMap) { p.StateMachineField = f; } context.Step("Delete unreachable blocks", function); 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()) { nestedContainer.SortBlocks(deleteUnreachableBlocks: true); } // We need to clean up nested blocks before the main block, so that edges from unreachable code // in nested containers into the main container are removed before we clean up the main container. } // Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid // (though some may point to now-deleted blocks) newBody.SortBlocks(deleteUnreachableBlocks: true); function.CheckInvariant(ILPhase.Normal); try { if (isCompiledWithMono) { CleanSkipFinallyBodies(function); } else if (isCompiledWithLegacyVisualBasic) { CleanDoFinallyBodies(function); } else if (isCompiledWithVisualBasic) { CleanFinallyStateChecks(function); } else { DecompileFinallyBlocks(); ReconstructTryFinallyBlocks(function); } } catch (SymbolicAnalysisFailedException ex) { // Revert the yield-return transformation 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}"); return; } context.Step("Translate fields to local accesses", function); TranslateFieldsToLocalAccess(function, function, fieldToParameterMap, isCompiledWithMono); // On mono, we still need to remove traces of the state variable(s): if (isCompiledWithMono || isCompiledWithVisualBasic) { if (fieldToParameterMap.TryGetValue(stateField, out var stateVar)) { returnStores.AddRange(stateVar.StoreInstructions.OfType()); } foreach (var cachedStateVar in cachedStateVars) { returnStores.AddRange(cachedStateVar.StoreInstructions.OfType()); } } if (returnStores.Count > 0) { context.Step("Remove temporaries", function); foreach (var store in returnStores) { if (store.Variable.LoadCount == 0 && store.Variable.AddressCount == 0 && store.Parent is Block block) { Debug.Assert(SemanticHelper.IsPure(store.Value.Flags)); block.Instructions.Remove(store); } } } // Re-run control flow simplification over the newly constructed set of gotos, // and inlining because TranslateFieldsToLocalAccess() might have opened up new inlining opportunities. function.RunTransforms(CSharpDecompiler.EarlyILTransforms(), context); } #endregion #region Match the enumerator creation pattern bool MatchEnumeratorCreationPattern(ILFunction function) { Block body = SingleBlock(function.Body); if (body == null || body.Instructions.Count == 0) { return false; } ILInstruction newObj; if (body.Instructions.Count == 1) { // No parameters passed to enumerator (not even 'this'): // ret(newobj(...)) if (!body.Instructions[0].MatchReturn(out newObj)) return false; if (MatchEnumeratorCreationNewObj(newObj)) { return true; } else if (MatchMonoEnumeratorCreationNewObj(newObj)) { isCompiledWithMono = true; return true; } else { return false; } } // If there's parameters passed to the helper class, the class instance is first // stored in a variable, then the parameters are copied over, then the instance is returned. int pos = 0; // stloc(var_1, newobj(..)) if (!body.Instructions[pos].MatchStLoc(out var var1, out newObj)) return false; if (MatchEnumeratorCreationNewObj(newObj)) { pos++; // OK } else if (MatchMonoEnumeratorCreationNewObj(newObj)) { pos++; 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)) // or (in structs): stfld(..., ldloc(var_1), ldobj(ldloc(this))) if (!body.Instructions[pos].MatchStFld(out var ldloc, out var storedField, out var value)) break; if (!ldloc.MatchLdLoc(var1)) { return false; } if (value.MatchLdLoc(out var parameter) && parameter.Kind == VariableKind.Parameter) { fieldToParameterMap[(IField)storedField.MemberDefinition] = parameter; } else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) { // 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; } } // In debug builds, the compiler may copy the var1 into another variable (var2) before returning it. if (body.Instructions[pos].MatchStLoc(out var var2, out var ldlocForStloc2) && ldlocForStloc2.MatchLdLoc(var1)) { // stloc(var_2, ldloc(var_1)) pos++; } if (isCompiledWithMono && !stateFieldInitialized) { // Mono initializes the state field separately: // (but not if it's left at the default value 0) if (body.Instructions[pos].MatchStFld(out var target, out var field, out var value) && target.MatchLdLoc(var2 ?? var1) && (value.MatchLdcI4(-2) || value.MatchLdcI4(0))) { stateField = (IField)field.MemberDefinition; pos++; } } if (body.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdLoc(var2 ?? var1)) { // ret(ldloc(var_2)) return true; } else { return false; } } /// /// Matches the body of a method as a single basic block. /// internal static Block SingleBlock(ILInstruction body) { var block = body as Block; if (body is BlockContainer blockContainer && blockContainer.Blocks.Count == 1) { block = blockContainer.Blocks.Single(); } return block; } /// /// Matches the newobj instruction that creates an instance of the compiler-generated enumerator helper class. /// bool MatchEnumeratorCreationNewObj(ILInstruction inst) { return MatchEnumeratorCreationNewObj(inst, metadata, currentType, out enumeratorCtor, out enumeratorType); } internal static bool MatchEnumeratorCreationNewObj(ILInstruction inst, MetadataReader metadata, TypeDefinitionHandle currentType, out MethodDefinitionHandle enumeratorCtor, out TypeDefinitionHandle enumeratorType) { enumeratorCtor = default; enumeratorType = default; // newobj(CurrentType/...::.ctor, ldc.i4(-2)) if (!(inst is NewObj newObj)) return false; if (newObj.Arguments.Count != 1) return false; if (!newObj.Arguments[0].MatchLdcI4(out int initialState)) return false; if (!(initialState == -2 || initialState == 0)) return false; var handle = newObj.Method.MetadataToken; enumeratorCtor = handle.IsNil || handle.Kind != HandleKind.MethodDefinition ? default : (MethodDefinitionHandle)handle; enumeratorType = enumeratorCtor.IsNil ? default : metadata.GetMethodDefinition(enumeratorCtor).GetDeclaringType(); return (enumeratorType.IsNil ? default : metadata.GetTypeDefinition(enumeratorType).GetDeclaringType()) == currentType && IsCompilerGeneratorEnumerator(enumeratorType, metadata); } bool MatchMonoEnumeratorCreationNewObj(ILInstruction inst) { // mcs generates iterators that take no parameters in the ctor if (!(inst is NewObj newObj)) return false; if (newObj.Arguments.Count != 0) return false; var handle = newObj.Method.MetadataToken; enumeratorCtor = handle.IsNil || handle.Kind != HandleKind.MethodDefinition ? default : (MethodDefinitionHandle)handle; enumeratorType = enumeratorCtor.IsNil ? default : metadata.GetMethodDefinition(enumeratorCtor).GetDeclaringType(); return (enumeratorType.IsNil ? default : metadata.GetTypeDefinition(enumeratorType).GetDeclaringType()) == currentType && IsCompilerGeneratorEnumerator(enumeratorType, metadata); } public static bool IsCompilerGeneratorEnumerator(TypeDefinitionHandle type, MetadataReader metadata) { TypeDefinition td; if (type.IsNil || !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) return false; foreach (var i in td.GetInterfaceImplementations()) { var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata); if (!tr.IsNested && tr.TopLevelTypeName.Namespace == "System.Collections" && tr.TopLevelTypeName.Name == "IEnumerator") return true; } return false; } #endregion #region Figure out what the 'state' field is (analysis of .ctor()) /// /// Looks at the enumerator's ctor and figures out which of the fields holds the state. /// void AnalyzeCtor() { Block body = SingleBlock(CreateILAst(enumeratorCtor, context).Body); if (body == null) throw new SymbolicAnalysisFailedException("Missing enumeratorCtor.Body"); foreach (var inst in body.Instructions) { if (inst.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && value.MatchLdLoc(out var arg) && arg.Kind == VariableKind.Parameter && arg.Index == 0) { stateField = (IField)field.MemberDefinition; } } if (stateField == null && !isCompiledWithMono) throw new SymbolicAnalysisFailedException("Could not find stateField"); } /// /// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step. /// internal static ILFunction CreateILAst(MethodDefinitionHandle method, ILTransformContext context) { var metadata = context.PEFile.Metadata; if (method.IsNil) throw new SymbolicAnalysisFailedException("Method not found"); var methodDef = metadata.GetMethodDefinition(method); if (!methodDef.HasBody()) throw new SymbolicAnalysisFailedException($"Method {methodDef.Name} has no body"); GenericContext genericContext = context.Function.GenericContext; genericContext = new GenericContext( classTypeParameters: (genericContext.ClassTypeParameters ?? EmptyList.Instance) .Concat(genericContext.MethodTypeParameters ?? EmptyList.Instance).ToArray(), methodTypeParameters: null); var body = context.TypeSystem.MainModule.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress); var il = context.CreateILReader() .ReadIL(method, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken); il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, DecompileRun = context.DecompileRun }); return il; } #endregion #region Figure out what the 'current' field is (analysis of get_Current()) /// /// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value. /// void AnalyzeCurrentProperty() { MethodDefinitionHandle getCurrentMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault( m => IsMethod(m, "get_Current")); Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body); if (body == null) throw new SymbolicAnalysisFailedException("get_Current has no body"); if (body.Instructions.Count == 1) { // release builds directly return the current field // ret(ldfld F(ldloc(this))) if (body.Instructions[0].MatchReturn(out var retVal) && retVal.MatchLdFld(out var target, out var field) && target.MatchLdThis()) { currentField = (IField)field.MemberDefinition; } } else if (body.Instructions.Count == 2) { // debug builds store the return value in a temporary // stloc V = ldfld F(ldloc(this)) // ret(ldloc V) if (body.Instructions[0].MatchStLoc(out var v, out var ldfld) && ldfld.MatchLdFld(out var target, out var field) && target.MatchLdThis() && body.Instructions[1].MatchReturn(out var retVal) && retVal.MatchLdLoc(v)) { currentField = (IField)field.MemberDefinition; } } if (currentField == null) throw new SymbolicAnalysisFailedException("Could not find currentField"); } #endregion #region Figure out the mapping of IEnumerable fields to IEnumerator fields (analysis of GetEnumerator()) void ResolveIEnumerableIEnumeratorFieldMapping() { MethodDefinitionHandle getEnumeratorMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault( m => IsMethod(m, "GetEnumerator")); ResolveIEnumerableIEnumeratorFieldMapping(getEnumeratorMethod, context, fieldToParameterMap); } internal static void ResolveIEnumerableIEnumeratorFieldMapping(MethodDefinitionHandle getEnumeratorMethod, ILTransformContext context, Dictionary fieldToParameterMap) { if (getEnumeratorMethod.IsNil) return; // no mappings (maybe it's just an IEnumerator implementation?) var function = CreateILAst(getEnumeratorMethod, context); foreach (var block in function.Descendants.OfType()) { foreach (var inst in block.Instructions) { // storeTarget.storeField = this.loadField; if (inst.MatchStFld(out var storeTarget, out var storeField, out var storeValue) && storeValue.MatchLdFld(out var loadTarget, out var loadField) && loadTarget.MatchLdThis()) { storeField = (IField)storeField.MemberDefinition; loadField = (IField)loadField.MemberDefinition; if (fieldToParameterMap.TryGetValue(loadField, out var mappedParameter)) fieldToParameterMap[storeField] = mappedParameter; } } } } #endregion #region Construction of the exception table (analysis of Dispose()) // We construct the exception table by analyzing the enumerator's Dispose() method. void ConstructExceptionTable() { disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => IsMethod(m, "Dispose")); var function = CreateILAst(disposeMethod, context); if (!isCompiledWithVisualBasic && !isCompiledWithMono) { 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 && stobj.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && field.Type.IsKnownType(KnownTypeCode.Boolean) && value.MatchLdcI4(1)) { disposingField = (IField)field.MemberDefinition; break; } } // On mono and VB, we don't need to analyse Dispose() to reconstruct the try-finally structure. finallyMethodToStateRange = default; } else { // 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; } } [Conditional("DEBUG")] void PrintFinallyMethodStateRanges(BlockContainer bc) { if (finallyMethodToStateRange == null) return; foreach (var (method, stateRange) in finallyMethodToStateRange) { bc.Blocks[0].Instructions.Insert(0, new Nop { Comment = method.Name + " in " + stateRange }); } } #endregion #region Analyze MoveNext() and generate new body BlockContainer AnalyzeMoveNext(ILFunction function) { context.StepStartGroup("AnalyzeMoveNext"); MethodDefinitionHandle moveNextMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "MoveNext"); ILFunction moveNextFunction = CreateILAst(moveNextMethod, context); function.MoveNextMethod = moveNextFunction.Method; function.SequencePointCandidates = moveNextFunction.SequencePointCandidates; function.CodeSize = moveNextFunction.CodeSize; function.LocalVariableSignatureLength = moveNextFunction.LocalVariableSignatureLength; // Copy-propagate temporaries holding a copy of 'this'. // This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables. foreach (var stloc in moveNextFunction.Descendants.OfType().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) { 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) { body = (BlockContainer)tryFault.TryBlock; var faultBlockContainer = tryFault.FaultBlock as BlockContainer; if (faultBlockContainer?.Blocks.Count != 1) throw new SymbolicAnalysisFailedException("Unexpected number of blocks in MoveNext() fault block"); var faultBlock = faultBlockContainer.Blocks.Single(); if (!(faultBlock.Instructions.Count == 2 && faultBlock.Instructions[0] is Call call && call.Method.MetadataToken == disposeMethod && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() && faultBlock.Instructions[1].MatchLeave(faultBlockContainer))) { throw new SymbolicAnalysisFailedException("Unexpected fault block contents in MoveNext()"); } } 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 // yet because the compiler let it be implicitly initialized to 0. // In this case, we must discover it from the first instruction in MoveNext(): if (body.EntryPoint.Instructions[0] is StLoc stloc && stloc.Value.MatchLdFld(out var target, out var field) && target.MatchLdThis() && field.Type.IsKnownType(KnownTypeCode.Int32)) { stateField = (IField)field.MemberDefinition; } else { throw new SymbolicAnalysisFailedException("Could not find state field."); } } skipFinallyBodies = null; if (isCompiledWithMono) { // Mono uses skipFinallyBodies; find out which variable that is: foreach (var tryFinally in body.Descendants.OfType()) { if ((tryFinally.FinallyBlock as BlockContainer)?.EntryPoint.Instructions[0] is IfInstruction ifInst) { if (ifInst.Condition.MatchLogicNot(out var arg) && arg.MatchLdLoc(out var v) && v.Type.IsKnownType(KnownTypeCode.Boolean)) { bool isInitializedInEntryBlock = false; for (int i = 0; i < 3; i++) { if (body.EntryPoint.Instructions.ElementAtOrDefault(i) is StLoc stloc && stloc.Variable == v && stloc.Value.MatchLdcI4(0)) { isInitializedInEntryBlock = true; break; } } if (isInitializedInEntryBlock) { skipFinallyBodies = v; break; } } } } } PropagateCopiesOfFields(body); // Note: body may contain try-catch or try-finally statements that have nested block containers, // but those cannot contain any yield statements. // So for reconstructing the control flow, we only consider the blocks directly within body. var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField, legacyVisualBasic: isCompiledWithLegacyVisualBasic); rangeAnalysis.skipFinallyBodies = skipFinallyBodies; rangeAnalysis.doFinallyBodies = doFinallyBodies; rangeAnalysis.CancellationToken = context.CancellationToken; rangeAnalysis.AssignStateRanges(body, LongSet.Universe); cachedStateVars = rangeAnalysis.CachedStateVars.ToHashSet(); var newBody = ConvertBody(body, rangeAnalysis); moveNextFunction.Variables.Clear(); // release references from old moveNextFunction to instructions that were moved over to newBody moveNextFunction.ReleaseRef(); context.StepEndGroup(); return newBody; } private void PropagateCopiesOfFields(BlockContainer body) { // Roslyn may optimize MoveNext() by copying fields from the iterator class into local variables // at the beginning of MoveNext(). Undo this optimization. context.StepStartGroup("PropagateCopiesOfFields"); var mutableFields = body.Descendants.OfType().Where(ldflda => ldflda.Parent.OpCode != OpCode.LdObj).Select(ldflda => ldflda.Field).ToHashSet(); for (int i = 0; i < body.EntryPoint.Instructions.Count; i++) { if (body.EntryPoint.Instructions[i] is StLoc store && store.Variable.IsSingleDefinition && store.Value is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldflda.Target.MatchLdThis()) { if (!mutableFields.Contains(ldflda.Field)) { // perform copy propagation: (unlike CopyPropagation.Propagate(), copy the ldobj arguments as well) foreach (var expr in store.Variable.LoadInstructions.ToArray()) { expr.ReplaceWith(store.Value.Clone()); } body.EntryPoint.Instructions.RemoveAt(i--); } else if (ldflda.Field.MemberDefinition == stateField.MemberDefinition) { continue; } else { break; // unsupported: load of mutable field (other than state field) } } else { break; // unknown instruction } } context.StepEndGroup(); } /// /// Convert the old body (of MoveNext function) to the new body (of decompiled iterator method). /// /// * Replace the sequence /// this.currentField = expr; /// this.state = N; /// return true; /// with: /// yield return expr; /// goto blockForState(N); /// * Replace the sequence: /// this._finally2(); /// this._finally1(); /// return false; /// with: /// yield break; /// * Reconstruct try-finally blocks from /// (on enter) this.state = N; /// (on exit) this._finallyX(); /// private BlockContainer ConvertBody(BlockContainer oldBody, StateRangeAnalysis rangeAnalysis) { var blockStateMap = rangeAnalysis.GetBlockStateSetMapping(oldBody); BlockContainer newBody = new BlockContainer().WithILRange(oldBody); // create all new blocks so that they can be referenced by gotos for (int blockIndex = 0; blockIndex < oldBody.Blocks.Count; blockIndex++) { newBody.Blocks.Add(new Block().WithILRange(oldBody.Blocks[blockIndex])); } // convert contents of blocks for (int i = 0; i < oldBody.Blocks.Count; i++) { var oldBlock = oldBody.Blocks[i]; var newBlock = newBody.Blocks[i]; foreach (var oldInst in oldBlock.Instructions) { context.CancellationToken.ThrowIfCancellationRequested(); if (oldInst.MatchStFld(out var target, out var field, out var value) && target.MatchLdThis()) { if (field.MemberDefinition.Equals(stateField)) { if (value.MatchLdcI4(out int newState)) { // On state change, break up the block: // (this allows us to consider each block individually for try-finally reconstruction) newBlock = SplitBlock(newBlock, oldInst); // We keep the state-changing instruction around (as first instruction of the new block) // for reconstructing the try-finallys. } else { newBlock.Instructions.Add(new InvalidExpression("Assigned non-constant to iterator.state field").WithILRange(oldInst)); ReportError(newBlock.Instructions.Last()); continue; // don't copy over this instruction, but continue with the basic block } } else if (field.MemberDefinition.Equals(currentField)) { // create yield return newBlock.Instructions.Add(new YieldReturn(value).WithILRange(oldInst)); ConvertBranchAfterYieldReturn(newBlock, oldBlock, oldInst.ChildIndex + 1); break; // we're done with this basic block } } else if (oldInst is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() && finallyMethodToStateRange.ContainsKey((IMethod)call.Method.MemberDefinition)) { // Break up the basic block on a call to a finally method // (this allows us to consider each block individually for try-finally reconstruction) newBlock = SplitBlock(newBlock, oldInst); } else if (oldInst is TryFinally tryFinally && (isCompiledWithMono || isCompiledWithVisualBasic)) { // with mono, we have to recurse into try-finally blocks var oldTryBlock = (BlockContainer)tryFinally.TryBlock; var sra = rangeAnalysis.CreateNestedAnalysis(); 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); UpdateBranchTargets(oldInst); } } // 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(initialState) } }); return newBody; void ConvertBranchAfterYieldReturn(Block newBlock, Block oldBlock, int pos) { Block targetBlock; if (isCompiledWithMono && disposingField != null) { // Mono skips over the state assignment if 'this.disposing' is set: // ... // stfld $current(ldloc this, yield-expr) // if (ldfld $disposing(ldloc this)) br IL_007c // br targetBlock // } // // Block targetBlock (incoming: 1) { // stfld $PC(ldloc this, ldc.i4 1) // br setSkipFinallyBodies // } // // Block setSkipFinallyBodies (incoming: 2) { // stloc skipFinallyBodies(ldc.i4 1) // br returnBlock // } if (oldBlock.Instructions[pos].MatchIfInstruction(out var condition, out _) && condition.MatchLdFld(out var condTarget, out var condField) && condTarget.MatchLdThis() && condField.MemberDefinition.Equals(disposingField) && oldBlock.Instructions[pos + 1].MatchBranch(out targetBlock) && targetBlock.Parent == oldBlock.Parent) { // Keep looking at the target block: oldBlock = targetBlock; pos = 0; } } // 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) && (localNewState is null || localNewState == newState)) { pos++; } else { newBlock.Instructions.Add(new InvalidBranch("Unable to find new state assignment for yield return")); ReportError(newBlock.Instructions.Last()); return; } // Mono may have 'br setSkipFinallyBodies' here, so follow the branch if (oldBlock.Instructions[pos].MatchBranch(out targetBlock) && targetBlock.Parent == oldBlock.Parent) { oldBlock = targetBlock; pos = 0; } if (oldBlock.Instructions[pos].MatchStLoc(skipFinallyBodies, out value)) { if (!value.MatchLdcI4(1)) { newBlock.Instructions.Add(new InvalidExpression { ExpectedResultType = StackType.Void, Message = "Unexpected assignment to skipFinallyBodies" }); ReportError(newBlock.Instructions.Last()); } 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" }); ReportError(newBlock.Instructions.Last()); } pos++; } if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1)) { // OK, found return directly after state assignment } else if (oldBlock.Instructions[pos].MatchBranch(out targetBlock) && targetBlock.Instructions[0].MatchReturn(out retVal) && retVal.MatchLdcI4(1)) { // OK, jump to common return block (e.g. on Mono) } else { newBlock.Instructions.Add(new InvalidBranch("Unable to find 'return true' for yield return")); ReportError(newBlock.Instructions.Last()); return; } newBlock.Instructions.Add(MakeGoTo(newState)); } Block SplitBlock(Block newBlock, ILInstruction oldInst) { if (newBlock.Instructions.Count > 0) { var newBlock2 = new Block(); newBlock2.AddILRange(new Interval(oldInst.StartILOffset, oldInst.StartILOffset)); newBody.Blocks.Add(newBlock2); newBlock.Instructions.Add(new Branch(newBlock2)); newBlock = newBlock2; } return newBlock; } ILInstruction MakeGoTo(int v) { Block targetBlock = blockStateMap.GetOrDefault(v); if (targetBlock != null) { if (targetBlock.Parent == oldBody) return new Branch(newBody.Blocks[targetBlock.ChildIndex]); else return new Branch(targetBlock); } else { var err = new InvalidBranch("Could not find block for state " + v); ReportError(err); return err; } } void UpdateBranchTargets(ILInstruction inst) { switch (inst) { case Branch branch: if (branch.TargetContainer == oldBody) { branch.TargetBlock = newBody.Blocks[branch.TargetBlock.ChildIndex]; } break; case Leave leave: if (leave.MatchReturn(out var value)) { bool validYieldBreak = value.MatchLdcI4(0); if (value.MatchLdLoc(out var v) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot) && v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0))) { validYieldBreak = true; returnStores.AddRange(v.StoreInstructions.Cast()); } if (validYieldBreak) { // yield break leave.ReplaceWith(new Leave(newBody).WithILRange(leave)); } else { // don't treat this as an error, it might just be unreachable code that will be removed soon // (occurs with mcs yield return) leave.ReplaceWith(new InvalidBranch("Unexpected return in MoveNext()").WithILRange(leave)); } } else { if (leave.TargetContainer == oldBody) { leave.TargetContainer = newBody; } } break; } foreach (var child in inst.Children) { UpdateBranchTargets(child); } } void ReportError(ILInstruction inst) { // ConvertBody is still called within the try-catch, so we can throw SymbolicAnalysisFailedException // to suppress conversion of the state machine altogether. // We still initially create an instruction before converting a to an exception, // so that the body of this function can be commented out for testing purposes. // (this allows seeing where exactly the error occurs in the converted body output) string message = "ConvertBody error"; if (inst is InvalidBranch invalidBranch) message = invalidBranch.Message; else if (inst is InvalidExpression invalidExpr) message = invalidExpr.Message; throw new SymbolicAnalysisFailedException(message); } } #endregion #region TranslateFieldsToLocalAccess /// /// Translates all field accesses in `function` to local variable accesses. /// internal static void TranslateFieldsToLocalAccess(ILFunction function, ILInstruction inst, Dictionary fieldToVariableMap, bool isCompiledWithMono = false) { if (inst is LdFlda ldflda && ldflda.Target.MatchLdThis()) { var fieldDef = (IField)ldflda.Field.MemberDefinition; if (!fieldToVariableMap.TryGetValue(fieldDef, out var v)) { string name = null; if (!string.IsNullOrEmpty(fieldDef.Name) && fieldDef.Name[0] == '<') { int pos = fieldDef.Name.IndexOf('>'); if (pos > 1) name = fieldDef.Name.Substring(1, pos - 1); } v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name); v.InitialValueIsInitialized = true; // the field was default-initialized, so keep those semantics for the variable v.UsesInitialValue = true; v.StateMachineField = ldflda.Field; fieldToVariableMap.Add(fieldDef, v); } if (v.StackType == StackType.Ref) { Debug.Assert(v.Kind == VariableKind.Parameter && v.Index < 0); // this pointer inst.ReplaceWith(new LdLoc(v).WithILRange(inst)); } else { inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); } } else if (!isCompiledWithMono && inst.MatchLdThis()) { inst.ReplaceWith(new InvalidExpression("stateMachine") { ExpectedResultType = inst.ResultType }.WithILRange(inst)); } else { foreach (var child in inst.Children) { TranslateFieldsToLocalAccess(function, child, fieldToVariableMap, isCompiledWithMono); } if (inst is LdObj ldobj && ldobj.Target is LdLoca ldloca && ldloca.Variable.StateMachineField != null) { LdLoc ldloc = new LdLoc(ldloca.Variable); ldloc.AddILRange(ldobj); ldloc.AddILRange(ldloca); inst.ReplaceWith(ldloc); } else if (inst is StObj stobj && stobj.Target is LdLoca ldloca2 && ldloca2.Variable.StateMachineField != null) { StLoc stloc = new StLoc(ldloca2.Variable, stobj.Value); stloc.AddILRange(stobj); stloc.AddILRange(ldloca2); inst.ReplaceWith(stloc); } } } #endregion #region DecompileFinallyBlocks void DecompileFinallyBlocks() { foreach (var method in finallyMethodToStateRange.Keys) { var function = CreateILAst((MethodDefinitionHandle)method.MetadataToken, context); var body = (BlockContainer)function.Body; var newState = GetNewState(body.EntryPoint); if (newState != null) { body.EntryPoint.Instructions.RemoveAt(0); } // Avoid yield-return decompilation if there are unrecognized state assignments in a finally method. foreach (var inst in body.Descendants) { if (IsStateAssignment(inst)) throw new SymbolicAnalysisFailedException($"Unknown state transition in {function.Name ?? "finally"} at IL_{inst.StartILOffset:x4}"); } function.ReleaseRef(); // make body reusable outside of function decompiledFinallyMethods.Add(method, (newState, function)); } } #endregion #region Reconstruct try-finally blocks /// /// Reconstruct try-finally blocks. /// * The stateChanges (iterator._state = N;) tell us when to open a try-finally block /// * The calls to the finally method tell us when to leave the try block. /// /// There might be multiple stateChanges for a given try-finally block, e.g. /// both the original entry point, and the target when leaving a nested block. /// In proper C# code, the entry point of the try-finally will dominate all other code /// in the try-block, so we can use dominance to find the proper entry point. /// /// Precondition: the blocks in newBody are topologically sorted. /// void ReconstructTryFinallyBlocks(ILFunction iteratorFunction) { BlockContainer newBody = (BlockContainer)iteratorFunction.Body; context.Step("Reconstuct try-finally blocks", newBody); var blockState = new int[newBody.Blocks.Count]; blockState[0] = -1; var stateToContainer = new Dictionary(); stateToContainer.Add(-1, newBody); // First, analyse the newBody: for each block, determine the active state number. foreach (var block in newBody.Blocks) { context.CancellationToken.ThrowIfCancellationRequested(); int oldState = blockState[block.ChildIndex]; BlockContainer container; // new container for the block if (GetNewState(block) is int newState) { // OK, state change // Remove the state-changing instruction block.Instructions.RemoveAt(0); if (!stateToContainer.TryGetValue(newState, out container)) { // First time we see this state. // This means we just found the entry point of a try block. CreateTryBlock(block, newState); // CreateTryBlock() wraps the contents of 'block' with a TryFinally. // We thus need to put the block (which now contains the whole TryFinally) // into the parent container. // Assuming a state transition never enters more than one state at once, // we can use stateToContainer[oldState] as parent. container = stateToContainer[oldState]; } } else { // Because newBody is topologically sorted we because we just removed unreachable code, // we can assume that blockState[] was already set for this block. newState = oldState; container = stateToContainer[oldState]; } if (container != newBody) { // Move the block into the container. container.Blocks.Add(block); // Keep the stale reference in newBody.Blocks for now, to avoid // changing the ChildIndex of the other blocks while we use it // to index the blockState array. } #if DEBUG block.Instructions.Insert(0, new Nop { Comment = "state == " + newState }); #endif // Propagate newState to successor blocks foreach (var branch in block.Descendants.OfType()) { if (branch.TargetBlock.Parent == newBody) { int stateAfterBranch = newState; if (Block.GetPredecessor(branch) is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() && call.Method.Name == "System.IDisposable.Dispose") { // pre-roslyn compiles "yield break;" into "Dispose(); goto return_false;", // so convert the dispose call into a state transition to the final state stateAfterBranch = -1; call.ReplaceWith(new Nop() { Comment = "Dispose call" }); } Debug.Assert(blockState[branch.TargetBlock.ChildIndex] == stateAfterBranch || blockState[branch.TargetBlock.ChildIndex] == 0); blockState[branch.TargetBlock.ChildIndex] = stateAfterBranch; } } } newBody.Blocks.RemoveAll(b => b.Parent != newBody); void CreateTryBlock(Block block, int state) { var finallyMethod = FindFinallyMethod(state); if (finallyMethod != null) { // remove the method so that it doesn't cause ambiguity when processing nested try-finally blocks finallyMethodToStateRange.Remove(finallyMethod); } var tryBlock = new Block(); tryBlock.AddILRange(block); tryBlock.Instructions.AddRange(block.Instructions); var tryBlockContainer = new BlockContainer(); tryBlockContainer.Blocks.Add(tryBlock); tryBlockContainer.AddILRange(tryBlock); stateToContainer.Add(state, tryBlockContainer); ILInstruction finallyBlock; if (finallyMethod == null) { finallyBlock = new InvalidBranch($"Could not find finallyMethod for state={state}.\n" + $"Possibly this method is affected by a C# compiler bug that causes the finally body\n" + $"not to run in case of an exception or early 'break;' out of a loop consuming this iterable."); } else if (decompiledFinallyMethods.TryGetValue(finallyMethod, out var decompiledMethod)) { finallyBlock = decompiledMethod.function.Body; var vars = decompiledMethod.function.Variables.ToArray(); decompiledMethod.function.Variables.Clear(); iteratorFunction.Variables.AddRange(vars); } else { finallyBlock = new InvalidBranch("Missing decompiledFinallyMethod"); } block.Instructions.Clear(); block.Instructions.Add(new TryFinally(tryBlockContainer, finallyBlock).WithILRange(tryBlockContainer)); } IMethod FindFinallyMethod(int state) { IMethod foundMethod = null; foreach (var (method, stateRange) in finallyMethodToStateRange) { if (stateRange.Contains(state)) { if (foundMethod == null) foundMethod = method; else Debug.Fail("Ambiguous finally method for state " + state); } } return foundMethod; } } bool IsStateAssignment(ILInstruction inst) { return inst.MatchStFld(out var target, out var field, out _) && target.MatchLdThis() && field.MemberDefinition.Equals(stateField); } // Gets the state that is transitioned to at the start of the block int? GetNewState(Block block) { if (block.Instructions[0].MatchStFld(out var target, out var field, out var value) && target.MatchLdThis() && field.MemberDefinition.Equals(stateField) && value.MatchLdcI4(out int newState)) { return newState; } else if (block.Instructions[0] is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() && decompiledFinallyMethods.TryGetValue((IMethod)call.Method.MemberDefinition, out var finallyMethod)) { return finallyMethod.outerState; } return null; } #endregion #region Cleanup finally blocks /// /// Eliminates usage of doFinallyBodies /// private void CleanSkipFinallyBodies(ILFunction function) { if (skipFinallyBodies == null) { return; // only mono-compiled code uses skipFinallyBodies } context.StepStartGroup("CleanFinallyBlocks", function); if (skipFinallyBodies.StoreInstructions.Count != 0 || skipFinallyBodies.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.MatchLogicNot(out var logicNotArg) && logicNotArg.MatchLdLoc(skipFinallyBodies)) { context.Step("Remove if (skipFinallyBodies) from try-finally", tryFinally); // condition will always be true now that we're using 'yield' instructions entryPoint.Instructions[0] = ifInst.TrueInst; entryPoint.Instructions.RemoveRange(1, entryPoint.Instructions.Count - 1); // find new entryPoint after the old one was modified entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(container); } } if (entryPoint?.Instructions.Count == 2 && IsCallToMonoFinallyMethod(entryPoint.Instructions[0] as Call, out var finallyMethod) && entryPoint.Instructions[1].MatchLeave((BlockContainer)tryFinally.FinallyBlock)) { context.Step("Inline " + finallyMethod.FullName + " into finally", tryFinally); var finallyFunction = CreateILAst((MethodDefinitionHandle)finallyMethod.MetadataToken, context); tryFinally.FinallyBlock = finallyFunction.Body; var variables = finallyFunction.Variables.ToArray(); finallyFunction.Variables.Clear(); function.Variables.AddRange(variables); } } context.StepEndGroup(keepIfEmpty: true); bool IsCallToMonoFinallyMethod(Call call, out IMethod finallyMethod) { finallyMethod = default; if (call == null) return false; if (!(call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis())) return false; if (!call.Method.Name.StartsWith("<>__Finally")) return false; ITypeDefinition declaringTypeDefinition = call.Method.DeclaringTypeDefinition; if (declaringTypeDefinition.MetadataToken != this.enumeratorType) return false; if (declaringTypeDefinition.ParentModule.MetadataFile.Metadata != metadata) return false; finallyMethod = call.Method; 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; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILAmbience.cs ================================================ // Copyright (c) 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.IO; using System.Reflection; using System.Reflection.Metadata; using System.Text; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; #nullable enable namespace ICSharpCode.Decompiler.IL { public class ILAmbience : IAmbience { public ConversionFlags ConversionFlags { get; set; } public string ConvertConstantValue(object constantValue) { throw new NotImplementedException(); } public string ConvertSymbol(ISymbol symbol) { StringWriter sw = new StringWriter(); ConvertSymbol(sw, symbol); return sw.ToString(); } void ConvertSymbol(StringWriter writer, ISymbol symbol) { var metadata = (symbol as IEntity)?.ParentModule!.MetadataFile?.Metadata; var token = (symbol as IEntity)?.MetadataToken ?? default; var output = new PlainTextOutput(writer); switch (symbol) { case IField f: Debug.Assert(metadata != null); if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) writer.Write(".field "); var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)token); if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) ReflectionDisassembler.WriteEnum(fd.Attributes & FieldAttributes.FieldAccessMask, ReflectionDisassembler.fieldVisibility, output); if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) { const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; ReflectionDisassembler.WriteFlags(fd.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), ReflectionDisassembler.fieldAttributes, output); if (!f.IsStatic) { writer.Write("instance "); } } break; case IMethod m: Debug.Assert(metadata != null); if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) writer.Write(".method "); var md = metadata.GetMethodDefinition((MethodDefinitionHandle)token); if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) ReflectionDisassembler.WriteEnum(md.Attributes & MethodAttributes.MemberAccessMask, ReflectionDisassembler.methodVisibility, output); if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) { ReflectionDisassembler.WriteFlags(md.Attributes & ~MethodAttributes.MemberAccessMask, ReflectionDisassembler.methodAttributeFlags, output); if (!m.IsStatic) { writer.Write("instance "); } } break; case IProperty p: Debug.Assert(metadata != null); if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) writer.Write(".property "); var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)token); if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) { ReflectionDisassembler.WriteFlags(pd.Attributes, ReflectionDisassembler.propertyAttributes, output); if (!p.IsStatic) { writer.Write("instance "); } } break; case IEvent e: Debug.Assert(metadata != null); if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) writer.Write(".event "); var ed = metadata.GetEventDefinition((EventDefinitionHandle)token); if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) { ReflectionDisassembler.WriteFlags(ed.Attributes, ReflectionDisassembler.eventAttributes, output); if (!e.IsStatic) { writer.Write("instance "); } } break; case ITypeDefinition: Debug.Assert(metadata != null); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)token); if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) { writer.Write(".class "); if (td.Attributes.HasFlag(TypeAttributes.Interface)) writer.Write("interface "); } if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) ReflectionDisassembler.WriteEnum(td.Attributes & TypeAttributes.VisibilityMask, ReflectionDisassembler.typeVisibility, output); const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) ReflectionDisassembler.WriteFlags(td.Attributes & ~masks, ReflectionDisassembler.typeAttributes, output); break; } bool showReturnTypeBefore = ConversionFlags.HasFlag(ConversionFlags.ShowReturnType) && !ConversionFlags.HasFlag(ConversionFlags.PlaceReturnTypeAfterParameterList); bool showReturnTypeAfter = ConversionFlags.HasFlag(ConversionFlags.ShowReturnType) && ConversionFlags.HasFlag(ConversionFlags.PlaceReturnTypeAfterParameterList); if (showReturnTypeBefore && symbol is IMember { SymbolKind: not SymbolKind.Constructor }) { switch (symbol) { case IField f: writer.Write(ConvertType(f.ReturnType)); break; case IMethod m: writer.Write(ConvertType(m.ReturnType)); break; case IProperty p: writer.Write(ConvertType(p.ReturnType)); break; case IEvent e: writer.Write(ConvertType(e.ReturnType)); break; } writer.Write(' '); } void WriteTypeDefinition(ITypeDefinition typeDef) { if ((ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedEntityNames) || ConversionFlags.HasFlag(ConversionFlags.ShowDeclaringType)) && typeDef.DeclaringTypeDefinition != null) { WriteTypeDefinition(typeDef.DeclaringTypeDefinition); writer.Write('.'); } else if (ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedEntityNames) && !string.IsNullOrEmpty(typeDef.Namespace)) { writer.Write(typeDef.Namespace); writer.Write('.'); } writer.Write(typeDef.Name); WriteTypeParameters(typeDef.TypeParameters, typeDef); } void WriteTypeParameters(IReadOnlyList typeParameters, IEntity owner) { if (typeParameters.Count > 0) { int typeParameterCount = typeParameters.Count - (owner.DeclaringTypeDefinition?.TypeParameterCount ?? 0); if (typeParameterCount > 0) { switch (owner) { case IType: writer.Write("`"); break; case IMethod _: writer.Write("``"); break; } writer.Write(typeParameterCount); } if (ConversionFlags.HasFlag(ConversionFlags.ShowTypeParameterList)) { int i = 0; writer.Write('<'); foreach (var tp in typeParameters) { if (i > 0) writer.Write(","); if (ConversionFlags.HasFlag(ConversionFlags.ShowTypeParameterVarianceModifier)) { switch (tp.Variance) { case VarianceModifier.Covariant: writer.Write('+'); break; case VarianceModifier.Contravariant: writer.Write('-'); break; } } writer.Write(tp.Name); i++; } writer.Write('>'); } } } switch (symbol) { case ITypeDefinition definition: WriteTypeDefinition(definition); break; case IMember member: if ((ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames) || ConversionFlags.HasFlag(ConversionFlags.ShowDeclaringType)) && member.DeclaringTypeDefinition != null) { WriteTypeDefinition(member.DeclaringTypeDefinition); writer.Write("::"); } writer.Write(member.Name); if (member is IMethod method) { WriteTypeParameters(method.TypeParameters, member); } break; } if (ConversionFlags.HasFlag(ConversionFlags.ShowParameterList) && symbol is IParameterizedMember { SymbolKind: not SymbolKind.Property } pm) { writer.Write('('); int i = 0; foreach (var parameter in pm.Parameters) { if (i > 0) writer.Write(", "); writer.Write(ConvertType(parameter.Type)); if (ConversionFlags.HasFlag(ConversionFlags.ShowParameterNames)) writer.Write(" " + parameter.Name); i++; } writer.Write(')'); } if (showReturnTypeAfter && symbol is IMember { SymbolKind: not SymbolKind.Constructor }) { writer.Write(" : "); switch (symbol) { case IField f: writer.Write(ConvertType(f.ReturnType)); break; case IMethod m: writer.Write(ConvertType(m.ReturnType)); break; case IProperty p: writer.Write(ConvertType(p.ReturnType)); break; case IEvent e: writer.Write(ConvertType(e.ReturnType)); break; } } } public string ConvertType(IType type) { var visitor = new TypeToStringVisitor(ConversionFlags); type.AcceptVisitor(visitor); return visitor.ToString(); } class TypeToStringVisitor : TypeVisitor { readonly ConversionFlags flags; readonly StringBuilder builder; public override string ToString() { return builder.ToString(); } public TypeToStringVisitor(ConversionFlags flags) { this.flags = flags; this.builder = new StringBuilder(); } public override IType VisitArrayType(ArrayType type) { base.VisitArrayType(type); builder.Append('['); builder.Append(',', type.Dimensions - 1); builder.Append(']'); return type; } public override IType VisitByReferenceType(ByReferenceType type) { base.VisitByReferenceType(type); builder.Append('&'); return type; } public override IType VisitModOpt(ModifiedType type) { type.ElementType.AcceptVisitor(this); builder.Append(" modopt("); type.Modifier.AcceptVisitor(this); builder.Append(")"); return type; } public override IType VisitModReq(ModifiedType type) { type.ElementType.AcceptVisitor(this); builder.Append(" modreq("); type.Modifier.AcceptVisitor(this); builder.Append(")"); return type; } public override IType VisitPointerType(PointerType type) { base.VisitPointerType(type); builder.Append('*'); return type; } public override IType VisitTypeParameter(ITypeParameter type) { base.VisitTypeParameter(type); EscapeName(builder, type.Name); return type; } public override IType VisitParameterizedType(ParameterizedType type) { type.GenericType.AcceptVisitor(this); builder.Append('<'); for (int i = 0; i < type.TypeArguments.Count; i++) { if (i > 0) builder.Append(','); type.TypeArguments[i].AcceptVisitor(this); } builder.Append('>'); return type; } public override IType VisitTupleType(TupleType type) { type.UnderlyingType.AcceptVisitor(this); return type; } public override IType VisitFunctionPointerType(FunctionPointerType type) { builder.Append("method "); if (type.CallingConvention != SignatureCallingConvention.Default) { builder.Append(type.CallingConvention.ToILSyntax()); builder.Append(' '); } type.ReturnType.AcceptVisitor(this); builder.Append(" *("); bool first = true; foreach (var p in type.ParameterTypes) { if (first) first = false; else builder.Append(", "); p.AcceptVisitor(this); } builder.Append(')'); return type; } public override IType VisitOtherType(IType type) { WriteType(type); return type; } private void WriteType(IType type) { if (flags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames)) EscapeName(builder, type.FullName); else EscapeName(builder, type.Name); if (type.TypeParameterCount > 0) { builder.Append('`'); builder.Append(type.TypeParameterCount); } } public override IType VisitTypeDefinition(ITypeDefinition type) { switch (type.KnownTypeCode) { case KnownTypeCode.Object: builder.Append("object"); break; case KnownTypeCode.Boolean: builder.Append("bool"); break; case KnownTypeCode.Char: builder.Append("char"); break; case KnownTypeCode.SByte: builder.Append("int8"); break; case KnownTypeCode.Byte: builder.Append("uint8"); break; case KnownTypeCode.Int16: builder.Append("int16"); break; case KnownTypeCode.UInt16: builder.Append("uint16"); break; case KnownTypeCode.Int32: builder.Append("int32"); break; case KnownTypeCode.UInt32: builder.Append("uint32"); break; case KnownTypeCode.Int64: builder.Append("int64"); break; case KnownTypeCode.UInt64: builder.Append("uint64"); break; case KnownTypeCode.Single: builder.Append("float32"); break; case KnownTypeCode.Double: builder.Append("float64"); break; case KnownTypeCode.String: builder.Append("string"); break; case KnownTypeCode.Void: builder.Append("void"); break; case KnownTypeCode.IntPtr: builder.Append("native int"); break; case KnownTypeCode.UIntPtr: builder.Append("native uint"); break; case KnownTypeCode.TypedReference: builder.Append("typedref"); break; default: WriteType(type); break; } return type; } } public string WrapComment(string comment) { return "// " + comment; } /// /// Escape characters that cannot be displayed in the UI. /// public static StringBuilder EscapeName(StringBuilder sb, string name) { foreach (char ch in name) { if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch)) sb.AppendFormat("\\u{0:x4}", (int)ch); else sb.Append(ch); } return sb; } /// /// Escape characters that cannot be displayed in the UI. /// public static string EscapeName(string name) { return EscapeName(new StringBuilder(name.Length), name).ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILAstWritingOptions.cs ================================================ #nullable enable // Copyright (c) 2017 Daniel Grunwald // // 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.ComponentModel; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.IL { public class ILAstWritingOptions : INotifyPropertyChanged { private bool useLogicOperationSugar; private bool useFieldSugar; private bool showILRanges; private bool showChildIndexInBlock; /// /// Sugar for logic.not/and/or. /// public bool UseLogicOperationSugar { get { return useLogicOperationSugar; } set { if (useLogicOperationSugar != value) { useLogicOperationSugar = value; OnPropertyChanged(); } } } /// /// Sugar for ldfld/stfld. /// public bool UseFieldSugar { get { return useFieldSugar; } set { if (useFieldSugar != value) { useFieldSugar = value; OnPropertyChanged(); } } } /// /// Show IL ranges in ILAst output. /// public bool ShowILRanges { get { return showILRanges; } set { if (showILRanges != value) { showILRanges = value; OnPropertyChanged(); } } } /// /// Show the child index of the instruction in ILAst output. /// public bool ShowChildIndexInBlock { get { return showChildIndexInBlock; } set { if (showChildIndexInBlock != value) { showChildIndexInBlock = value; OnPropertyChanged(); } } } protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChanged?.Invoke(this, e); } public event PropertyChangedEventHandler? PropertyChanged; } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs ================================================ #nullable enable using System; using System.Collections.Generic; using System.Text; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { internal static class ILInstructionExtensions { public static T WithILRange(this T target, ILInstruction sourceInstruction) where T : ILInstruction { target.AddILRange(sourceInstruction); return target; } public static T WithILRange(this T target, Interval range) where T : ILInstruction { target.AddILRange(range); return target; } public static ILInstruction? GetNextSibling(this ILInstruction? instruction) { if (instruction?.Parent == null) return null; if (instruction.ChildIndex + 1 >= instruction.Parent.Children.Count) return null; return instruction.Parent.Children[instruction.ChildIndex + 1]; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILReader.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ArrayType = ICSharpCode.Decompiler.TypeSystem.ArrayType; using ByReferenceType = ICSharpCode.Decompiler.TypeSystem.ByReferenceType; using PinnedType = ICSharpCode.Decompiler.TypeSystem.Implementation.PinnedType; namespace ICSharpCode.Decompiler.IL { /// /// Reads IL bytecodes and converts them into ILAst instructions. /// /// /// Instances of this class are not thread-safe. Use separate instances to decompile multiple members in parallel. /// public class ILReader { /// /// Represents a block of IL instructions. /// private sealed class ImportedBlock { // These members are immediately available after construction: public readonly Block Block = new Block(BlockKind.ControlFlow); public ImmutableStack InputStack; public int StartILOffset => Block.StartILOffset; /// True if the import is in progress or completed. public bool ImportStarted = false; // When the block is imported, Block.Instructions is filled with the imported instructions // and the following members are initialized: public List<(ImportedBlock, ImmutableStack)> OutgoingEdges = new(); public ImportedBlock(int offset, ImmutableStack inputStack) { this.InputStack = inputStack; this.Block.AddILRange(new Interval(offset, offset)); } /// /// Compares stack types and update InputStack if necessary. /// Returns true if InputStack was updated, making a reimport necessary. /// public bool MergeStackTypes(ImmutableStack newEdge) { var a = this.InputStack; var b = newEdge; bool changed = false; while (!a.IsEmpty && !b.IsEmpty) { if (a.Peek().StackType < b.Peek().StackType) { changed = true; } a = a.Pop(); b = b.Pop(); } if (!changed || !(a.IsEmpty && b.IsEmpty)) return false; a = this.InputStack; b = newEdge; var output = new List(); while (!a.IsEmpty && !b.IsEmpty) { if (a.Peek().StackType < b.Peek().StackType) { output.Add(b.Peek()); } else { output.Add(a.Peek()); } a = a.Pop(); b = b.Pop(); } output.Reverse(); // restore correct stack order this.InputStack = ImmutableStack.CreateRange(output); this.ImportStarted = false; return true; } internal void ResetForReimport() { this.Block.Instructions.Clear(); this.OutgoingEdges.Clear(); } } readonly ICompilation compilation; readonly MetadataModule module; readonly MetadataReader metadata; public bool UseDebugSymbols { get; set; } public bool UseRefLocalsForAccurateOrderOfEvaluation { get; set; } public DebugInfo.IDebugInfoProvider? DebugInfo { get; set; } public List Warnings { get; } = new List(); // List of candidate locations for sequence points. Includes empty il stack locations, any nop instructions, and the instruction following // a call instruction. public List SequencePointCandidates { get; } = new List(); /// /// Creates a new ILReader instance. /// /// /// The module used to resolve metadata tokens in the type system. /// public ILReader(MetadataModule module) { if (module == null) throw new ArgumentNullException(nameof(module)); this.module = module; this.compilation = module.Compilation; this.metadata = module.metadata; } // The members initialized with `null!` are initialized in the Init method. GenericContext genericContext; IMethod method = null!; MethodBodyBlock body = null!; StackType methodReturnStackType; BlobReader reader; ImmutableStack currentStack = ImmutableStack.Empty; ImportedBlock? currentBlock; List expressionStack = new List(); ILVariable[] parameterVariables = null!; ILVariable[] localVariables = null!; BitSet isBranchTarget = null!; BlockContainer mainContainer = null!; int currentInstructionStart; Dictionary blocksByOffset = new Dictionary(); Queue importQueue = new Queue(); Dictionary variableByExceptionHandler = new Dictionary(); IEnumerable? stackVariables; void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext) { if (body == null) throw new ArgumentNullException(nameof(body)); if (methodDefinitionHandle.IsNil) throw new ArgumentException("methodDefinitionHandle.IsNil"); this.method = module.GetDefinition(methodDefinitionHandle); if (genericContext.ClassTypeParameters == null && genericContext.MethodTypeParameters == null) { // no generic context specified: use the method's own type parameters genericContext = new GenericContext(method); } else { // generic context specified, so specialize the method for it: this.method = this.method.Specialize(genericContext.ToSubstitution()); } this.genericContext = genericContext; this.body = body; this.reader = body.GetILReader(); this.currentStack = ImmutableStack.Empty; this.expressionStack.Clear(); this.methodReturnStackType = method.ReturnType.GetStackType(); InitParameterVariables(); localVariables = InitLocalVariables(); foreach (var v in localVariables) { v.InitialValueIsInitialized = body.LocalVariablesInitialized; v.UsesInitialValue = true; } this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); this.blocksByOffset.Clear(); this.importQueue.Clear(); this.isBranchTarget = new BitSet(reader.Length); this.variableByExceptionHandler.Clear(); } EntityHandle ReadAndDecodeMetadataToken() { int token = reader.ReadInt32(); if (token <= 0) { // SRM uses negative tokens as "virtual tokens" and can get confused // if we manually create them. // Row-IDs < 1 are always invalid. throw new BadImageFormatException("Invalid metadata token"); } var handle = MetadataTokens.EntityHandle(token); if (handle.IsNil) { // The runtime will crash with a BadImageFormatException when it encounters a row-ID of 0. // We assume the code following this instruction to be unreachable. throw new BadImageFormatException("Invalid metadata token"); } return handle; } IType ReadAndDecodeTypeReference() { var typeReference = ReadAndDecodeMetadataToken(); return module.ResolveType(typeReference, genericContext); } IMethod ReadAndDecodeMethodReference() { var methodReference = ReadAndDecodeMetadataToken(); return module.ResolveMethod(methodReference, genericContext); } IField ReadAndDecodeFieldReference() { var fieldReference = ReadAndDecodeMetadataToken(); var f = module.ResolveEntity(fieldReference, genericContext) as IField; if (f == null) throw new BadImageFormatException("Invalid field token"); return f; } ILVariable[] InitLocalVariables() { if (body.LocalSignature.IsNil) return Empty.Array; ImmutableArray variableTypes; try { variableTypes = module.DecodeLocalSignature(body.LocalSignature, genericContext); } catch (BadImageFormatException ex) { Warnings.Add("Error decoding local variables: " + ex.Message); variableTypes = ImmutableArray.Empty; } var localVariables = new ILVariable[variableTypes.Length]; foreach (var (index, type) in variableTypes.WithIndex()) { localVariables[index] = CreateILVariable(index, type); } return localVariables; } void InitParameterVariables() { int popCount = method.Parameters.Count; if (!method.IsStatic) popCount++; if (method.Parameters.LastOrDefault()?.Type == SpecialType.ArgList) popCount--; parameterVariables = new ILVariable[popCount]; int paramIndex = 0; int offset = 0; if (!method.IsStatic) { offset = 1; IType declaringType = method.DeclaringType; if (declaringType.IsUnbound()) { // If method is a definition (and not specialized), the declaring type is also just a definition, // and needs to be converted into a normally usable type. declaringType = new ParameterizedType(declaringType, declaringType.TypeParameters); } ILVariable ilVar = CreateILVariable(-1, declaringType, "this"); ilVar.IsRefReadOnly = method.ThisIsRefReadOnly; parameterVariables[paramIndex++] = ilVar; } while (paramIndex < parameterVariables.Length) { IParameter parameter = method.Parameters[paramIndex - offset]; ILVariable ilVar = CreateILVariable(paramIndex - offset, parameter.Type, parameter.Name); ilVar.IsRefReadOnly = parameter.ReferenceKind is ReferenceKind.In or ReferenceKind.RefReadOnly; parameterVariables[paramIndex] = ilVar; paramIndex++; } Debug.Assert(paramIndex == parameterVariables.Length); } ILVariable CreateILVariable(int index, IType type) { VariableKind kind; if (type.SkipModifiers() is PinnedType pinned) { kind = VariableKind.PinnedLocal; type = pinned.ElementType; } else { kind = VariableKind.Local; } if (UseDebugSymbols && DebugInfo is not null && DebugInfo.TryGetExtraTypeInfo((MethodDefinitionHandle)method.MetadataToken, index, out var pdbExtraTypeInfo)) { type = ApplyAttributeTypeVisitor.ApplyAttributesToType(type, compilation, module.TypeSystemOptions, pdbExtraTypeInfo); } ILVariable ilVar = new ILVariable(kind, type, index); if (!UseDebugSymbols || DebugInfo == null || !DebugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name)) { ilVar.Name = "V_" + index; ilVar.HasGeneratedName = true; } else if (string.IsNullOrWhiteSpace(name)) { ilVar.Name = "V_" + index; ilVar.HasGeneratedName = true; } else { ilVar.Name = name; } return ilVar; } ILVariable CreateILVariable(int index, IType parameterType, string name) { Debug.Assert(!parameterType.IsUnbound()); ITypeDefinition? def = parameterType.GetDefinition(); if (def != null && index < 0 && def.IsReferenceType == false) { parameterType = new ByReferenceType(parameterType); } var ilVar = new ILVariable(VariableKind.Parameter, parameterType, index); Debug.Assert(ilVar.StoreCount == 1); // count the initial store when the method is called with an argument if (index < 0) ilVar.Name = "this"; else if (string.IsNullOrWhiteSpace(name)) { ilVar.Name = "P_" + index; ilVar.HasGeneratedName = true; } else ilVar.Name = name; return ilVar; } /// /// Warn when invalid IL is detected. /// ILSpy should be able to handle invalid IL; but this method can be helpful for debugging the ILReader, /// as this method should not get called when processing valid IL. /// void Warn(string message) { Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstructionStart, message)); } /// /// Check control flow edges for compatible stacks. /// Returns union find data structure for unifying the different variables for the same stack slot. /// Also inserts stack adjustments where necessary. /// UnionFind CheckOutgoingEdges() { var unionFind = new UnionFind(); foreach (var block in blocksByOffset.Values) { foreach (var (outgoing, stack) in block.OutgoingEdges) { var a = stack; var b = outgoing.InputStack; if (a.Count() != b.Count()) { // Let's not try to merge mismatched stacks. Warnings.Add($"IL_{block.Block.EndILOffset:x4}->IL{outgoing.StartILOffset:x4}: Incompatible stack heights: {a.Count()} vs {b.Count()}"); continue; } while (!a.IsEmpty && !b.IsEmpty) { var varA = a.Peek(); var varB = b.Peek(); if (varA.StackType == varB.StackType) { // The stack types match, so we can merge the variables. unionFind.Merge(varA, varB); } else { Debug.Assert(varA.StackType < varB.StackType); if (!IsValidTypeStackTypeMerge(varA.StackType, varB.StackType)) { Warnings.Add($"IL_{block.Block.EndILOffset:x4}->IL{outgoing.StartILOffset:x4}: Incompatible stack types: {varA.StackType} vs {varB.StackType}"); } InsertStackAdjustment(block.Block, varA, varB); } a = a.Pop(); b = b.Pop(); } } } return unionFind; } /// /// Inserts a copy from varA to varB (with conversion) at the end of . /// If the block ends with a branching instruction, the copy is inserted before the branching instruction. /// private void InsertStackAdjustment(Block block, ILVariable varA, ILVariable varB) { int insertionPosition = block.Instructions.Count; while (insertionPosition > 0 && block.Instructions[insertionPosition - 1].HasFlag(InstructionFlags.MayBranch)) { // Branch instruction mustn't be initializing varA. Debug.Assert(!block.Instructions[insertionPosition - 1].HasFlag(InstructionFlags.MayWriteLocals)); insertionPosition--; } ILInstruction value = new LdLoc(varA); value = new Conv(value, varB.StackType.ToPrimitiveType(), false, Sign.Signed); block.Instructions.Insert(insertionPosition, new StLoc(varB, value) { IsStackAdjustment = true }); } private static bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType2) { if (stackType1 == StackType.I && stackType2 == StackType.I4) return true; if (stackType1 == StackType.I4 && stackType2 == StackType.I) return true; if (stackType1 == StackType.F4 && stackType2 == StackType.F8) return true; if (stackType1 == StackType.F8 && stackType2 == StackType.F4) return true; // allow merging unknown type with any other type return stackType1 == StackType.Unknown || stackType2 == StackType.Unknown; } /// /// Stores the given stack for a branch to `offset`. /// ImportedBlock StoreStackForOffset(int offset, ImmutableStack stack) { if (blocksByOffset.TryGetValue(offset, out var existing)) { bool wasImported = existing.ImportStarted; if (existing.MergeStackTypes(stack) && wasImported) { // If the stack changed, we need to re-import the block. importQueue.Enqueue(existing); } return existing; } else { ImportedBlock newBlock = new ImportedBlock(offset, stack); blocksByOffset.Add(offset, newBlock); importQueue.Enqueue(newBlock); return newBlock; } } void ReadInstructions(CancellationToken cancellationToken) { reader.Reset(); StoreStackForOffset(0, ImmutableStack.Empty); if (reader.Length == 0) { blocksByOffset[0].Block.Instructions.Add( new InvalidBranch("Empty body found. Decompiled assembly might be a reference assembly.") ); stackVariables = Enumerable.Empty(); return; } ILParser.SetBranchTargets(ref reader, isBranchTarget); PrepareBranchTargetsAndStacksForExceptionHandlers(); // Import of IL byte codes: while (importQueue.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); ImportedBlock block = importQueue.Dequeue(); ReadBlock(block, cancellationToken); } EnsureExceptionHandlersHaveBlocks(); // Merge different variables for same stack slot: var unionFind = CheckOutgoingEdges(); var visitor = new CollectStackVariablesVisitor(unionFind); foreach (var block in blocksByOffset.Values) { block.Block.AcceptVisitor(visitor); } stackVariables = visitor.variables; } void ReadBlock(ImportedBlock block, CancellationToken cancellationToken) { Debug.Assert(!block.ImportStarted); block.ResetForReimport(); block.ImportStarted = true; reader.Offset = block.StartILOffset; //Debug.WriteLine($"Import block at IL_{block.StartILOffset:x4} with inputs {string.Join(", ", block.InputStack.Select(v => v.StackType.ToString()))}"); currentBlock = block; currentStack = block.InputStack; // Read instructions until we reach the end of the block. while (reader.RemainingBytes > 0) { cancellationToken.ThrowIfCancellationRequested(); int start = reader.Offset; currentInstructionStart = start; bool startedWithEmptyStack = CurrentStackIsEmpty(); DecodedInstruction decodedInstruction; try { decodedInstruction = DecodeInstruction(); } catch (BadImageFormatException ex) { decodedInstruction = new InvalidBranch(ex.Message); } var inst = decodedInstruction.Instruction; if (inst.ResultType == StackType.Unknown && inst.OpCode != OpCode.InvalidBranch && inst.OpCode != OpCode.InvalidExpression) Warn("Unknown result type (might be due to invalid IL or missing references)"); inst.CheckInvariant(ILPhase.InILReader); int end = reader.Offset; inst.AddILRange(new Interval(start, end)); if (!decodedInstruction.PushedOnExpressionStack) { // Flush to avoid re-ordering of side-effects FlushExpressionStack(); block.Block.Instructions.Add(inst); } if ((!decodedInstruction.PushedOnExpressionStack && IsSequencePointInstruction(inst)) || startedWithEmptyStack) { this.SequencePointCandidates.Add(inst.StartILOffset); } if (inst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) { break; // end of block, don't parse following instructions if they are unreachable } else if (isBranchTarget[end] || inst.HasFlag(InstructionFlags.MayBranch)) { break; // end of block (we'll create fall through) } } // Finalize block FlushExpressionStack(); block.Block.AddILRange(new Interval(block.StartILOffset, reader.Offset)); if (!block.Block.HasFlag(InstructionFlags.EndPointUnreachable)) { // create fall through branch ILInstruction branch; if (reader.RemainingBytes > 0) { MarkBranchTarget(reader.Offset, isFallThrough: true); branch = new Branch(reader.Offset); } else { branch = new InvalidBranch("End of method reached without returning."); } if (block.Block.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) { // Instead of putting the default branch after the switch instruction switchInst.Sections.Last().Body = branch; Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable)); } else { block.Block.Instructions.Add(branch); } } } private bool CurrentStackIsEmpty() { return currentStack.IsEmpty && expressionStack.Count == 0; } private void PrepareBranchTargetsAndStacksForExceptionHandlers() { // Fill isBranchTarget and branchStackDict based on exception handlers foreach (var eh in body.ExceptionRegions) { // Always mark the start of the try block as a "branch target". // We need to ensure that we put a block boundary there. isBranchTarget[eh.TryOffset] = true; ImmutableStack ehStack; if (eh.Kind == ExceptionRegionKind.Catch) { var catchType = module.ResolveType(eh.CatchType, genericContext); var v = new ILVariable(VariableKind.ExceptionStackSlot, catchType, eh.HandlerOffset) { Name = "E_" + eh.HandlerOffset, HasGeneratedName = true }; variableByExceptionHandler.Add(eh, v); ehStack = ImmutableStack.Create(v); } else if (eh.Kind == ExceptionRegionKind.Filter) { var v = new ILVariable(VariableKind.ExceptionStackSlot, compilation.FindType(KnownTypeCode.Object), eh.HandlerOffset) { Name = "E_" + eh.HandlerOffset, HasGeneratedName = true }; variableByExceptionHandler.Add(eh, v); ehStack = ImmutableStack.Create(v); } else { ehStack = ImmutableStack.Empty; } if (eh.FilterOffset != -1) { isBranchTarget[eh.FilterOffset] = true; StoreStackForOffset(eh.FilterOffset, ehStack); } if (eh.HandlerOffset != -1) { isBranchTarget[eh.HandlerOffset] = true; StoreStackForOffset(eh.HandlerOffset, ehStack); } } } private void EnsureExceptionHandlersHaveBlocks() { // PrepareBranchTargetsAndStacksForExceptionHandlers enqueued filter/handler offsets // so we have blocks for those; but it's possible that the TryOffset was never enqueued // because it is unreachable. // We need to ensure that we have blocks for all exception handler offsets, // as otherwise the BlockBuilder will fail. foreach (var eh in body.ExceptionRegions) { if (blocksByOffset.ContainsKey(eh.TryOffset)) continue; // Create a dummy block for the try offset var block = new ImportedBlock(eh.TryOffset, ImmutableStack.Empty); block.Block.Instructions.Add(new InvalidBranch("Unreachable try block")); blocksByOffset.Add(eh.TryOffset, block); } // Note that after the BlockBuilder is done, it may delete the whole block containing // the unreachable try-except construct, if it is completely unreachable. } private static bool IsSequencePointInstruction(ILInstruction instruction) { if (instruction.OpCode is OpCode.Nop or OpCode.Call or OpCode.CallIndirect or OpCode.CallVirt) { return true; } else { return false; } } /// /// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout. /// public void WriteTypedIL(MethodDefinitionHandle method, MethodBodyBlock body, ITextOutput output, GenericContext genericContext = default, CancellationToken cancellationToken = default) { Init(method, body, genericContext); ReadInstructions(cancellationToken); foreach (var importBlock in blocksByOffset.Values.OrderBy(b => b.StartILOffset)) { output.Write(" ["); bool isFirstElement = true; foreach (var element in importBlock.InputStack) { if (isFirstElement) isFirstElement = false; else output.Write(", "); output.WriteLocalReference(element.Name, element); output.Write(":"); output.Write(element.StackType); } output.Write(']'); output.WriteLine(); importBlock.Block.WriteTo(output, new ILAstWritingOptions()); output.WriteLine(); } new Disassembler.MethodBodyDisassembler(output, cancellationToken) { DetectControlStructure = false } .WriteExceptionHandlers(module.MetadataFile, method, body); } /// /// Decodes the specified method body and returns an ILFunction. /// public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); Init(method, body, genericContext); ReadInstructions(cancellationToken); var blockBuilder = new BlockBuilder(body, variableByExceptionHandler, compilation); blockBuilder.CreateBlocks(mainContainer, blocksByOffset.Values.Select(ib => ib.Block), cancellationToken); var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); function.Variables.AddRange(parameterVariables); function.Variables.AddRange(localVariables); function.LocalVariableSignatureLength = localVariables.Length; Debug.Assert(stackVariables != null); function.Variables.AddRange(stackVariables); function.Variables.AddRange(variableByExceptionHandler.Values); function.Variables.AddRange(blockBuilder.OnErrorDispatcherVariables); function.AddRef(); // mark the root node var removedBlocks = new List(); foreach (var c in function.Descendants.OfType()) { var newOrder = c.TopologicalSort(deleteUnreachableBlocks: true); if (newOrder.Count < c.Blocks.Count) { removedBlocks.AddRange(c.Blocks.Except(newOrder)); } c.Blocks.ReplaceList(newOrder); } if (removedBlocks.Count > 0) { removedBlocks.SortBy(b => b.StartILOffset); function.Warnings.Add("Discarded unreachable code: " + string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}"))); } this.SequencePointCandidates.Sort(); function.SequencePointCandidates = this.SequencePointCandidates; function.Warnings.AddRange(Warnings); return function; } DecodedInstruction Neg() { switch (PeekStackType()) { case StackType.I4: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None)); case StackType.I: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None), Pop(), checkForOverflow: false, sign: Sign.None)); case StackType.I8: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None)); case StackType.F4: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF4(0), Pop(), checkForOverflow: false, sign: Sign.None)); case StackType.F8: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF8(0), Pop(), checkForOverflow: false, sign: Sign.None)); default: Warn("Unsupported input type for neg."); goto case StackType.I4; } } struct DecodedInstruction { public ILInstruction Instruction; public bool PushedOnExpressionStack; public static implicit operator DecodedInstruction(ILInstruction instruction) { return new DecodedInstruction { Instruction = instruction }; } } DecodedInstruction DecodeInstruction() { if (reader.RemainingBytes == 0) return new InvalidBranch("Unexpected end of body"); var opCode = ILParser.DecodeOpCode(ref reader); switch (opCode) { case ILOpCode.Constrained: return DecodeConstrainedCall(); case ILOpCode.Readonly: return DecodeReadonly(); case ILOpCode.Tail: return DecodeTailCall(); case ILOpCode.Unaligned: return DecodeUnaligned(); case ILOpCode.Volatile: return DecodeVolatile(); case ILOpCode.Add: return BinaryNumeric(BinaryNumericOperator.Add); case ILOpCode.Add_ovf: return BinaryNumeric(BinaryNumericOperator.Add, true, Sign.Signed); case ILOpCode.Add_ovf_un: return BinaryNumeric(BinaryNumericOperator.Add, true, Sign.Unsigned); case ILOpCode.And: return BinaryNumeric(BinaryNumericOperator.BitAnd); case ILOpCode.Arglist: return Push(new Arglist()); case ILOpCode.Beq: return DecodeComparisonBranch(opCode, ComparisonKind.Equality); case ILOpCode.Beq_s: return DecodeComparisonBranch(opCode, ComparisonKind.Equality); case ILOpCode.Bge: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual); case ILOpCode.Bge_s: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual); case ILOpCode.Bge_un: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual, un: true); case ILOpCode.Bge_un_s: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual, un: true); case ILOpCode.Bgt: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan); case ILOpCode.Bgt_s: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan); case ILOpCode.Bgt_un: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan, un: true); case ILOpCode.Bgt_un_s: return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan, un: true); case ILOpCode.Ble: return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual); case ILOpCode.Ble_s: return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual); case ILOpCode.Ble_un: return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual, un: true); case ILOpCode.Ble_un_s: return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual, un: true); case ILOpCode.Blt: return DecodeComparisonBranch(opCode, ComparisonKind.LessThan); case ILOpCode.Blt_s: return DecodeComparisonBranch(opCode, ComparisonKind.LessThan); case ILOpCode.Blt_un: return DecodeComparisonBranch(opCode, ComparisonKind.LessThan, un: true); case ILOpCode.Blt_un_s: return DecodeComparisonBranch(opCode, ComparisonKind.LessThan, un: true); case ILOpCode.Bne_un: return DecodeComparisonBranch(opCode, ComparisonKind.Inequality, un: true); case ILOpCode.Bne_un_s: return DecodeComparisonBranch(opCode, ComparisonKind.Inequality, un: true); case ILOpCode.Br: return DecodeUnconditionalBranch(opCode); case ILOpCode.Br_s: return DecodeUnconditionalBranch(opCode); case ILOpCode.Break: return new DebugBreak(); case ILOpCode.Brfalse: return DecodeConditionalBranch(opCode, true); case ILOpCode.Brfalse_s: return DecodeConditionalBranch(opCode, true); case ILOpCode.Brtrue: return DecodeConditionalBranch(opCode, false); case ILOpCode.Brtrue_s: return DecodeConditionalBranch(opCode, false); case ILOpCode.Call: return DecodeCall(OpCode.Call); case ILOpCode.Callvirt: return DecodeCall(OpCode.CallVirt); case ILOpCode.Calli: return DecodeCallIndirect(); case ILOpCode.Ceq: return Push(Comparison(ComparisonKind.Equality)); case ILOpCode.Cgt: return Push(Comparison(ComparisonKind.GreaterThan)); case ILOpCode.Cgt_un: return Push(Comparison(ComparisonKind.GreaterThan, un: true)); case ILOpCode.Clt: return Push(Comparison(ComparisonKind.LessThan)); case ILOpCode.Clt_un: return Push(Comparison(ComparisonKind.LessThan, un: true)); case ILOpCode.Ckfinite: return new Ckfinite(Peek()); case ILOpCode.Conv_i1: return Push(new Conv(Pop(), PrimitiveType.I1, false, Sign.None)); case ILOpCode.Conv_i2: return Push(new Conv(Pop(), PrimitiveType.I2, false, Sign.None)); case ILOpCode.Conv_i4: return Push(new Conv(Pop(), PrimitiveType.I4, false, Sign.None)); case ILOpCode.Conv_i8: return Push(new Conv(Pop(), PrimitiveType.I8, false, Sign.None)); case ILOpCode.Conv_r4: return Push(new Conv(Pop(), PrimitiveType.R4, false, Sign.Signed)); case ILOpCode.Conv_r8: return Push(new Conv(Pop(), PrimitiveType.R8, false, Sign.Signed)); case ILOpCode.Conv_u1: return Push(new Conv(Pop(), PrimitiveType.U1, false, Sign.None)); case ILOpCode.Conv_u2: return Push(new Conv(Pop(), PrimitiveType.U2, false, Sign.None)); case ILOpCode.Conv_u4: return Push(new Conv(Pop(), PrimitiveType.U4, false, Sign.None)); case ILOpCode.Conv_u8: return Push(new Conv(Pop(), PrimitiveType.U8, false, Sign.None)); case ILOpCode.Conv_i: return Push(new Conv(Pop(), PrimitiveType.I, false, Sign.None)); case ILOpCode.Conv_u: return Push(new Conv(Pop(), PrimitiveType.U, false, Sign.None)); case ILOpCode.Conv_r_un: return Push(new Conv(Pop(), PrimitiveType.R, false, Sign.Unsigned)); case ILOpCode.Conv_ovf_i1: return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Signed)); case ILOpCode.Conv_ovf_i2: return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Signed)); case ILOpCode.Conv_ovf_i4: return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Signed)); case ILOpCode.Conv_ovf_i8: return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Signed)); case ILOpCode.Conv_ovf_u1: return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Signed)); case ILOpCode.Conv_ovf_u2: return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Signed)); case ILOpCode.Conv_ovf_u4: return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Signed)); case ILOpCode.Conv_ovf_u8: return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Signed)); case ILOpCode.Conv_ovf_i: return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Signed)); case ILOpCode.Conv_ovf_u: return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Signed)); case ILOpCode.Conv_ovf_i1_un: return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_i2_un: return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_i4_un: return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_i8_un: return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_u1_un: return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_u2_un: return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_u4_un: return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_u8_un: return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_i_un: return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Unsigned)); case ILOpCode.Conv_ovf_u_un: return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned)); case ILOpCode.Cpblk: // This preserves the evaluation order because the ILAst will run // destAddress; sourceAddress; size. return new Cpblk(size: Pop(StackType.I4), sourceAddress: PopPointer(), destAddress: PopPointer()); case ILOpCode.Div: return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Signed); case ILOpCode.Div_un: return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Unsigned); case ILOpCode.Dup: return Push(Peek()); case ILOpCode.Endfilter: return new Leave(null, Pop()); case ILOpCode.Endfinally: return new Leave(null); case ILOpCode.Initblk: // This preserves the evaluation order because the ILAst will run // address; value; size. return new Initblk(size: Pop(StackType.I4), value: Pop(StackType.I4), address: PopPointer()); case ILOpCode.Jmp: return DecodeJmp(); case ILOpCode.Ldarg: case ILOpCode.Ldarg_s: return Push(Ldarg(ILParser.DecodeIndex(ref reader, opCode))); case ILOpCode.Ldarg_0: return Push(Ldarg(0)); case ILOpCode.Ldarg_1: return Push(Ldarg(1)); case ILOpCode.Ldarg_2: return Push(Ldarg(2)); case ILOpCode.Ldarg_3: return Push(Ldarg(3)); case ILOpCode.Ldarga: case ILOpCode.Ldarga_s: return Push(Ldarga(ILParser.DecodeIndex(ref reader, opCode))); case ILOpCode.Ldc_i4: return Push(new LdcI4(reader.ReadInt32())); case ILOpCode.Ldc_i8: return Push(new LdcI8(reader.ReadInt64())); case ILOpCode.Ldc_r4: return Push(new LdcF4(reader.ReadSingle())); case ILOpCode.Ldc_r8: return Push(new LdcF8(reader.ReadDouble())); case ILOpCode.Ldc_i4_m1: return Push(new LdcI4(-1)); case ILOpCode.Ldc_i4_0: return Push(new LdcI4(0)); case ILOpCode.Ldc_i4_1: return Push(new LdcI4(1)); case ILOpCode.Ldc_i4_2: return Push(new LdcI4(2)); case ILOpCode.Ldc_i4_3: return Push(new LdcI4(3)); case ILOpCode.Ldc_i4_4: return Push(new LdcI4(4)); case ILOpCode.Ldc_i4_5: return Push(new LdcI4(5)); case ILOpCode.Ldc_i4_6: return Push(new LdcI4(6)); case ILOpCode.Ldc_i4_7: return Push(new LdcI4(7)); case ILOpCode.Ldc_i4_8: return Push(new LdcI4(8)); case ILOpCode.Ldc_i4_s: return Push(new LdcI4(reader.ReadSByte())); case ILOpCode.Ldnull: return Push(new LdNull()); case ILOpCode.Ldstr: return Push(DecodeLdstr()); case ILOpCode.Ldftn: return Push(new LdFtn(ReadAndDecodeMethodReference())); case ILOpCode.Ldind_i1: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.SByte))); case ILOpCode.Ldind_i2: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int16))); case ILOpCode.Ldind_i4: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int32))); case ILOpCode.Ldind_i8: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int64))); case ILOpCode.Ldind_u1: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Byte))); case ILOpCode.Ldind_u2: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt16))); case ILOpCode.Ldind_u4: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt32))); case ILOpCode.Ldind_r4: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Single))); case ILOpCode.Ldind_r8: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Double))); case ILOpCode.Ldind_i: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.IntPtr))); case ILOpCode.Ldind_ref: return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Object))); case ILOpCode.Ldloc: case ILOpCode.Ldloc_s: return Push(Ldloc(ILParser.DecodeIndex(ref reader, opCode))); case ILOpCode.Ldloc_0: return Push(Ldloc(0)); case ILOpCode.Ldloc_1: return Push(Ldloc(1)); case ILOpCode.Ldloc_2: return Push(Ldloc(2)); case ILOpCode.Ldloc_3: return Push(Ldloc(3)); case ILOpCode.Ldloca: case ILOpCode.Ldloca_s: return Push(Ldloca(ILParser.DecodeIndex(ref reader, opCode))); case ILOpCode.Leave: return DecodeUnconditionalBranch(opCode, isLeave: true); case ILOpCode.Leave_s: return DecodeUnconditionalBranch(opCode, isLeave: true); case ILOpCode.Localloc: return Push(new LocAlloc(Pop())); case ILOpCode.Mul: return BinaryNumeric(BinaryNumericOperator.Mul, false, Sign.None); case ILOpCode.Mul_ovf: return BinaryNumeric(BinaryNumericOperator.Mul, true, Sign.Signed); case ILOpCode.Mul_ovf_un: return BinaryNumeric(BinaryNumericOperator.Mul, true, Sign.Unsigned); case ILOpCode.Neg: return Neg(); case ILOpCode.Newobj: return DecodeCall(OpCode.NewObj); case ILOpCode.Nop: return new Nop(); case ILOpCode.Not: return Push(new BitNot(Pop())); case ILOpCode.Or: return BinaryNumeric(BinaryNumericOperator.BitOr); case ILOpCode.Pop: FlushExpressionStack(); // discard only the value, not the side-effects Pop(); return new Nop() { Kind = NopKind.Pop }; case ILOpCode.Rem: return BinaryNumeric(BinaryNumericOperator.Rem, false, Sign.Signed); case ILOpCode.Rem_un: return BinaryNumeric(BinaryNumericOperator.Rem, false, Sign.Unsigned); case ILOpCode.Ret: return Return(); case ILOpCode.Shl: return BinaryNumeric(BinaryNumericOperator.ShiftLeft, false, Sign.None); case ILOpCode.Shr: return BinaryNumeric(BinaryNumericOperator.ShiftRight, false, Sign.Signed); case ILOpCode.Shr_un: return BinaryNumeric(BinaryNumericOperator.ShiftRight, false, Sign.Unsigned); case ILOpCode.Starg: case ILOpCode.Starg_s: return Starg(ILParser.DecodeIndex(ref reader, opCode)); case ILOpCode.Stind_i1: // target will run before value, thus preserving the evaluation order return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.SByte)); case ILOpCode.Stind_i2: return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int16)); case ILOpCode.Stind_i4: return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int32)); case ILOpCode.Stind_i8: return new StObj(value: Pop(StackType.I8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int64)); case ILOpCode.Stind_r4: return new StObj(value: Pop(StackType.F4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Single)); case ILOpCode.Stind_r8: return new StObj(value: Pop(StackType.F8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Double)); case ILOpCode.Stind_i: return new StObj(value: Pop(StackType.I), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.IntPtr)); case ILOpCode.Stind_ref: return new StObj(value: Pop(StackType.O), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Object)); case ILOpCode.Stloc: case ILOpCode.Stloc_s: return Stloc(ILParser.DecodeIndex(ref reader, opCode)); case ILOpCode.Stloc_0: return Stloc(0); case ILOpCode.Stloc_1: return Stloc(1); case ILOpCode.Stloc_2: return Stloc(2); case ILOpCode.Stloc_3: return Stloc(3); case ILOpCode.Sub: return BinaryNumeric(BinaryNumericOperator.Sub, false, Sign.None); case ILOpCode.Sub_ovf: return BinaryNumeric(BinaryNumericOperator.Sub, true, Sign.Signed); case ILOpCode.Sub_ovf_un: return BinaryNumeric(BinaryNumericOperator.Sub, true, Sign.Unsigned); case ILOpCode.Switch: return DecodeSwitch(); case ILOpCode.Xor: return BinaryNumeric(BinaryNumericOperator.BitXor); case ILOpCode.Box: { var type = ReadAndDecodeTypeReference(); return Push(new Box(Pop(type.GetStackType()), type)); } case ILOpCode.Castclass: return Push(new CastClass(Pop(StackType.O), ReadAndDecodeTypeReference())); case ILOpCode.Cpobj: { var type = ReadAndDecodeTypeReference(); // OK, 'target' runs before 'value: ld' var ld = new LdObj(PopPointer(), type); return new StObj(PopStObjTarget(), ld, type); } case ILOpCode.Initobj: return InitObj(PopStObjTarget(), ReadAndDecodeTypeReference()); case ILOpCode.Isinst: { var type = ReadAndDecodeTypeReference(); if (type.IsReferenceType != true) { FlushExpressionStack(); // value-type isinst has inlining restrictions } return Push(new IsInst(Pop(StackType.O), type)); } case ILOpCode.Ldelem: return LdElem(ReadAndDecodeTypeReference()); case ILOpCode.Ldelem_i1: return LdElem(compilation.FindType(KnownTypeCode.SByte)); case ILOpCode.Ldelem_i2: return LdElem(compilation.FindType(KnownTypeCode.Int16)); case ILOpCode.Ldelem_i4: return LdElem(compilation.FindType(KnownTypeCode.Int32)); case ILOpCode.Ldelem_i8: return LdElem(compilation.FindType(KnownTypeCode.Int64)); case ILOpCode.Ldelem_u1: return LdElem(compilation.FindType(KnownTypeCode.Byte)); case ILOpCode.Ldelem_u2: return LdElem(compilation.FindType(KnownTypeCode.UInt16)); case ILOpCode.Ldelem_u4: return LdElem(compilation.FindType(KnownTypeCode.UInt32)); case ILOpCode.Ldelem_r4: return LdElem(compilation.FindType(KnownTypeCode.Single)); case ILOpCode.Ldelem_r8: return LdElem(compilation.FindType(KnownTypeCode.Double)); case ILOpCode.Ldelem_i: return LdElem(compilation.FindType(KnownTypeCode.IntPtr)); case ILOpCode.Ldelem_ref: return LdElem(compilation.FindType(KnownTypeCode.Object)); case ILOpCode.Ldelema: // LdElema will evalute the array before the indices, so we're preserving the evaluation order return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference())); case ILOpCode.Ldfld: { var field = ReadAndDecodeFieldReference(); return Push(new LdObj(new LdFlda(PopLdFldTarget(field), field) { DelayExceptions = true }, field.Type)); } case ILOpCode.Ldflda: { var field = ReadAndDecodeFieldReference(); return Push(new LdFlda(PopFieldTarget(field), field)); } case ILOpCode.Stfld: { var field = ReadAndDecodeFieldReference(); return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(PopFieldTarget(field), field) { DelayExceptions = true }, type: field.Type); } case ILOpCode.Ldlen: return Push(new LdLen(StackType.I, Pop(StackType.O))); case ILOpCode.Ldobj: return Push(new LdObj(PopPointer(), ReadAndDecodeTypeReference())); case ILOpCode.Ldsfld: { var field = ReadAndDecodeFieldReference(); return Push(new LdObj(new LdsFlda(field), field.Type)); } case ILOpCode.Ldsflda: return Push(new LdsFlda(ReadAndDecodeFieldReference())); case ILOpCode.Stsfld: { var field = ReadAndDecodeFieldReference(); return new StObj(value: Pop(field.Type.GetStackType()), target: new LdsFlda(field), type: field.Type); } case ILOpCode.Ldtoken: return Push(LdToken(ReadAndDecodeMetadataToken())); case ILOpCode.Ldvirtftn: return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference())); case ILOpCode.Mkrefany: return Push(new MakeRefAny(PopPointer(), ReadAndDecodeTypeReference())); case ILOpCode.Newarr: return Push(new NewArr(ReadAndDecodeTypeReference(), Pop())); case ILOpCode.Refanytype: return Push(new RefAnyType(Pop())); case ILOpCode.Refanyval: return Push(new RefAnyValue(Pop(), ReadAndDecodeTypeReference())); case ILOpCode.Rethrow: return new Rethrow(); case ILOpCode.Sizeof: return Push(new SizeOf(ReadAndDecodeTypeReference())); case ILOpCode.Stelem: return StElem(ReadAndDecodeTypeReference()); case ILOpCode.Stelem_i1: return StElem(compilation.FindType(KnownTypeCode.SByte)); case ILOpCode.Stelem_i2: return StElem(compilation.FindType(KnownTypeCode.Int16)); case ILOpCode.Stelem_i4: return StElem(compilation.FindType(KnownTypeCode.Int32)); case ILOpCode.Stelem_i8: return StElem(compilation.FindType(KnownTypeCode.Int64)); case ILOpCode.Stelem_r4: return StElem(compilation.FindType(KnownTypeCode.Single)); case ILOpCode.Stelem_r8: return StElem(compilation.FindType(KnownTypeCode.Double)); case ILOpCode.Stelem_i: return StElem(compilation.FindType(KnownTypeCode.IntPtr)); case ILOpCode.Stelem_ref: return StElem(compilation.FindType(KnownTypeCode.Object)); case ILOpCode.Stobj: { var type = ReadAndDecodeTypeReference(); // OK, target runs before value return new StObj(value: Pop(type.GetStackType()), target: PopStObjTarget(), type: type); } case ILOpCode.Throw: return new Throw(Pop()); case ILOpCode.Unbox: return Push(new Unbox(Pop(), ReadAndDecodeTypeReference())); case ILOpCode.Unbox_any: return Push(new UnboxAny(Pop(), ReadAndDecodeTypeReference())); default: return new InvalidBranch($"Unknown opcode: 0x{(int)opCode:X2}"); } } StackType PeekStackType() { if (expressionStack.Count > 0) return expressionStack.Last().ResultType; if (currentStack.IsEmpty) return StackType.Unknown; else return currentStack.Peek().StackType; } sealed class CollectStackVariablesVisitor : ILVisitor { readonly UnionFind unionFind; internal readonly HashSet variables = new HashSet(); public CollectStackVariablesVisitor(UnionFind unionFind) { Debug.Assert(unionFind != null); this.unionFind = unionFind; } protected override ILInstruction Default(ILInstruction inst) { foreach (var child in inst.Children) { var newChild = child.AcceptVisitor(this); if (newChild != child) child.ReplaceWith(newChild); } return inst; } protected internal override ILInstruction VisitLdLoc(LdLoc inst) { base.VisitLdLoc(inst); if (inst.Variable.Kind == VariableKind.StackSlot) { var variable = unionFind.Find(inst.Variable); if (variables.Add(variable)) variable.Name = $"S_{variables.Count - 1}"; return new LdLoc(variable).WithILRange(inst); } return inst; } protected internal override ILInstruction VisitStLoc(StLoc inst) { base.VisitStLoc(inst); if (inst.Variable.Kind == VariableKind.StackSlot) { var variable = unionFind.Find(inst.Variable); if (variables.Add(variable)) variable.Name = $"S_{variables.Count - 1}"; return new StLoc(variable, inst.Value).WithILRange(inst); } return inst; } } DecodedInstruction Push(ILInstruction inst) { expressionStack.Add(inst); return new DecodedInstruction { Instruction = inst, PushedOnExpressionStack = true }; } ILInstruction Peek() { FlushExpressionStack(); if (currentStack.IsEmpty) { return new InvalidExpression("Stack underflow").WithILRange(new Interval(reader.Offset, reader.Offset)); } return new LdLoc(currentStack.Peek()); } /// /// Pops a value/instruction from the evaluation stack. /// Note that instructions popped from the stack must be evaluated in the order they /// were pushed (so in reverse order of the pop calls!). /// /// For instructions like 'conv' that pop a single element and then push their result, /// it's fine to pop just one element as the instruction itself will end up on the stack, /// thus maintaining the evaluation order. /// For instructions like 'call' that pop multiple arguments, it's critical that /// the evaluation order of the resulting ILAst will be reverse from the order of the push /// calls. /// For instructions like 'brtrue', it's fine to pop only a part of the stack because /// ReadInstructions() will flush the evaluation stack before outputting the brtrue instruction. /// /// Use FlushExpressionStack() to ensure that following Pop() calls do not return /// instructions that involve side-effects. This way evaluation order is preserved /// no matter which order the ILAst will execute the popped instructions in. /// ILInstruction Pop() { if (expressionStack.Count > 0) { var inst = expressionStack.Last(); expressionStack.RemoveAt(expressionStack.Count - 1); return inst; } if (currentStack.IsEmpty) { return new InvalidExpression("Stack underflow").WithILRange(new Interval(reader.Offset, reader.Offset)); } ILVariable v; currentStack = currentStack.Pop(out v); return new LdLoc(v); } ILInstruction Pop(StackType expectedType) { ILInstruction inst = Pop(); return Cast(inst, expectedType, Warnings, reader.Offset); } internal static ILInstruction Cast(ILInstruction inst, StackType expectedType, List? warnings, int ilOffset) { if (expectedType != inst.ResultType) { if (inst is InvalidExpression) { ((InvalidExpression)inst).ExpectedResultType = expectedType; } else if (expectedType == StackType.I && inst.ResultType == StackType.I4) { // IL allows implicit I4->I conversions inst = new Conv(inst, PrimitiveType.I, false, Sign.None); } else if (expectedType == StackType.I4 && inst.ResultType == StackType.I) { // C++/CLI also sometimes implicitly converts in the other direction: inst = new Conv(inst, PrimitiveType.I4, false, Sign.None); } else if (expectedType == StackType.Unknown) { inst = new Conv(inst, PrimitiveType.Unknown, false, Sign.None); } else if (inst.ResultType == StackType.Ref) { // Implicitly stop GC tracking; this occurs when passing the result of 'ldloca' or 'ldsflda' // to a method expecting a native pointer. inst = new Conv(inst, PrimitiveType.I, false, Sign.None); switch (expectedType) { case StackType.I4: inst = new Conv(inst, PrimitiveType.I4, false, Sign.None); break; case StackType.I: break; case StackType.I8: inst = new Conv(inst, PrimitiveType.I8, false, Sign.None); break; default: Warn($"Expected {expectedType}, but got {StackType.Ref}"); inst = new Conv(inst, expectedType.ToPrimitiveType(), false, Sign.None); break; } } else if (expectedType == StackType.Ref) { // implicitly start GC tracking / object to interior if (!inst.ResultType.IsIntegerType() && inst.ResultType != StackType.O) { // We also handle the invalid to-ref cases here because the else case // below uses expectedType.ToKnownTypeCode(), which doesn't work for Ref. Warn($"Expected {expectedType}, but got {inst.ResultType}"); } inst = new Conv(inst, PrimitiveType.Ref, false, Sign.None); } else if (expectedType == StackType.F8 && inst.ResultType == StackType.F4) { // IL allows implicit F4->F8 conversions, because in IL F4 and F8 are the same. inst = new Conv(inst, PrimitiveType.R8, false, Sign.Signed); } else if (expectedType == StackType.F4 && inst.ResultType == StackType.F8) { // IL allows implicit F8->F4 conversions, because in IL F4 and F8 are the same. inst = new Conv(inst, PrimitiveType.R4, false, Sign.Signed); } else { Warn($"Expected {expectedType}, but got {inst.ResultType}"); inst = new Conv(inst, expectedType.ToPrimitiveType(), false, Sign.Signed); } } return inst; void Warn(string message) { if (warnings != null) { warnings.Add(string.Format("IL_{0:x4}: {1}", ilOffset, message)); } } } ILInstruction PopPointer() { ILInstruction inst = Pop(); switch (inst.ResultType) { case StackType.I4: case StackType.I8: case StackType.Unknown: return new Conv(inst, PrimitiveType.I, false, Sign.None); case StackType.I: case StackType.Ref: return inst; default: Warn("Expected native int or pointer, but got " + inst.ResultType); return new Conv(inst, PrimitiveType.I, false, Sign.None); } } ILInstruction PopStObjTarget() { // stobj has a special invariant (StObj.CheckTargetSlot) // that prohibits inlining LdElema/LdFlda. if (expressionStack.LastOrDefault() is LdElema or LdFlda) { FlushExpressionStack(); } return PopPointer(); } ILInstruction PopFieldTarget(IField field) { switch (field.DeclaringType.IsReferenceType) { case true: return Pop(StackType.O); case false: return PopPointer(); default: // field in unresolved type var stackType = PeekStackType(); if (stackType == StackType.O || stackType == StackType.Unknown) return Pop(); else return PopPointer(); } } /// /// Like PopFieldTarget, but supports ldfld's special behavior for fields of temporary value types. /// ILInstruction PopLdFldTarget(IField field) { switch (field.DeclaringType.IsReferenceType) { case true: return Pop(StackType.O); case false: // field of value type: ldfld can handle temporaries if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) return new AddressOf(Pop(), field.DeclaringType); else return PopPointer(); default: // field in unresolved type if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) return Pop(); else return PopPointer(); } } private ILInstruction Return() { if (methodReturnStackType == StackType.Void) { return new IL.Leave(mainContainer); } else if (currentInstructionStart == 0) { Debug.Assert(expressionStack.Count == 0 && currentStack.IsEmpty); return new InvalidBranch("Method body consists only of 'ret', but nothing is being returned. Decompiled assembly might be a reference assembly."); } else { return new IL.Leave(mainContainer, Pop(methodReturnStackType)); } } private ILInstruction DecodeLdstr() { return new LdStr(ILParser.DecodeUserString(ref reader, metadata)); } private ILInstruction Ldarg(int v) { if (v >= 0 && v < parameterVariables.Length) { return new LdLoc(parameterVariables[v]); } else { return new InvalidExpression($"ldarg {v} (out-of-bounds)"); } } private ILInstruction Ldarga(int v) { if (v >= 0 && v < parameterVariables.Length) { return new LdLoca(parameterVariables[v]); } else { return new InvalidExpression($"ldarga {v} (out-of-bounds)"); } } private ILInstruction Starg(int v) { if (v >= 0 && v < parameterVariables.Length) { return new StLoc(parameterVariables[v], Pop(parameterVariables[v].StackType)); } else { FlushExpressionStack(); Pop(); return new InvalidExpression($"starg {v} (out-of-bounds)"); } } private ILInstruction Ldloc(int v) { if (v >= 0 && v < localVariables.Length) { return new LdLoc(localVariables[v]); } else { return new InvalidExpression($"ldloc {v} (out-of-bounds)"); } } private ILInstruction Ldloca(int v) { if (v >= 0 && v < localVariables.Length) { return new LdLoca(localVariables[v]); } else { return new InvalidExpression($"ldloca {v} (out-of-bounds)"); } } private ILInstruction Stloc(int v) { if (v >= 0 && v < localVariables.Length) { return new StLoc(localVariables[v], Pop(localVariables[v].StackType)) { ILStackWasEmpty = CurrentStackIsEmpty() }; } else { FlushExpressionStack(); Pop(); return new InvalidExpression($"stloc {v} (out-of-bounds)"); } } private DecodedInstruction LdElem(IType type) { return Push(new LdObj(new LdElema(indices: Pop(), array: Pop(), type: type) { DelayExceptions = true }, type)); } private ILInstruction StElem(IType type) { var value = Pop(type.GetStackType()); var index = Pop(); var array = Pop(); // OK, evaluation order is array, index, value return new StObj(new LdElema(type, array, index) { DelayExceptions = true }, value, type); } ILInstruction InitObj(ILInstruction target, IType type) { var value = new DefaultValue(type); value.ILStackWasEmpty = CurrentStackIsEmpty(); return new StObj(target, value, type); } IType? constrainedPrefix; private DecodedInstruction DecodeConstrainedCall() { constrainedPrefix = ReadAndDecodeTypeReference(); var inst = DecodeInstruction(); var call = inst.Instruction as CallInstruction; if (call != null) Debug.Assert(call.ConstrainedTo == constrainedPrefix); else Warn("Ignored invalid 'constrained' prefix"); constrainedPrefix = null; return inst; } private DecodedInstruction DecodeTailCall() { var inst = DecodeInstruction(); var call = inst.Instruction as CallInstruction; if (call != null) call.IsTail = true; else Warn("Ignored invalid 'tail' prefix"); return inst; } private DecodedInstruction DecodeUnaligned() { byte alignment = reader.ReadByte(); var inst = DecodeInstruction(); var sup = inst.Instruction as ISupportsUnalignedPrefix; if (sup != null) sup.UnalignedPrefix = alignment; else Warn("Ignored invalid 'unaligned' prefix"); return inst; } private DecodedInstruction DecodeVolatile() { var inst = DecodeInstruction(); var svp = inst.Instruction as ISupportsVolatilePrefix; if (svp != null) svp.IsVolatile = true; else Warn("Ignored invalid 'volatile' prefix"); return inst; } private DecodedInstruction DecodeReadonly() { var inst = DecodeInstruction(); var ldelema = inst.Instruction as LdElema; if (ldelema != null) ldelema.IsReadOnly = true; else Warn("Ignored invalid 'readonly' prefix"); return inst; } DecodedInstruction DecodeCall(OpCode opCode) { var method = ReadAndDecodeMethodReference(); ILInstruction[] arguments; switch (method.DeclaringType.Kind) { case TypeKind.Array: { arguments = PrepareArguments(firstArgumentIsStObjTarget: false); var elementType = ((ArrayType)method.DeclaringType).ElementType; if (opCode == OpCode.NewObj) return Push(new NewArr(elementType, arguments)); if (method.Name == "Set") { var target = arguments[0]; var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray(); var value = arguments.Last(); // preserves evaluation order target,indices,value return new StObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, value, elementType); } if (method.Name == "Get") { var target = arguments[0]; var indices = arguments.Skip(1).ToArray(); // preserves evaluation order target,indices return Push(new LdObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, elementType)); } if (method.Name == "Address") { var target = arguments[0]; var indices = arguments.Skip(1).ToArray(); // preserves evaluation order target,indices return Push(new LdElema(elementType, target, indices)); } Warn("Unknown method called on array type: " + method.Name); goto default; } case TypeKind.Struct when method.IsConstructor && !method.IsStatic && opCode == OpCode.Call && method.ReturnType.Kind == TypeKind.Void: { // "call Struct.ctor(target, ...)" doesn't exist in C#, // the next best equivalent is an assignment `*target = new Struct(...);`. // So we represent this call as "stobj Struct(target, newobj Struct.ctor(...))". // This needs to happen early (not as a transform) because the StObj.TargetSlot has // restricted inlining (doesn't accept ldflda when exceptions aren't delayed). arguments = PrepareArguments(firstArgumentIsStObjTarget: true); var newobj = new NewObj(method); newobj.ILStackWasEmpty = CurrentStackIsEmpty(); newobj.ConstrainedTo = constrainedPrefix; newobj.Arguments.AddRange(arguments.Skip(1)); return new StObj(arguments[0], newobj, method.DeclaringType); } default: arguments = PrepareArguments(firstArgumentIsStObjTarget: false); var call = CallInstruction.Create(opCode, method); call.ILStackWasEmpty = CurrentStackIsEmpty(); call.ConstrainedTo = constrainedPrefix; call.Arguments.AddRange(arguments); if (call.ResultType != StackType.Void) return Push(call); return call; } ILInstruction[] PrepareArguments(bool firstArgumentIsStObjTarget) { int firstArgument = (opCode != OpCode.NewObj && !method.IsStatic) ? 1 : 0; var arguments = new ILInstruction[firstArgument + method.Parameters.Count]; IType typeOfThis = constrainedPrefix ?? method.DeclaringType; StackType expectedStackType = CallInstruction.ExpectedTypeForThisPointer(method.DeclaringType, constrainedPrefix); bool requiresLdObjIfRef = firstArgument == 1 && !firstArgumentIsStObjTarget && UseRefLocalsForAccurateOrderOfEvaluation && expectedStackType == StackType.Ref && typeOfThis.IsReferenceType != false; for (int i = method.Parameters.Count - 1; i >= 0; i--) { if (requiresLdObjIfRef) { FlushExpressionStack(); } arguments[firstArgument + i] = Pop(method.Parameters[i].Type.GetStackType()); } if (firstArgument == 1) { ILInstruction firstArgumentInstruction; if (firstArgumentIsStObjTarget) { firstArgumentInstruction = PopStObjTarget(); } else { firstArgumentInstruction = Pop(expectedStackType); if (requiresLdObjIfRef) { firstArgumentInstruction = new LdObjIfRef(firstArgumentInstruction, typeOfThis); } } arguments[0] = firstArgumentInstruction; } // arguments is in reverse order of the Pop calls, thus // arguments is now in the correct evaluation order. return arguments; } } DecodedInstruction DecodeCallIndirect() { StandaloneSignatureHandle signatureHandle; try { signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); } catch (InvalidCastException ex) { throw new BadImageFormatException("Invalid calli metadata token", ex); } var (header, fpt) = module.DecodeMethodSignature(signatureHandle, genericContext); var functionPointer = Pop(StackType.I); int firstArgument = header.IsInstance ? 1 : 0; var arguments = new ILInstruction[firstArgument + fpt.ParameterTypes.Length]; for (int i = fpt.ParameterTypes.Length - 1; i >= 0; i--) { arguments[firstArgument + i] = Pop(fpt.ParameterTypes[i].GetStackType()); } if (firstArgument == 1) { arguments[0] = Pop(); } // arguments is in reverse order of the Pop calls, thus // arguments is now in the correct evaluation order. var call = new CallIndirect( header.IsInstance, header.HasExplicitThis, fpt, functionPointer, arguments ); if (call.ResultType != StackType.Void) return Push(call); else return call; } ILInstruction Comparison(ComparisonKind kind, bool un = false) { if (!kind.IsEqualityOrInequality() && PeekStackType() == StackType.O) { FlushExpressionStack(); } var right = Pop(); var left = Pop(); // left will run before right, thus preserving the evaluation order if ((left.ResultType == StackType.O || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType()) { // C++/CLI sometimes compares object references with integers. // Also happens with Ref==I in Unsafe.IsNullRef(). if (right.ResultType == StackType.I4) { // ensure we compare at least native integer size right = new Conv(right, PrimitiveType.I, false, Sign.None); } left = new Conv(left, right.ResultType.ToPrimitiveType(), false, Sign.None); } else if ((right.ResultType == StackType.O || right.ResultType == StackType.Ref) && left.ResultType.IsIntegerType()) { if (left.ResultType == StackType.I4) { left = new Conv(left, PrimitiveType.I, false, Sign.None); } right = new Conv(right, left.ResultType.ToPrimitiveType(), false, Sign.None); } // make implicit integer conversions explicit: MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I, conversionType: PrimitiveType.I); MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I8, conversionType: PrimitiveType.I8); MakeExplicitConversion(sourceType: StackType.I, targetType: StackType.I8, conversionType: PrimitiveType.I8); // Based on Table 4: Binary Comparison or Branch Operation if (left.ResultType.IsFloatType() && right.ResultType.IsFloatType()) { if (left.ResultType != right.ResultType) { // make the implicit F4->F8 conversion explicit: MakeExplicitConversion(StackType.F4, StackType.F8, PrimitiveType.R8); } if (un) { // for floats, 'un' means 'unordered' return Comp.LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); } else { return new Comp(kind, Sign.None, left, right); } } else if (left.ResultType.IsIntegerType() && right.ResultType.IsIntegerType() && !kind.IsEqualityOrInequality()) { // integer comparison where the sign matters Debug.Assert(right.ResultType.IsIntegerType()); return new Comp(kind, un ? Sign.Unsigned : Sign.Signed, left, right); } else if (left.ResultType == right.ResultType) { // integer equality, object reference or managed reference comparison return new Comp(kind, Sign.None, left, right); } else { Warn($"Invalid comparison between {left.ResultType} and {right.ResultType}"); if (left.ResultType < right.ResultType) { left = new Conv(left, right.ResultType.ToPrimitiveType(), false, Sign.Signed); } else { right = new Conv(right, left.ResultType.ToPrimitiveType(), false, Sign.Signed); } return new Comp(kind, Sign.None, left, right); } void MakeExplicitConversion(StackType sourceType, StackType targetType, PrimitiveType conversionType) { if (left.ResultType == sourceType && right.ResultType == targetType) { left = new Conv(left, conversionType, false, Sign.None); } else if (left.ResultType == targetType && right.ResultType == sourceType) { right = new Conv(right, conversionType, false, Sign.None); } } } bool IsInvalidBranch(int target) => target < 0 || target >= reader.Length; ILInstruction DecodeComparisonBranch(ILOpCode opCode, ComparisonKind kind, bool un = false) { int start = reader.Offset - 1; // opCode is always one byte in this case int target = ILParser.DecodeBranchTarget(ref reader, opCode); var condition = Comparison(kind, un); condition.AddILRange(new Interval(start, reader.Offset)); if (!IsInvalidBranch(target)) { MarkBranchTarget(target); return new IfInstruction(condition, new Branch(target)); } else { return new IfInstruction(condition, new InvalidBranch("Invalid branch target")); } } ILInstruction DecodeConditionalBranch(ILOpCode opCode, bool negate) { int target = ILParser.DecodeBranchTarget(ref reader, opCode); ILInstruction condition = Pop(); switch (condition.ResultType) { case StackType.O: // introduce explicit comparison with null condition = new Comp( negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdNull()); break; case StackType.I: // introduce explicit comparison with 0 condition = new Comp( negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); break; case StackType.I8: // introduce explicit comparison with 0 condition = new Comp( negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdcI8(0)); break; case StackType.Ref: // introduce explicit comparison with null ref condition = new Comp( negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, new Conv(condition, PrimitiveType.I, false, Sign.None), new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); break; case StackType.I4: if (negate) { condition = Comp.LogicNot(condition); } break; default: condition = new Conv(condition, PrimitiveType.I4, false, Sign.None); if (negate) { condition = Comp.LogicNot(condition); } break; } if (!IsInvalidBranch(target)) { MarkBranchTarget(target); return new IfInstruction(condition, new Branch(target)); } else { return new IfInstruction(condition, new InvalidBranch("Invalid branch target")); } } ILInstruction DecodeUnconditionalBranch(ILOpCode opCode, bool isLeave = false) { int target = ILParser.DecodeBranchTarget(ref reader, opCode); if (isLeave) { FlushExpressionStack(); currentStack = currentStack.Clear(); } if (!IsInvalidBranch(target)) { MarkBranchTarget(target); return new Branch(target); } else { return new InvalidBranch("Invalid branch target"); } } void MarkBranchTarget(int targetILOffset, bool isFallThrough = false) { FlushExpressionStack(); Debug.Assert(isFallThrough || isBranchTarget[targetILOffset]); var targetBlock = StoreStackForOffset(targetILOffset, currentStack); Debug.Assert(currentBlock != null); currentBlock.OutgoingEdges.Add((targetBlock, currentStack)); } /// /// The expression stack holds ILInstructions that might have side-effects /// that should have already happened (in the order of the pushes). /// This method forces these instructions to be added to the instructionBuilder. /// This is used e.g. to avoid moving side-effects past branches. /// private void FlushExpressionStack() { Debug.Assert(currentBlock != null); foreach (var inst in expressionStack) { Debug.Assert(inst.ResultType != StackType.Void); IType type = compilation.FindType(inst.ResultType); var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType); v.HasGeneratedName = true; currentStack = currentStack.Push(v); currentBlock.Block.Instructions.Add(new StLoc(v, inst).WithILRange(inst)); } expressionStack.Clear(); } ILInstruction DecodeSwitch() { var targets = ILParser.DecodeSwitchTargets(ref reader); var instr = new SwitchInstruction(Pop(StackType.I4)); for (int i = 0; i < targets.Length; i++) { var section = new SwitchSection(); section.Labels = new LongSet(i); int target = targets[i]; if (!IsInvalidBranch(target)) { MarkBranchTarget(target); section.Body = new Branch(target); } else { section.Body = new InvalidBranch("Invalid branch target"); } instr.Sections.Add(section); } var defaultSection = new SwitchSection(); defaultSection.Labels = new LongSet(new LongInterval(0, targets.Length)).Invert(); defaultSection.Body = new Nop(); instr.Sections.Add(defaultSection); return instr; } DecodedInstruction BinaryNumeric(BinaryNumericOperator @operator, bool checkForOverflow = false, Sign sign = Sign.None) { var right = Pop(); var left = Pop(); // left will run before right, thus preserving the evaluation order if (@operator != BinaryNumericOperator.Add && @operator != BinaryNumericOperator.Sub) { // we are treating all Refs as I, make the conversion explicit if (left.ResultType == StackType.Ref) { left = new Conv(left, PrimitiveType.I, false, Sign.None); } if (right.ResultType == StackType.Ref) { right = new Conv(right, PrimitiveType.I, false, Sign.None); } } if (@operator != BinaryNumericOperator.ShiftLeft && @operator != BinaryNumericOperator.ShiftRight) { // make the implicit I4->I conversion explicit: MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I, conversionType: PrimitiveType.I); // I4->I8 conversion: MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I8, conversionType: PrimitiveType.I8); // I->I8 conversion: MakeExplicitConversion(sourceType: StackType.I, targetType: StackType.I8, conversionType: PrimitiveType.I8); // F4->F8 conversion: MakeExplicitConversion(sourceType: StackType.F4, targetType: StackType.F8, conversionType: PrimitiveType.R8); } return Push(new BinaryNumericInstruction(@operator, left, right, checkForOverflow, sign)); void MakeExplicitConversion(StackType sourceType, StackType targetType, PrimitiveType conversionType) { if (left.ResultType == sourceType && right.ResultType == targetType) { left = new Conv(left, conversionType, false, Sign.None); } else if (left.ResultType == targetType && right.ResultType == sourceType) { right = new Conv(right, conversionType, false, Sign.None); } } } ILInstruction DecodeJmp() { IMethod method = ReadAndDecodeMethodReference(); // Translate jmp into tail call: Call call = new Call(method); call.IsTail = true; call.ILStackWasEmpty = true; if (!method.IsStatic) { call.Arguments.Add(Ldarg(0)); } foreach (var p in method.Parameters) { call.Arguments.Add(Ldarg(call.Arguments.Count)); } return new Leave(mainContainer, call); } ILInstruction LdToken(EntityHandle token) { if (token.Kind.IsTypeKind()) return new LdTypeToken(module.ResolveType(token, genericContext)); if (token.Kind.IsMemberKind()) { var entity = module.ResolveEntity(token, genericContext); if (entity is IMember member) return new LdMemberToken(member); } throw new BadImageFormatException("Invalid metadata token for ldtoken instruction."); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILTypeExtensions.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { static class ILTypeExtensions { public static StackType GetStackType(this PrimitiveType primitiveType) { switch (primitiveType) { case PrimitiveType.I1: case PrimitiveType.U1: case PrimitiveType.I2: case PrimitiveType.U2: case PrimitiveType.I4: case PrimitiveType.U4: return StackType.I4; case PrimitiveType.I8: case PrimitiveType.U8: return StackType.I8; case PrimitiveType.I: case PrimitiveType.U: return StackType.I; case PrimitiveType.R4: return StackType.F4; case PrimitiveType.R8: case PrimitiveType.R: return StackType.F8; case PrimitiveType.Ref: // ByRef return StackType.Ref; case PrimitiveType.Unknown: return StackType.Unknown; default: return StackType.O; } } public static Sign GetSign(this PrimitiveType primitiveType) { switch (primitiveType) { case PrimitiveType.I1: case PrimitiveType.I2: case PrimitiveType.I4: case PrimitiveType.I8: case PrimitiveType.R4: case PrimitiveType.R8: case PrimitiveType.R: case PrimitiveType.I: return Sign.Signed; case PrimitiveType.U1: case PrimitiveType.U2: case PrimitiveType.U4: case PrimitiveType.U8: case PrimitiveType.U: return Sign.Unsigned; default: return Sign.None; } } public static bool HasOppositeSign(this PrimitiveType primitiveType) { switch (primitiveType) { case PrimitiveType.I1: case PrimitiveType.I2: case PrimitiveType.I4: case PrimitiveType.I8: case PrimitiveType.U1: case PrimitiveType.U2: case PrimitiveType.U4: case PrimitiveType.U8: case PrimitiveType.I: case PrimitiveType.U: return true; default: return false; } } /// /// Gets the size in bytes of the primitive type. /// /// Returns 0 for non-primitive types. /// Returns NativeIntSize for native int/references. /// public static int GetSize(this PrimitiveType type) { switch (type) { case PrimitiveType.I1: case PrimitiveType.U1: return 1; case PrimitiveType.I2: case PrimitiveType.U2: return 2; case PrimitiveType.I4: case PrimitiveType.U4: case PrimitiveType.R4: return 4; case PrimitiveType.I8: case PrimitiveType.R8: case PrimitiveType.U8: case PrimitiveType.R: return 8; case PrimitiveType.I: case PrimitiveType.U: case PrimitiveType.Ref: return TypeUtils.NativeIntSize; default: return 0; } } /// /// Gets whether the type is a small integer type. /// Small integer types are: /// * bool, sbyte, byte, char, short, ushort /// * any enums that have a small integer type as underlying type /// public static bool IsSmallIntegerType(this PrimitiveType type) { return GetSize(type) < 4; } public static bool IsIntegerType(this PrimitiveType primitiveType) { return primitiveType.GetStackType().IsIntegerType(); } public static bool IsFloatType(this PrimitiveType type) { switch (type) { case PrimitiveType.R4: case PrimitiveType.R8: case PrimitiveType.R: return true; default: return false; } } /// /// Infers the C# type for an IL instruction. /// /// Returns SpecialType.UnknownType for unsupported instructions. /// public static IType InferType(this ILInstruction inst, ICompilation? compilation) { switch (inst) { case NewObj newObj: return newObj.Method.DeclaringType ?? SpecialType.UnknownType; case NewArr newArr: if (compilation != null) return new ArrayType(compilation, newArr.Type, newArr.Indices.Count); else return SpecialType.UnknownType; case Call call: return call.Method.ReturnType; case CallVirt callVirt: return callVirt.Method.ReturnType; case CallIndirect calli: return calli.FunctionPointerType.ReturnType; case UserDefinedLogicOperator logicOp: return logicOp.Method.ReturnType; case LdObj ldobj: return ldobj.Type; case StObj stobj: return stobj.Type; case LdLoc ldloc: return ldloc.Variable.Type; case StLoc stloc: return stloc.Variable.Type; case LdLoca ldloca: return new ByReferenceType(ldloca.Variable.Type); case LdFlda ldflda: return new ByReferenceType(ldflda.Field.Type); case LdsFlda ldsflda: return new ByReferenceType(ldsflda.Field.Type); case LdElema ldelema: if (ldelema.Array.InferType(compilation) is ArrayType arrayType) { if (TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, ldelema.Type)) { return new ByReferenceType(arrayType.ElementType); } } return new ByReferenceType(ldelema.Type); case Comp comp: if (compilation == null) return SpecialType.UnknownType; switch (comp.LiftingKind) { case ComparisonLiftingKind.None: case ComparisonLiftingKind.CSharp: return compilation.FindType(KnownTypeCode.Boolean); case ComparisonLiftingKind.ThreeValuedLogic: return NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); default: return SpecialType.UnknownType; } case BinaryNumericInstruction bni: if (bni.IsLifted) return SpecialType.UnknownType; switch (bni.Operator) { case BinaryNumericOperator.BitAnd: case BinaryNumericOperator.BitOr: case BinaryNumericOperator.BitXor: var left = bni.Left.InferType(compilation); var right = bni.Right.InferType(compilation); if (left.Equals(right) && (left.IsCSharpPrimitiveIntegerType() || left.IsCSharpNativeIntegerType() || left.IsKnownType(KnownTypeCode.Boolean))) return left; else return SpecialType.UnknownType; default: return SpecialType.UnknownType; } case DefaultValue defaultValue: return defaultValue.Type; case ILFunction func when func.DelegateType != null: return func.DelegateType; default: return SpecialType.UnknownType; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/ILVariable.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public enum VariableKind { /// /// A local variable. /// Local, /// /// A pinned local variable (not associated with a pinned region) /// PinnedLocal, /// /// A pinned local variable (associated with a pinned region) /// PinnedRegionLocal, /// /// A local variable used as using-resource variable. /// UsingLocal, /// /// A local variable used as foreach variable. /// ForeachLocal, /// /// A local variable used inside an array, collection or /// object initializer block to denote the object being initialized. /// InitializerTarget, /// /// A parameter. /// Parameter, /// /// Variable created for exception handler. /// ExceptionStackSlot, /// /// Local variable used in a catch block. /// ExceptionLocal, /// /// Variable created from stack slot. /// StackSlot, /// /// Variable in BlockKind.CallWithNamedArgs /// NamedArgument, /// /// Local variable that holds the display class used for lambdas within this function. /// DisplayClassLocal, /// /// Local variable declared within a pattern match. /// PatternLocal, /// /// Temporary variable declared in a deconstruction init section. /// DeconstructionInitTemporary, } static class VariableKindExtensions { public static bool IsThis(this ILVariable v) { return v.Kind == VariableKind.Parameter && v.Index < 0; } public static bool IsLocal(this VariableKind kind) { switch (kind) { case VariableKind.Local: case VariableKind.ExceptionLocal: case VariableKind.ForeachLocal: case VariableKind.UsingLocal: case VariableKind.PatternLocal: case VariableKind.PinnedLocal: case VariableKind.PinnedRegionLocal: case VariableKind.DisplayClassLocal: return true; default: return false; } } } [DebuggerDisplay("{Name} : {Type}")] public class ILVariable { VariableKind kind; public VariableKind Kind { get { return kind; } internal set { if (kind == VariableKind.Parameter) throw new InvalidOperationException("Kind=Parameter cannot be changed!"); if (Index != null && value.IsLocal() && !kind.IsLocal()) { // For variables, Index has different meaning than for stack slots, // so we need to reset it to null. // StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray) Index = null; } kind = value; } } public readonly StackType StackType; IType type; public IType Type { get { return type; } internal set { if (value.GetStackType() != StackType) throw new ArgumentException($"Expected stack-type: {StackType} may not be changed. Found: {value.GetStackType()}"); type = value; } } /// /// This variable is either a C# 7 'in' parameter or must be declared as 'ref readonly'. /// public bool IsRefReadOnly { get; internal set; } /// /// The index of the local variable or parameter (depending on Kind) /// /// For VariableKinds with "Local" in the name: /// * if non-null, the Index refers to the LocalVariableSignature. /// * index may be null for variables that used to be fields (captured by lambda/async) /// For Parameters, the Index refers to the method's list of parameters. /// The special "this" parameter has index -1. /// For ExceptionStackSlot, the index is the IL offset of the exception handler. /// For other kinds, the index has no meaning, and is usually null. /// public int? Index { get; private set; } [Conditional("DEBUG")] internal void CheckInvariant() { switch (kind) { case VariableKind.Local: case VariableKind.ForeachLocal: case VariableKind.PatternLocal: case VariableKind.PinnedLocal: case VariableKind.PinnedRegionLocal: case VariableKind.UsingLocal: case VariableKind.ExceptionLocal: case VariableKind.DisplayClassLocal: // in range of LocalVariableSignature Debug.Assert(Index == null || Index >= 0); break; case VariableKind.Parameter: // -1 for the "this" parameter Debug.Assert(Index >= -1); Debug.Assert(Function == null || Index < Function.Parameters.Count); break; case VariableKind.ExceptionStackSlot: Debug.Assert(Index >= 0); break; } } public string? Name { get; set; } public bool HasGeneratedName { get; set; } /// /// Gets the function in which this variable is declared. /// /// /// This property is set automatically when the variable is added to the ILFunction.Variables collection. /// public ILFunction? Function { get; internal set; } /// /// Gets the block container in which this variable is captured. /// For captured variables declared inside the loop, the capture scope is the BlockContainer of the loop. /// For captured variables declared outside of the loop, the capture scope is the BlockContainer of the parent function. /// /// /// This property returns null for variables that are not captured. /// public BlockContainer? CaptureScope { get; internal set; } /// /// Gets the index of this variable within the Function.Variables collection. /// /// /// This property is set automatically when the variable is added to the VariableScope.Variables collection. /// It may change if an item with a lower index is removed from the collection. /// public int IndexInFunction { get; internal set; } /// /// Number of ldloc instructions referencing this variable. /// /// /// This variable is automatically updated when adding/removing ldloc instructions from the ILAst. /// public int LoadCount => LoadInstructions.Count; readonly List loadInstructions = new List(); /// /// List of ldloc instructions referencing this variable. /// /// /// This list is automatically updated when adding/removing ldloc instructions from the ILAst. /// public IReadOnlyList LoadInstructions => loadInstructions; /// /// Number of store instructions referencing this variable, /// plus 1 if HasInitialValue. /// /// Stores are: /// /// stloc /// TryCatchHandler (assigning the exception variable) /// PinnedRegion (assigning the pointer variable) /// initial values () /// /// /// /// This variable is automatically updated when adding/removing stores instructions from the ILAst. /// public int StoreCount => (usesInitialValue ? 1 : 0) + StoreInstructions.Count; readonly List storeInstructions = new List(); /// /// List of store instructions referencing this variable. /// /// Stores are: /// /// stloc /// TryCatchHandler (assigning the exception variable) /// PinnedRegion (assigning the pointer variable) /// initial values () -- however, there is no instruction for /// the initial value, so it is not contained in the store list. /// /// /// /// This list is automatically updated when adding/removing stores instructions from the ILAst. /// public IReadOnlyList StoreInstructions => storeInstructions; /// /// Number of ldloca instructions referencing this variable. /// /// /// This variable is automatically updated when adding/removing ldloca instructions from the ILAst. /// public int AddressCount => AddressInstructions.Count; readonly List addressInstructions = new List(); /// /// List of ldloca instructions referencing this variable. /// /// /// This list is automatically updated when adding/removing ldloca instructions from the ILAst. /// public IReadOnlyList AddressInstructions => addressInstructions; internal void AddLoadInstruction(LdLoc inst) => inst.IndexInLoadInstructionList = AddInstruction(loadInstructions, inst); internal void AddStoreInstruction(IStoreInstruction inst) => inst.IndexInStoreInstructionList = AddInstruction(storeInstructions, inst); internal void AddAddressInstruction(LdLoca inst) => inst.IndexInAddressInstructionList = AddInstruction(addressInstructions, inst); internal void RemoveLoadInstruction(LdLoc inst) => RemoveInstruction(loadInstructions, inst.IndexInLoadInstructionList, inst); internal void RemoveStoreInstruction(IStoreInstruction inst) => RemoveInstruction(storeInstructions, inst.IndexInStoreInstructionList, inst); internal void RemoveAddressInstruction(LdLoca inst) => RemoveInstruction(addressInstructions, inst.IndexInAddressInstructionList, inst); int AddInstruction(List list, T inst) where T : class, IInstructionWithVariableOperand { list.Add(inst); return list.Count - 1; } void RemoveInstruction(List list, int index, T? inst) where T : class, IInstructionWithVariableOperand { Debug.Assert(list[index] == inst); int indexToMove = list.Count - 1; list[index] = list[indexToMove]; list[index].IndexInVariableInstructionMapping = index; list.RemoveAt(indexToMove); } bool initialValueIsInitialized; /// /// Gets/Sets whether the variable's initial value is initialized. /// This is always true for parameters (incl. this). /// /// Normal variables have an initial value if the function uses ".locals init". /// public bool InitialValueIsInitialized { get { return initialValueIsInitialized; } set { if (Kind == VariableKind.Parameter && !value) throw new InvalidOperationException("Cannot remove InitialValueIsInitialized from parameters"); initialValueIsInitialized = value; } } bool usesInitialValue; /// /// Gets/Sets whether the initial value of the variable is used. /// This is always true for parameters (incl. this). /// /// Normal variables use the initial value, if no explicit initialization is done. /// /// /// The following table shows the relationship between /// and . /// /// /// /// /// Meaning /// /// /// /// /// This variable's initial value is zero-initialized (.locals init) and the initial value is used. /// From C#'s point of view a the value default(T) is assigned at the site of declaration. /// /// /// /// /// This variable's initial value is zero-initialized (.locals init) and the initial value is not used. /// From C#'s point of view no implicit initialization occurs, because the code assigns a value /// explicitly, before the variable is first read. /// /// /// /// /// This variable's initial value is uninitialized (.locals without init) and the /// initial value is used. /// From C#'s point of view a call to System.Runtime.CompilerServices.Unsafe.SkipInit(out T) /// is generated after the declaration. /// /// /// /// /// This variable's initial value is uninitialized (.locals without init) and the /// initial value is not used. /// From C#'s point of view no implicit initialization occurs, because the code assigns a value /// explicitly, before the variable is first read. /// /// /// public bool UsesInitialValue { get { return usesInitialValue; } set { if (Kind == VariableKind.Parameter && !value) throw new InvalidOperationException("Cannot remove UsesInitialValue from parameters"); usesInitialValue = value; } } [Obsolete("Use 'UsesInitialValue' instead.")] public bool HasInitialValue { get => UsesInitialValue; set => UsesInitialValue = value; } /// /// Gets whether the variable is in SSA form: /// There is exactly 1 store, and every load sees the value from that store. /// /// /// Note: the single store is not necessary a store instruction, it might also /// be the use of the implicit initial value. /// For example: for parameters, IsSingleDefinition will only return true if /// the parameter is never assigned to within the function. /// public bool IsSingleDefinition { get { return StoreCount == 1 && AddressCount == 0; } } /// /// Gets whether the variable is dead - unused. /// public bool IsDead { get { return StoreInstructions.Count == 0 && LoadCount == 0 && AddressCount == 0; } } /// /// The field which was converted to a local variable. /// Set when the variable is from a 'yield return' or 'async' state machine. /// public IField? StateMachineField; /// /// If enabled, remove dead stores to this variable as if the "Remove dead code" option is enabled. /// internal bool RemoveIfRedundant; public ILVariable(VariableKind kind, IType type, int? index = null) { if (type == null) throw new ArgumentNullException(nameof(type)); this.Kind = kind; this.type = type; this.StackType = type.GetStackType(); this.Index = index; if (kind == VariableKind.Parameter) { this.InitialValueIsInitialized = true; this.UsesInitialValue = true; } CheckInvariant(); } public ILVariable(VariableKind kind, IType type, StackType stackType, int? index = null) { if (type == null) throw new ArgumentNullException(nameof(type)); this.Kind = kind; this.type = type; this.StackType = stackType; this.Index = index; if (kind == VariableKind.Parameter) { this.InitialValueIsInitialized = true; this.UsesInitialValue = true; } CheckInvariant(); } public override string? ToString() { return Name; } internal void WriteDefinitionTo(ITextOutput output) { if (IsRefReadOnly) { output.Write("readonly "); } switch (Kind) { case VariableKind.Local: output.Write("local "); break; case VariableKind.PinnedLocal: output.Write("pinned local "); break; case VariableKind.PinnedRegionLocal: output.Write("PinnedRegion local "); break; case VariableKind.Parameter: output.Write("param "); break; case VariableKind.ExceptionLocal: output.Write("exception local "); break; case VariableKind.ExceptionStackSlot: output.Write("exception stack "); break; case VariableKind.StackSlot: output.Write("stack "); break; case VariableKind.InitializerTarget: output.Write("initializer "); break; case VariableKind.ForeachLocal: output.Write("foreach "); break; case VariableKind.UsingLocal: output.Write("using "); break; case VariableKind.NamedArgument: output.Write("named_arg "); break; case VariableKind.DisplayClassLocal: output.Write("display_class local "); break; case VariableKind.PatternLocal: output.Write("pattern local "); break; case VariableKind.DeconstructionInitTemporary: output.Write("deconstruction init temporary "); break; default: throw new ArgumentOutOfRangeException(); } output.WriteLocalReference(this.Name, this, isDefinition: true); output.Write(" : "); Type.WriteTo(output); output.Write('('); if (Kind == VariableKind.Parameter || Kind == VariableKind.Local || Kind == VariableKind.PinnedLocal || Kind == VariableKind.PinnedRegionLocal) { output.Write("Index={0}, ", Index); } output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount); if (Kind != VariableKind.Parameter) { if (initialValueIsInitialized) { output.Write(" init"); } else { output.Write(" uninit"); } if (usesInitialValue) { output.Write(" used"); } else { output.Write(" unused"); } } if (CaptureScope != null) { output.Write(" captured in "); output.WriteLocalReference(CaptureScope.EntryPoint?.Label, CaptureScope); } if (StateMachineField != null) { output.Write(" from state-machine"); } } internal void WriteTo(ITextOutput output) { output.WriteLocalReference(this.Name, this); } /// /// Gets whether this variable occurs within the specified instruction. /// internal bool IsUsedWithin(ILInstruction inst) { if (inst is IInstructionWithVariableOperand iwvo && iwvo.Variable == this) { return true; } foreach (var child in inst.Children) { if (IsUsedWithin(child)) return true; } return false; } } public interface IInstructionWithVariableOperand { ILVariable Variable { get; set; } int IndexInVariableInstructionMapping { get; set; } } public interface IStoreInstruction : IInstructionWithVariableOperand { int IndexInStoreInstructionList { get; set; } } interface ILoadInstruction : IInstructionWithVariableOperand { int IndexInLoadInstructionList { get; set; } } interface IAddressInstruction : IInstructionWithVariableOperand { int IndexInAddressInstructionList { get; set; } } public class ILVariableEqualityComparer : IEqualityComparer { public static readonly ILVariableEqualityComparer Instance = new ILVariableEqualityComparer(); public bool Equals(ILVariable? x, ILVariable? y) { if (x == y) return true; if (x == null || y == null) return false; if (x.Kind == VariableKind.StackSlot || y.Kind == VariableKind.StackSlot) return false; if (x.Kind == VariableKind.PatternLocal || y.Kind == VariableKind.PatternLocal) return false; if (!(x.Function == y.Function && x.Kind == y.Kind)) return false; if (x.Index != null) return x.Index == y.Index; else if (x.StateMachineField != null) return x.StateMachineField.Equals(y.StateMachineField); else return false; } public int GetHashCode(ILVariable obj) { if (obj.Kind is VariableKind.StackSlot or VariableKind.PatternLocal) return obj.GetHashCode(); if (obj.Index != null) return (obj.Function, obj.Kind, obj.Index).GetHashCode(); if (obj.StateMachineField != null) return (obj.Function, obj.Kind, obj.StateMachineField).GetHashCode(); return obj.GetHashCode(); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/InstructionFlags.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.IL { [Flags] public enum InstructionFlags { None = 0, /// /// The instruction may read from local variables. /// MayReadLocals = 0x10, /// /// The instruction may write to local variables. /// /// /// This flag is not set for indirect writes to local variables through pointers. /// Ensure you also check the SideEffect flag when checking for instructions that might write to locals. /// MayWriteLocals = 0x20, /// /// The instruction may have side effects, such as accessing heap memory, /// performing system calls, writing to local variables through pointers, etc. /// /// /// Throwing an exception or directly writing to local variables /// is not considered a side effect, and is modeled by separate flags. /// SideEffect = 0x40, /// /// The instruction may throw an exception. /// MayThrow = 0x100, /// /// The instruction may exit with a branch or leave. /// MayBranch = 0x200, /// /// The instruction may jump to the closest containing nullable.rewrap instruction. /// MayUnwrapNull = 0x400, /// /// The instruction performs unconditional control flow, so that its endpoint is unreachable. /// /// /// If EndPointUnreachable is set, either MayThrow or MayBranch should also be set /// (unless the instruction represents an infinite loop). /// EndPointUnreachable = 0x800, /// /// The instruction contains some kind of internal control flow. /// /// /// If this flag is not set, all descendants of the instruction are fully evaluated (modulo MayThrow/MayBranch/MayUnwrapNull) /// in left-to-right pre-order. /// /// Note that branch instructions don't have this flag set, because their control flow is not internal /// (and they don't have any unusual argument evaluation rules). /// ControlFlow = 0x1000, } } ================================================ FILE: ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { public static partial class InstructionOutputExtensions { public static void Write(this ITextOutput output, OpCode opCode) { output.Write(originalOpCodeNames[(int)opCode]); } public static void Write(this ITextOutput output, StackType stackType) { output.Write(stackType.ToString().ToLowerInvariant()); } public static void Write(this ITextOutput output, PrimitiveType primitiveType) { output.Write(primitiveType.ToString().ToLowerInvariant()); } public static void WriteTo(this IType type, ITextOutput output) { output.WriteReference(type, type.ReflectionName); } public static void WriteTo(this IMember member, ITextOutput output) { if (member is IMethod method && method.IsConstructor) output.WriteReference(member, method.DeclaringType?.Name + "." + method.Name); else output.WriteReference(member, member.Name); } public static void WriteTo(this Interval interval, ITextOutput output, ILAstWritingOptions options) { if (!options.ShowILRanges) return; if (interval.IsEmpty) output.Write("[empty] "); else output.Write($"[{interval.Start:x4}..{interval.InclusiveEnd:x4}] "); } public static void WriteTo(this EntityHandle entity, MetadataFile module, ITextOutput output, Metadata.MetadataGenericContext genericContext, ILNameSyntax syntax = ILNameSyntax.Signature) { if (entity.IsNil) { output.Write(""); return; } if (module == null) throw new ArgumentNullException(nameof(module)); var metadata = module.Metadata; Action signature; MethodSignature> methodSignature; string memberName; switch (entity.Kind) { case HandleKind.TypeDefinition: { var td = metadata.GetTypeDefinition((TypeDefinitionHandle)entity); output.WriteReference(module, entity, td.GetFullTypeName(metadata).ToILNameString()); break; } case HandleKind.TypeReference: { var tr = metadata.GetTypeReference((TypeReferenceHandle)entity); EntityHandle resolutionScope; try { resolutionScope = tr.ResolutionScope; } catch (BadImageFormatException) { resolutionScope = default; } if (!resolutionScope.IsNil) { output.Write("["); var currentTypeRef = tr; while (currentTypeRef.ResolutionScope.Kind == HandleKind.TypeReference) { currentTypeRef = metadata.GetTypeReference((TypeReferenceHandle)currentTypeRef.ResolutionScope); } switch (currentTypeRef.ResolutionScope.Kind) { case HandleKind.ModuleDefinition: var modDef = metadata.GetModuleDefinition(); output.Write(DisassemblerHelpers.Escape(metadata.GetString(modDef.Name))); break; case HandleKind.ModuleReference: break; case HandleKind.AssemblyReference: var asmRef = metadata.GetAssemblyReference((AssemblyReferenceHandle)currentTypeRef.ResolutionScope); output.Write(DisassemblerHelpers.Escape(metadata.GetString(asmRef.Name))); break; } output.Write("]"); } output.WriteReference(module, entity, entity.GetFullTypeName(metadata).ToILNameString()); break; } case HandleKind.TypeSpecification: { var ts = metadata.GetTypeSpecification((TypeSpecificationHandle)entity); signature = ts.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); signature(syntax); break; } case HandleKind.FieldDefinition: { var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)entity); signature = fd.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new Metadata.MetadataGenericContext(fd.GetDeclaringType(), metadata)); signature(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); ((EntityHandle)fd.GetDeclaringType()).WriteTo(module, output, default, ILNameSyntax.TypeName); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(metadata.GetString(fd.Name))); break; } case HandleKind.MethodDefinition: { var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); methodSignature = md.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new Metadata.MetadataGenericContext((MethodDefinitionHandle)entity, metadata)); methodSignature.Header.WriteTo(output); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = md.GetDeclaringType(); if (!declaringType.IsNil) { ((EntityHandle)declaringType).WriteTo(module, output, genericContext, ILNameSyntax.TypeName); output.Write("::"); } bool isCompilerControlled = (md.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; if (isCompilerControlled) { output.WriteReference(module, entity, DisassemblerHelpers.Escape(metadata.GetString(md.Name) + "$PST" + MetadataTokens.GetToken(entity).ToString("X8"))); } else { output.WriteReference(module, entity, DisassemblerHelpers.Escape(metadata.GetString(md.Name))); } var genericParameters = md.GetGenericParameters(); if (genericParameters.Count > 0) { output.Write('<'); for (int i = 0; i < genericParameters.Count; i++) { if (i > 0) output.Write(", "); var gp = metadata.GetGenericParameter(genericParameters[i]); if ((gp.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) { output.Write("class "); } else if ((gp.Attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) { output.Write("valuetype "); } if ((gp.Attributes & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint) { output.Write(".ctor "); } var constraints = gp.GetConstraints(); if (constraints.Count > 0) { output.Write('('); for (int j = 0; j < constraints.Count; j++) { if (j > 0) output.Write(", "); var constraint = metadata.GetGenericParameterConstraint(constraints[j]); constraint.Type.WriteTo(module, output, new Metadata.MetadataGenericContext((MethodDefinitionHandle)entity, metadata), ILNameSyntax.TypeName); } output.Write(") "); } if ((gp.Attributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant) { output.Write('-'); } else if ((gp.Attributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant) { output.Write('+'); } output.Write(DisassemblerHelpers.Escape(metadata.GetString(gp.Name))); } output.Write('>'); } WriteParameterList(output, methodSignature); break; } case HandleKind.MemberReference: var mr = metadata.GetMemberReference((MemberReferenceHandle)entity); memberName = metadata.GetString(mr.Name); switch (mr.GetKind()) { case MemberReferenceKind.Method: methodSignature = mr.DecodeMethodSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); methodSignature.Header.WriteTo(output); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, mr.Parent, genericContext, syntax); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName)); WriteParameterList(output, methodSignature); break; case MemberReferenceKind.Field: var fieldSignature = mr.DecodeFieldSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); fieldSignature(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, mr.Parent, genericContext, syntax); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName)); break; } break; case HandleKind.MethodSpecification: var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)entity); var substitution = ms.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); switch (ms.Method.Kind) { case HandleKind.MethodDefinition: var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)ms.Method); var methodName = metadata.GetString(methodDefinition.Name); methodSignature = methodDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); methodSignature.Header.WriteTo(output); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = methodDefinition.GetDeclaringType(); if (!declaringType.IsNil) { ((EntityHandle)declaringType).WriteTo(module, output, genericContext, ILNameSyntax.TypeName); output.Write("::"); } bool isCompilerControlled = (methodDefinition.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; if (isCompilerControlled) { output.Write(DisassemblerHelpers.Escape(methodName + "$PST" + MetadataTokens.GetToken(ms.Method).ToString("X8"))); } else { output.Write(DisassemblerHelpers.Escape(methodName)); } WriteTypeParameterList(output, syntax, substitution); WriteParameterList(output, methodSignature); break; case HandleKind.MemberReference: var memberReference = metadata.GetMemberReference((MemberReferenceHandle)ms.Method); memberName = metadata.GetString(memberReference.Name); methodSignature = memberReference.DecodeMethodSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); methodSignature.Header.WriteTo(output); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, memberReference.Parent, genericContext, syntax); output.Write("::"); output.Write(DisassemblerHelpers.Escape(memberName)); WriteTypeParameterList(output, syntax, substitution); WriteParameterList(output, methodSignature); break; } break; case HandleKind.StandaloneSignature: var standaloneSig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)entity); var header = metadata.GetBlobReader(standaloneSig.Signature).ReadSignatureHeader(); switch (header.Kind) { case SignatureKind.Method: methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureTypeProvider(module, output), genericContext); methodSignature.Header.WriteTo(output); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); WriteParameterList(output, methodSignature); break; default: output.Write($"@{MetadataTokens.GetToken(entity):X8} /* signature {header.Kind} */"); break; } break; default: output.Write($"@{MetadataTokens.GetToken(entity):X8}"); break; } } static void WriteTypeParameterList(ITextOutput output, ILNameSyntax syntax, System.Collections.Immutable.ImmutableArray> substitution) { output.Write('<'); for (int i = 0; i < substitution.Length; i++) { if (i > 0) output.Write(", "); substitution[i](syntax); } output.Write('>'); } internal static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) { output.Write("("); for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { if (i > 0) output.Write(", "); if (i == methodSignature.RequiredParameterCount) output.Write("..., "); methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); } output.Write(")"); } internal static void WriteTo(this in SignatureHeader header, ITextOutput output) { if (header.HasExplicitThis) { output.Write("instance explicit "); } else if (header.IsInstance) { output.Write("instance "); } if (header.CallingConvention != SignatureCallingConvention.Default) { output.Write(header.CallingConvention.ToILSyntax()); output.Write(' '); } } static void WriteParent(ITextOutput output, MetadataFile metadataFile, EntityHandle parentHandle, Metadata.MetadataGenericContext genericContext, ILNameSyntax syntax) { switch (parentHandle.Kind) { case HandleKind.MethodDefinition: var methodDef = metadataFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)parentHandle); ((EntityHandle)methodDef.GetDeclaringType()).WriteTo(metadataFile, output, genericContext, syntax); break; case HandleKind.ModuleReference: output.Write('['); var moduleRef = metadataFile.Metadata.GetModuleReference((ModuleReferenceHandle)parentHandle); output.Write(metadataFile.Metadata.GetString(moduleRef.Name)); output.Write(']'); break; case HandleKind.TypeDefinition: case HandleKind.TypeReference: case HandleKind.TypeSpecification: parentHandle.WriteTo(metadataFile, output, genericContext, syntax); break; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Await.cs ================================================ #nullable enable // Copyright (c) 2017 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class Await { public IMethod? GetAwaiterMethod; public IMethod? GetResultMethod; } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public enum BinaryNumericOperator : byte { None, Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, ShiftLeft, ShiftRight } public partial class BinaryNumericInstruction : BinaryInstruction, ILiftableInstruction { /// /// Gets whether the instruction checks for overflow. /// public readonly bool CheckForOverflow; /// /// For integer operations that depend on the sign, specifies whether the operation /// is signed or unsigned. /// For instructions that produce the same result for either sign, returns Sign.None. /// public readonly Sign Sign; public readonly StackType LeftInputType; public readonly StackType RightInputType; /// /// The operator used by this binary operator instruction. /// public readonly BinaryNumericOperator Operator; /// /// Gets whether this is a lifted nullable operation. /// /// /// A lifted binary operation allows its arguments to be a value of type Nullable{T}, where /// T.GetStackType() == [Left|Right]InputType. /// If both input values are non-null: /// * they are sign/zero-extended to the corresponding InputType (based on T's sign) /// * the underlying numeric operator is applied /// * the result is wrapped in a Nullable{UnderlyingResultType}. /// If either input is null, the instruction evaluates to default(UnderlyingResultType?). /// (this result type is underspecified, since there may be multiple C# types for the stack type) /// public bool IsLifted { get; } readonly StackType resultType; public BinaryNumericInstruction(BinaryNumericOperator op, ILInstruction left, ILInstruction right, bool checkForOverflow, Sign sign) : this(op, left, right, left.ResultType, right.ResultType, checkForOverflow, sign) { } public BinaryNumericInstruction(BinaryNumericOperator op, ILInstruction left, ILInstruction right, StackType leftInputType, StackType rightInputType, bool checkForOverflow, Sign sign, bool isLifted = false) : base(OpCode.BinaryNumericInstruction, left, right) { this.CheckForOverflow = checkForOverflow; this.Sign = sign; this.Operator = op; this.LeftInputType = leftInputType; this.RightInputType = rightInputType; this.IsLifted = isLifted; this.resultType = ComputeResultType(op, LeftInputType, RightInputType); } internal static StackType ComputeResultType(BinaryNumericOperator op, StackType left, StackType right) { // Based on Table 2: Binary Numeric Operations // also works for Table 5: Integer Operations // and for Table 7: Overflow Arithmetic Operations if (left == right || op == BinaryNumericOperator.ShiftLeft || op == BinaryNumericOperator.ShiftRight) { // Shift op codes use Table 6 return left; } if (left == StackType.Ref || right == StackType.Ref) { if (left == StackType.Ref && right == StackType.Ref) { // sub(&, &) = I Debug.Assert(op == BinaryNumericOperator.Sub); return StackType.I; } else { // add/sub with I or I4 and & Debug.Assert(op == BinaryNumericOperator.Add || op == BinaryNumericOperator.Sub); return StackType.Ref; } } return StackType.Unknown; } public StackType UnderlyingResultType { get => resultType; } public sealed override StackType ResultType { get => IsLifted ? StackType.O : resultType; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); if (!IsLifted) { Debug.Assert(LeftInputType == Left.ResultType); Debug.Assert(RightInputType == Right.ResultType); } } protected override InstructionFlags ComputeFlags() { var flags = base.ComputeFlags(); if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) flags |= InstructionFlags.MayThrow; return flags; } public override InstructionFlags DirectFlags { get { if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) return base.DirectFlags | InstructionFlags.MayThrow; return base.DirectFlags; } } internal static string GetOperatorName(BinaryNumericOperator @operator) { switch (@operator) { case BinaryNumericOperator.Add: return "add"; case BinaryNumericOperator.Sub: return "sub"; case BinaryNumericOperator.Mul: return "mul"; case BinaryNumericOperator.Div: return "div"; case BinaryNumericOperator.Rem: return "rem"; case BinaryNumericOperator.BitAnd: return "bit.and"; case BinaryNumericOperator.BitOr: return "bit.or"; case BinaryNumericOperator.BitXor: return "bit.xor"; case BinaryNumericOperator.ShiftLeft: return "bit.shl"; case BinaryNumericOperator.ShiftRight: return "bit.shr"; default: throw new ArgumentOutOfRangeException(); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write("." + GetOperatorName(Operator)); if (CheckForOverflow) { output.Write(".ovf"); } if (Sign == Sign.Unsigned) { output.Write(".unsigned"); } else if (Sign == Sign.Signed) { output.Write(".signed"); } output.Write('.'); output.Write(resultType.ToString().ToLowerInvariant()); if (IsLifted) { output.Write(".lifted"); } output.Write('('); Left.WriteTo(output, options); output.Write(", "); Right.WriteTo(output, options); output.Write(')'); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Block.cs ================================================ #nullable enable // Copyright (c) 2014-2016 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { /// /// A block consists of a list of IL instructions. /// /// /// Note: if execution reaches the end of the instruction list, /// the FinalInstruction (which is not part of the list) will be executed. /// The block returns returns the result value of the FinalInstruction. /// For blocks returning void, the FinalInstruction will usually be 'nop'. /// /// /// There are three different uses for blocks: /// 1) Blocks in block containers. Used as targets for Branch instructions. /// 2) Blocks to group a bunch of statements, e.g. the TrueInst of an IfInstruction. /// 3) Inline blocks that evaluate to a value, e.g. for array initializers. /// partial class Block : ILInstruction { public static readonly SlotInfo InstructionSlot = new SlotInfo("Instruction", isCollection: true); public static readonly SlotInfo FinalInstructionSlot = new SlotInfo("FinalInstruction"); public readonly BlockKind Kind; public readonly InstructionCollection Instructions; ILInstruction finalInstruction = null!; /// /// For blocks in a block container, this field holds /// the number of incoming control flow edges to this block. /// /// /// This variable is automatically updated when adding/removing branch instructions from the ILAst, /// or when adding the block as an entry point to a BlockContainer. /// public int IncomingEdgeCount { get; internal set; } /// /// A 'final instruction' that gets executed after the Instructions collection. /// Provides the return value for the block. /// /// /// Blocks in containers must have 'Nop' as a final instruction. /// /// Note that the FinalInstruction is included in Block.Children, /// but not in Block.Instructions! /// public ILInstruction FinalInstruction { get { return finalInstruction; } set { ValidateChild(value); SetChildInstruction(ref finalInstruction, value, Instructions.Count); } } protected internal override void InstructionCollectionUpdateComplete() { base.InstructionCollectionUpdateComplete(); if (finalInstruction.Parent == this) finalInstruction.ChildIndex = Instructions.Count; } public Block(BlockKind kind = BlockKind.ControlFlow) : base(OpCode.Block) { this.Kind = kind; this.Instructions = new InstructionCollection(this, 0); this.FinalInstruction = new Nop(); } public override ILInstruction Clone() { Block clone = new Block(Kind); clone.AddILRange(this); clone.Instructions.AddRange(this.Instructions.Select(inst => inst.Clone())); clone.FinalInstruction = this.FinalInstruction.Clone(); return clone; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); for (int i = 0; i < Instructions.Count - 1; i++) { // only the last instruction may have an unreachable endpoint Debug.Assert(!Instructions[i].HasFlag(InstructionFlags.EndPointUnreachable)); } switch (this.Kind) { case BlockKind.ControlFlow: Debug.Assert(finalInstruction.OpCode == OpCode.Nop); break; case BlockKind.CallInlineAssign: Debug.Assert(MatchInlineAssignBlock(out _, out _)); break; case BlockKind.CallWithNamedArgs: Debug.Assert(finalInstruction is CallInstruction); foreach (var inst in Instructions) { var stloc = inst as StLoc; DebugAssert(stloc != null, "Instructions in CallWithNamedArgs must be assignments"); DebugAssert(stloc.Variable.Kind == VariableKind.NamedArgument); DebugAssert(stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 1); DebugAssert(stloc.Variable.LoadInstructions.Single().Parent == finalInstruction); } var call = (CallInstruction)finalInstruction; if (call.IsInstanceCall) { // special case: with instance calls, Instructions[0] must be for the this parameter ILVariable v = ((StLoc)Instructions[0]).Variable; Debug.Assert(call.Arguments[0].MatchLdLoc(v)); } break; case BlockKind.ArrayInitializer: var final = finalInstruction as LdLoc; Debug.Assert(final != null && final.Variable.IsSingleDefinition && final.Variable.Kind == VariableKind.InitializerTarget); IType? type = null; Debug.Assert(Instructions[0].MatchStLoc(final!.Variable, out var init) && init.MatchNewArr(out type)); for (int i = 1; i < Instructions.Count; i++) { DebugAssert(Instructions[i].MatchStObj(out ILInstruction? target, out _, out var t) && type != null && type.Equals(t)); DebugAssert(target.MatchLdElema(out t, out ILInstruction? array) && type.Equals(t)); DebugAssert(array.MatchLdLoc(out ILVariable? v) && v == final.Variable); } break; case BlockKind.CollectionInitializer: case BlockKind.ObjectInitializer: var final2 = finalInstruction as LdLoc; Debug.Assert(final2 != null); var initVar2 = final2!.Variable; Debug.Assert(initVar2.StoreCount == 1 && initVar2.Kind == VariableKind.InitializerTarget); IType? type2 = null; bool condition = Instructions[0].MatchStLoc(final2.Variable, out var init2); Debug.Assert(condition); Debug.Assert(init2 is NewObj || init2 is DefaultValue || (init2 is CallInstruction c && c.Method.FullNameIs("System.Activator", "CreateInstance") && c.Method.TypeArguments.Count == 1) || (init2 is Block named && named.Kind == BlockKind.CallWithNamedArgs)); switch (init2) { case NewObj newObj: type2 = newObj.Method.DeclaringType; break; case DefaultValue defaultValue: type2 = defaultValue.Type; break; case Block callWithNamedArgs when callWithNamedArgs.Kind == BlockKind.CallWithNamedArgs: type2 = ((CallInstruction)callWithNamedArgs.FinalInstruction).Method.ReturnType; break; case CallInstruction ci2 when TransformCollectionAndObjectInitializers.IsRecordCloneMethodCall(ci2): type2 = ci2.Method.DeclaringType; break; case Call c2 when c2.Method.FullNameIs("System.Activator", "CreateInstance") && c2.Method.TypeArguments.Count == 1: type2 = c2.Method.TypeArguments[0]; break; default: Debug.Assert(false); break; } for (int i = 1; i < Instructions.Count; i++) { Debug.Assert(Instructions[i] is StLoc || AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != AccessPathKind.Invalid); } break; case BlockKind.DeconstructionConversions: Debug.Assert(this.SlotInfo == DeconstructInstruction.ConversionsSlot); break; case BlockKind.DeconstructionAssignments: Debug.Assert(this.SlotInfo == DeconstructInstruction.AssignmentsSlot); break; case BlockKind.InterpolatedString: Debug.Assert(FinalInstruction is Call { Method: { Name: "ToStringAndClear" }, Arguments: { Count: 1 } }); var interpolInit = Instructions[0] as StLoc; DebugAssert(interpolInit != null && interpolInit.Variable.Kind == VariableKind.InitializerTarget && interpolInit.Variable.AddressCount == Instructions.Count && interpolInit.Variable.StoreCount == 1); for (int i = 1; i < Instructions.Count; i++) { Call? inst = Instructions[i] as Call; DebugAssert(inst != null); DebugAssert(inst.Arguments.Count >= 1 && inst.Arguments[0].MatchLdLoca(interpolInit.Variable)); } break; } } public override StackType ResultType { get { return finalInstruction.ResultType; } } internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { switch (Kind) { case BlockKind.ControlFlow when Parent is BlockContainer: case BlockKind.ArrayInitializer: case BlockKind.CollectionInitializer: case BlockKind.ObjectInitializer: case BlockKind.CallInlineAssign: // Allow inlining into the first instruction of the block return childIndex == 0; default: return false; } } /// /// Gets the name of this block. /// public string Label { get { return Disassembler.DisassemblerHelpers.OffsetToString(this.StartILOffset); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("Block "); output.WriteLocalReference(Label, this, isDefinition: true); if (Kind != BlockKind.ControlFlow) output.Write($" ({Kind})"); if (Parent is BlockContainer) output.Write(" (incoming: {0})", IncomingEdgeCount); output.Write(' '); output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); int index = 0; foreach (var inst in Instructions) { if (options.ShowChildIndexInBlock) { output.Write("[" + index + "] "); index++; } inst.WriteTo(output, options); output.WriteLine(); } if (finalInstruction.OpCode != OpCode.Nop) { output.Write("final: "); finalInstruction.WriteTo(output, options); output.WriteLine(); } output.Unindent(); output.Write("}"); output.MarkFoldEnd(); } protected override int GetChildCount() { return Instructions.Count + 1; } protected override ILInstruction GetChild(int index) { if (index == Instructions.Count) return finalInstruction; return Instructions[index]; } protected override void SetChild(int index, ILInstruction value) { if (index == Instructions.Count) FinalInstruction = value; else Instructions[index] = value; } protected override SlotInfo GetChildSlot(int index) { if (index == Instructions.Count) return FinalInstructionSlot; else return InstructionSlot; } protected override InstructionFlags ComputeFlags() { var flags = InstructionFlags.None; foreach (var inst in Instructions) { flags |= inst.Flags; } flags |= FinalInstruction.Flags; return flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } /// /// Deletes this block from its parent container. /// This may cause the indices of other blocks in that container to change. /// /// It is an error to call this method on blocks that are not directly within a container. /// It is also an error to call this method on the entry-point block. /// public void Remove() { Debug.Assert(ChildIndex > 0); var container = (BlockContainer)Parent!; Debug.Assert(container.Blocks[ChildIndex] == this); container.Blocks.SwapRemoveAt(ChildIndex); } /// /// Apply a list of transforms to this function. /// public void RunTransforms(IEnumerable transforms, BlockTransformContext context) { this.CheckInvariant(ILPhase.Normal); foreach (var transform in transforms) { context.CancellationToken.ThrowIfCancellationRequested(); Debug.Assert(context.IndexOfFirstAlreadyTransformedInstruction <= this.Instructions.Count); context.StepStartGroup(transform.GetType().Name); transform.Run(this, context); this.CheckInvariant(ILPhase.Normal); context.StepEndGroup(); } } /// /// Gets the predecessor of the given instruction. /// Returns null if inst.Parent is not a block. /// public static ILInstruction? GetPredecessor(ILInstruction inst) { if (inst.Parent is Block block && inst.ChildIndex > 0) { return block.Instructions[inst.ChildIndex - 1]; } else { return null; } } /// /// If inst is a block consisting of a single instruction, returns that instruction. /// Otherwise, returns the input instruction. /// [return: NotNullIfNotNull("inst")] public static ILInstruction? Unwrap(ILInstruction? inst) { if (inst is Block block) { if (block.Instructions.Count == 1 && block.finalInstruction.MatchNop()) return block.Instructions[0]; } return inst; } /// /// Gets the closest parent Block. /// Returns null, if the instruction is not a descendant of a Block. /// public static Block? FindClosestBlock(ILInstruction? inst) { var curr = inst; while (curr != null) { if (curr is Block b) return b; curr = curr.Parent; } return null; } /// /// Gets the closest ancestor that is child of a control-flow (top-level) Block. /// Returns null, if the instruction is not a descendant of a Block. /// public static ILInstruction? GetContainingStatement(ILInstruction inst) { var curr = inst; while (curr != null) { if (curr.Parent is Block { Kind: BlockKind.ControlFlow }) return curr; curr = curr.Parent; } return null; } public bool MatchInlineAssignBlock([NotNullWhen(true)] out CallInstruction? call, [NotNullWhen(true)] out ILInstruction? value) { call = null; value = null; if (this.Kind != BlockKind.CallInlineAssign) return false; if (this.Instructions.Count != 1) return false; call = this.Instructions[0] as CallInstruction; if (call == null || call.Arguments.Count == 0) return false; if (!call.Arguments.Last().MatchStLoc(out var tmp, out value)) return false; if (!(tmp.IsSingleDefinition && tmp.LoadCount == 1)) return false; return this.FinalInstruction.MatchLdLoc(tmp); } public bool MatchIfAtEndOfBlock([NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst) { condition = null; trueInst = null; falseInst = null; if (Instructions.Count < 2) return false; if (Instructions[Instructions.Count - 2].MatchIfInstruction(out condition, out trueInst)) { // Swap trueInst<>falseInst for every logic.not in the condition. falseInst = Instructions.Last(); while (condition.MatchLogicNot(out var arg)) { condition = arg; (trueInst, falseInst) = (falseInst, trueInst); } return true; } return false; } } public enum BlockKind { /// /// Block is used for control flow. /// All blocks in block containers must have this type. /// Control flow blocks cannot evaluate to a value (FinalInstruction must be Nop). /// ControlFlow, /// /// Block is used for array initializers, e.g. `new int[] { expr1, expr2 }`. /// ArrayInitializer, CollectionInitializer, ObjectInitializer, StackAllocInitializer, /// /// Block is used for using the result of a property setter inline. /// Example: Use(this.Property = value); /// This is only for inline assignments to property or indexers; other inline assignments work /// by using the result value of the stloc/stobj instructions. /// /// Constructed by TransformAssignment. /// Can be deconstructed using Block.MatchInlineAssignBlock(). /// /// /// Block { /// call setter(..., stloc s(...)) /// final: ldloc s /// } /// CallInlineAssign, /// /// Call using named arguments. /// /// /// Each instruction is assigning to a new local. /// The final instruction is a call. /// The locals for this block have exactly one store and /// exactly one load, which must be an immediate argument to the call. /// /// /// Block { /// stloc arg0 = ... /// stloc arg1 = ... /// final: call M(..., arg1, arg0, ...) /// } /// CallWithNamedArgs, /// /// /// DeconstructionConversions, /// /// /// DeconstructionAssignments, WithInitializer, /// /// String interpolation using DefaultInterpolatedStringHandler. /// /// /// Block { /// stloc I_0 = newobj DefaultInterpolatedStringHandler(...) /// call AppendXXX(I_0, ...) /// ... /// final: call ToStringAndClear(ldloc I_0) /// } /// InterpolatedString, } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics.CodeAnalysis; using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { /// /// A container of IL blocks. /// Each block is an extended basic block (branches may only jump to the beginning of blocks, not into the middle), /// and only branches within this container may reference the blocks in this container. /// That means that viewed from the outside, the block container has a single entry point (but possibly multiple exit points), /// and the same holds for every block within the container. /// /// All blocks in the container must perform unconditional control flow (falling through to the block end is not allowed). /// To exit the block container, use the 'leave' instruction. /// partial class BlockContainer : ILInstruction { public static readonly SlotInfo BlockSlot = new SlotInfo("Block", isCollection: true); public readonly InstructionCollection Blocks; public ContainerKind Kind { get; set; } public StackType ExpectedResultType { get; set; } int leaveCount; /// /// Gets the number of 'leave' instructions that target this BlockContainer. /// public int LeaveCount { get => leaveCount; internal set { leaveCount = value; InvalidateFlags(); } } Block? entryPoint; /// /// Gets the container's entry point. This is the first block in the Blocks collection. /// public Block EntryPoint { get { // HACK: While it's possible to have BlockContainers without entry point, // normally every container must have an entry point according to its invariant. // Thus it's easier on the transforms if this property returns a non-nullable EntryPoint. return entryPoint!; } private set { if (entryPoint != null && IsConnected) entryPoint.IncomingEdgeCount--; entryPoint = value; if (entryPoint != null && IsConnected) entryPoint.IncomingEdgeCount++; } } public BlockContainer(ContainerKind kind = ContainerKind.Normal, StackType expectedResultType = StackType.Void) : base(OpCode.BlockContainer) { this.Kind = kind; this.Blocks = new InstructionCollection(this, 0); this.ExpectedResultType = expectedResultType; } public override ILInstruction Clone() { BlockContainer clone = new BlockContainer(this.Kind, this.ExpectedResultType); clone.AddILRange(this); clone.Blocks.AddRange(this.Blocks.Select(block => (Block)block.Clone())); // Adjust branch instructions to point to the new container foreach (var branch in clone.Descendants.OfType()) { if (branch.TargetBlock != null && branch.TargetBlock.Parent == this) branch.TargetBlock = clone.Blocks[branch.TargetBlock.ChildIndex]; } foreach (var leave in clone.Descendants.OfType()) { if (leave.TargetContainer == this) leave.TargetContainer = clone; } return clone; } protected internal override void InstructionCollectionUpdateComplete() { base.InstructionCollectionUpdateComplete(); this.EntryPoint = this.Blocks.FirstOrDefault()!; } protected override void Connected() { base.Connected(); if (entryPoint != null) entryPoint.IncomingEdgeCount++; } protected override void Disconnected() { base.Disconnected(); if (entryPoint != null) entryPoint.IncomingEdgeCount--; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.WriteLocalReference("BlockContainer", this, isDefinition: true); output.Write(' '); switch (Kind) { case ContainerKind.Loop: output.Write("(while-true) "); break; case ContainerKind.Switch: output.Write("(switch) "); break; case ContainerKind.While: output.Write("(while) "); break; case ContainerKind.DoWhile: output.Write("(do-while) "); break; case ContainerKind.For: output.Write("(for) "); break; } output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); foreach (var inst in Blocks) { if (inst.Parent == this) { inst.WriteTo(output, options); } else { output.Write("stale reference to "); output.WriteLocalReference(inst.Label, inst); } output.WriteLine(); output.WriteLine(); } output.Unindent(); output.Write("}"); output.MarkFoldEnd(); } protected override int GetChildCount() { return Blocks.Count; } protected override ILInstruction GetChild(int index) { return Blocks[index]; } protected override void SetChild(int index, ILInstruction? value) { if (Blocks[index] != value) throw new InvalidOperationException("Cannot replace blocks in BlockContainer"); } protected override SlotInfo GetChildSlot(int index) { return BlockSlot; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Blocks.Count > 0 && EntryPoint == Blocks[0]); Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1); Debug.Assert(Parent is ILFunction || !ILRangeIsEmpty); Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable))); Debug.Assert(Blocks.All(b => b.Kind == BlockKind.ControlFlow)); // this also implies that the blocks don't use FinalInstruction Debug.Assert(TopologicalSort(deleteUnreachableBlocks: true).Count == Blocks.Count, "Container should not have any unreachable blocks"); Block? bodyStartBlock; switch (Kind) { case ContainerKind.Normal: break; case ContainerKind.Loop: Debug.Assert(EntryPoint.IncomingEdgeCount > 1); break; case ContainerKind.Switch: Debug.Assert(EntryPoint.Instructions.Count == 1); Debug.Assert(EntryPoint.Instructions[0] is SwitchInstruction); Debug.Assert(EntryPoint.IncomingEdgeCount == 1); break; case ContainerKind.While: Debug.Assert(EntryPoint.IncomingEdgeCount > 1); Debug.Assert(Blocks.Count >= 2); Debug.Assert(MatchConditionBlock(EntryPoint, out _, out bodyStartBlock)); Debug.Assert(bodyStartBlock == Blocks[1]); break; case ContainerKind.DoWhile: Debug.Assert(EntryPoint.IncomingEdgeCount > 1); Debug.Assert(Blocks.Count >= 2); Debug.Assert(MatchConditionBlock(Blocks.Last(), out _, out bodyStartBlock)); Debug.Assert(bodyStartBlock == EntryPoint); break; case ContainerKind.For: Debug.Assert(EntryPoint.IncomingEdgeCount == 2); Debug.Assert(Blocks.Count >= 3); Debug.Assert(MatchConditionBlock(EntryPoint, out _, out bodyStartBlock)); Debug.Assert(MatchIncrementBlock(Blocks.Last())); Debug.Assert(bodyStartBlock == Blocks[1]); break; } } protected override InstructionFlags ComputeFlags() { InstructionFlags flags = InstructionFlags.ControlFlow; foreach (var block in Blocks) { flags |= block.Flags; } // The end point of the BlockContainer is only reachable if there's a leave instruction if (LeaveCount == 0) flags |= InstructionFlags.EndPointUnreachable; else flags &= ~InstructionFlags.EndPointUnreachable; return flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { // Inlining into the entry-point is allowed as long as we're not moving code into a loop. // This is used to inline into the switch expression. return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1; } internal override bool PrepareExtract(int childIndex, ExtractionContext ctx) { // Un-inlining from the entry-point is allowed as long as we're not moving code out of a loop return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1; } /// /// Topologically sort the blocks. /// The new order is returned without modifying the BlockContainer. /// /// If true, unreachable blocks are not included in the new order. public List TopologicalSort(bool deleteUnreachableBlocks = false) { // Visit blocks in post-order BitSet visited = new BitSet(Blocks.Count); List postOrder = new List(); GraphTraversal.DepthFirstSearch(new[] { EntryPoint }, MarkAsVisited, Successors, postOrder.Add, reverseSuccessors: true); postOrder.Reverse(); if (!deleteUnreachableBlocks) { for (int i = 0; i < Blocks.Count; i++) { if (!visited[i]) postOrder.Add(Blocks[i]); } } return postOrder; bool MarkAsVisited(Block block) { Debug.Assert(block.Parent == this); if (!visited[block.ChildIndex]) { visited[block.ChildIndex] = true; return true; } else { return false; } } IEnumerable Successors(Block block) { foreach (var branch in block.Descendants.OfType()) { if (branch.TargetBlock.Parent == this) { yield return branch.TargetBlock; } } } } /// /// Topologically sort the blocks. /// /// If true, delete unreachable blocks. public void SortBlocks(bool deleteUnreachableBlocks = false) { if (Blocks.Count < 2) return; var newOrder = TopologicalSort(deleteUnreachableBlocks); Debug.Assert(newOrder[0] == Blocks[0]); Blocks.ReplaceList(newOrder); } public static BlockContainer? FindClosestContainer(ILInstruction? inst) { while (inst != null) { if (inst is BlockContainer bc) return bc; inst = inst.Parent; } return null; } public static BlockContainer? FindClosestSwitchContainer(ILInstruction? inst) { while (inst != null) { if (inst is BlockContainer { Kind: ContainerKind.Switch } bc) return bc; inst = inst.Parent; } return null; } public bool MatchConditionBlock(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out Block? bodyStartBlock) { condition = null; bodyStartBlock = null; if (block.Instructions.Count != 1) return false; if (!block.Instructions[0].MatchIfInstruction(out condition, out var trueInst, out var falseInst)) return false; return falseInst.MatchLeave(this) && trueInst.MatchBranch(out bodyStartBlock); } public bool MatchIncrementBlock(Block block) { if (block.Instructions.Count == 0) return false; if (!block.Instructions.Last().MatchBranch(EntryPoint)) return false; return true; } /// /// If the container consists of a single block with a single instruction, /// returns that instruction. /// Otherwise returns the block, or the container itself if it has multiple blocks. /// public ILInstruction SingleInstruction() { if (Blocks.Count != 1) return this; if (Blocks[0].Instructions.Count != 1) return Blocks[0]; return Blocks[0].Instructions[0]; } } public enum ContainerKind { /// /// Normal container that contains control-flow blocks. /// Normal, /// /// A while-true loop. /// Continue is represented as branch to entry-point. /// Return/break is represented as leave. /// Loop, /// /// Container that has a switch instruction as entry-point. /// Goto case is represented as branch. /// Break is represented as leave. /// Switch, /// /// while-loop. /// The entry-point is a block consisting of a single if instruction /// that if true: jumps to the head of the loop body, /// if false: leaves the block. /// Continue is a branch to the entry-point. /// Break is a leave. /// While, /// /// do-while-loop. /// The entry-point is a block that is the head of the loop body. /// The last block consists of a single if instruction /// that if true: jumps to the head of the loop body, /// if false: leaves the block. /// Only the last block is allowed to jump to the entry-point. /// Continue is a branch to the last block. /// Break is a leave. /// DoWhile, /// /// for-loop. /// The entry-point is a block consisting of a single if instruction /// that if true: jumps to the head of the loop body, /// if false: leaves the block. /// The last block is the increment block. /// Only the last block is allowed to jump to the entry-point. /// Continue is a branch to the last block. /// Break is a leave. /// For } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Branch.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { /// /// Unconditional branch. goto target; /// /// /// When jumping to the entrypoint of the current block container, the branch represents a continue statement. /// partial class Branch : SimpleInstruction, IBranchOrLeaveInstruction { readonly int targetILOffset; Block? targetBlock; public Branch(int targetILOffset) : base(OpCode.Branch) { this.targetILOffset = targetILOffset; } public Branch(Block targetBlock) : base(OpCode.Branch) { this.targetBlock = targetBlock ?? throw new ArgumentNullException(nameof(targetBlock)); this.targetILOffset = targetBlock.StartILOffset; } public int TargetILOffset { get { return targetBlock != null ? targetBlock.StartILOffset : targetILOffset; } } public Block TargetBlock { get { // HACK: We treat TargetBlock as non-nullable publicly, because it's only null inside // the ILReader, and becomes non-null once the BlockBuilder has run. return targetBlock!; } set { if (targetBlock != null && IsConnected) targetBlock.IncomingEdgeCount--; targetBlock = value; if (targetBlock != null && IsConnected) targetBlock.IncomingEdgeCount++; } } /// /// Gets the BlockContainer that contains the target block. /// public BlockContainer TargetContainer { get { return (BlockContainer)targetBlock?.Parent!; } } protected override void Connected() { base.Connected(); if (targetBlock != null) targetBlock.IncomingEdgeCount++; } protected override void Disconnected() { base.Disconnected(); if (targetBlock != null) targetBlock.IncomingEdgeCount--; } public string TargetLabel { get { return targetBlock != null ? targetBlock.Label : string.Format("IL_{0:x4}", TargetILOffset); } } /// /// Gets whether this branch executes at least one finally block before jumping to the target block. /// public bool TriggersFinallyBlock { get { return GetExecutesFinallyBlock(this, TargetContainer); } } internal static bool GetExecutesFinallyBlock(ILInstruction? inst, BlockContainer? container) { for (; inst != container && inst != null; inst = inst.Parent) { if (inst.Parent is TryFinally && inst.SlotInfo == TryFinally.TryBlockSlot) return true; } return false; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); if (phase > ILPhase.InILReader) { Debug.Assert(targetBlock?.Parent is BlockContainer); Debug.Assert(this.IsDescendantOf(targetBlock!.Parent!)); Debug.Assert(targetBlock!.Parent!.Children[targetBlock.ChildIndex] == targetBlock); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); output.WriteLocalReference(TargetLabel, (object?)targetBlock ?? TargetILOffset); } } interface IBranchOrLeaveInstruction { BlockContainer TargetContainer { get; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs ================================================ #nullable enable // Copyright (c) 2017 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class CallIndirect { // Note: while in IL the arguments come first and the function pointer last; // in the ILAst we're handling it as in C#: the function pointer is evaluated first, the arguments later. public static readonly SlotInfo FunctionPointerSlot = new SlotInfo("FunctionPointer", canInlineInto: true); public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true); ILInstruction functionPointer = null!; public readonly InstructionCollection Arguments; public bool IsInstance { get; } public bool HasExplicitThis { get; } public FunctionPointerType FunctionPointerType { get; } public ILInstruction FunctionPointer { get { return functionPointer; } set { ValidateChild(value); SetChildInstruction(ref functionPointer, value, 0); } } public CallIndirect(bool isInstance, bool hasExplicitThis, FunctionPointerType functionPointerType, ILInstruction functionPointer, IEnumerable arguments) : base(OpCode.CallIndirect) { this.IsInstance = isInstance; this.HasExplicitThis = hasExplicitThis; this.FunctionPointerType = functionPointerType; this.FunctionPointer = functionPointer; this.Arguments = new InstructionCollection(this, 1); this.Arguments.AddRange(arguments); } public override ILInstruction Clone() { return new CallIndirect(IsInstance, HasExplicitThis, FunctionPointerType, functionPointer.Clone(), this.Arguments.Select(inst => inst.Clone()) ).WithILRange(this); } public override StackType ResultType => FunctionPointerType.ReturnType.GetStackType(); internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Arguments.Count == FunctionPointerType.ParameterTypes.Length + (IsInstance ? 1 : 0)); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("call.indirect "); FunctionPointerType.ReturnType.WriteTo(output); output.Write('('); functionPointer.WriteTo(output, options); int firstArgument = IsInstance ? 1 : 0; if (firstArgument == 1) { output.Write(", "); Arguments[0].WriteTo(output, options); } foreach (var (inst, type) in Arguments.Zip(FunctionPointerType.ParameterTypes, (a, b) => (a, b))) { output.Write(", "); inst.WriteTo(output, options); output.Write(" : "); type.WriteTo(output); } if (Arguments.Count > 0) output.Write(')'); } protected override int GetChildCount() { return Arguments.Count + 1; } protected override ILInstruction GetChild(int index) { if (index == 0) return functionPointer; return Arguments[index - 1]; } protected override void SetChild(int index, ILInstruction value) { if (index == 0) FunctionPointer = value; else Arguments[index - 1] = value; } protected override SlotInfo GetChildSlot(int index) { if (index == 0) return FunctionPointerSlot; else return ArgumentSlot; } protected override InstructionFlags ComputeFlags() { var flags = this.DirectFlags; flags |= functionPointer.Flags; foreach (var inst in Arguments) { flags |= inst.Flags; } return flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } bool EqualSignature(CallIndirect other) { if (IsInstance != other.IsInstance) return false; if (HasExplicitThis != other.HasExplicitThis) return false; return FunctionPointerType.Equals(other.FunctionPointerType); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public abstract partial class CallInstruction : ILInstruction { public static CallInstruction Create(OpCode opCode, IMethod method) { switch (opCode) { case OpCode.Call: return new Call(method); case OpCode.CallVirt: return new CallVirt(method); case OpCode.NewObj: return new NewObj(method); default: throw new ArgumentException("Not a valid call opcode"); } } public readonly IMethod Method; /// /// Gets/Sets whether the call has the 'tail.' prefix. /// public bool IsTail; /// /// Gets/Sets the type specified in the 'constrained.' prefix. /// Returns null if no 'constrained.' prefix exists for this call. /// public IType? ConstrainedTo; /// /// Gets whether the IL stack was empty at the point of this call. /// (not counting the arguments/return value of the call itself) /// public bool ILStackWasEmpty; protected CallInstruction(OpCode opCode, IMethod method) : base(opCode) { this.Method = method ?? throw new ArgumentNullException(nameof(method)); this.Arguments = new InstructionCollection(this, 0); } /// /// Gets whether this is an instance call (i.e. whether the first argument is the 'this' pointer). /// public bool IsInstanceCall { get { return !(Method.IsStatic || OpCode == OpCode.NewObj); } } /// /// Gets the parameter for the argument with the specified index. /// Returns null for the this parameter. /// public IParameter? GetParameter(int argumentIndex) { int firstParamIndex = (Method.IsStatic || OpCode == OpCode.NewObj) ? 0 : 1; if (argumentIndex < firstParamIndex) { return null; // asking for 'this' parameter } return Method.Parameters[argumentIndex - firstParamIndex]; } public override StackType ResultType { get { if (OpCode == OpCode.NewObj) return Method.DeclaringType.GetStackType(); else return Method.ReturnType.GetStackType(); } } /// /// Gets the expected stack type for passing the this pointer in a method call. /// Returns StackType.Ref if constrainedTo is not null, /// StackType.O for reference types (this pointer passed as object reference), /// and StackType.Ref for type parameters and value types (this pointer passed as managed reference). /// /// Returns StackType.Unknown if the input type is unknown. /// internal static StackType ExpectedTypeForThisPointer(IType declaringType, IType? constrainedTo) { if (constrainedTo != null) return StackType.Ref; if (declaringType.Kind == TypeKind.TypeParameter) return StackType.Ref; switch (declaringType.IsReferenceType) { case true: return StackType.O; case false: return StackType.Ref; default: return StackType.Unknown; } } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); int firstArgument = (OpCode != OpCode.NewObj && !Method.IsStatic) ? 1 : 0; Debug.Assert(Method.Parameters.Count + firstArgument == Arguments.Count); if (firstArgument == 1) { if (!(Arguments[0].ResultType == ExpectedTypeForThisPointer(Method.DeclaringType, ConstrainedTo))) Debug.Fail($"Stack type mismatch in 'this' argument in call to {Method.Name}()"); } for (int i = 0; i < Method.Parameters.Count; ++i) { if (!(Arguments[firstArgument + i].ResultType == Method.Parameters[i].Type.GetStackType())) Debug.Fail($"Stack type mismatch in parameter {i} in call to {Method.Name}()"); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (ConstrainedTo != null) { output.Write("constrained["); ConstrainedTo.WriteTo(output); output.Write("]."); } if (IsTail) output.Write("tail."); output.Write(OpCode); output.Write(' '); Method.WriteTo(output); output.Write('('); for (int i = 0; i < Arguments.Count; i++) { if (i > 0) output.Write(", "); Arguments[i].WriteTo(output, options); } output.Write(')'); } protected internal sealed override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { CallInstruction? o = other as CallInstruction; return o != null && this.OpCode == o.OpCode && this.Method.Equals(o.Method) && this.IsTail == o.IsTail && object.Equals(this.ConstrainedTo, o.ConstrainedTo) && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } partial class Call : ILiftableInstruction { /// /// Calls can only be lifted when calling a lifted operator. /// Note that the semantics of such a lifted call depend on the type of operator: /// we follow C# semantics here. /// public bool IsLifted => Method is CSharp.Resolver.ILiftedOperator; public StackType UnderlyingResultType { get { if (Method is CSharp.Resolver.ILiftedOperator liftedOp) return liftedOp.NonLiftedReturnType.GetStackType(); else return Method.ReturnType.GetStackType(); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Comp.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public enum ComparisonKind : byte { Equality, Inequality, LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual } static class ComparisonKindExtensions { public static bool IsEqualityOrInequality(this ComparisonKind kind) { return kind == ComparisonKind.Equality || kind == ComparisonKind.Inequality; } public static ComparisonKind Negate(this ComparisonKind kind) { switch (kind) { case ComparisonKind.Equality: return ComparisonKind.Inequality; case ComparisonKind.Inequality: return ComparisonKind.Equality; case ComparisonKind.LessThan: return ComparisonKind.GreaterThanOrEqual; case ComparisonKind.LessThanOrEqual: return ComparisonKind.GreaterThan; case ComparisonKind.GreaterThan: return ComparisonKind.LessThanOrEqual; case ComparisonKind.GreaterThanOrEqual: return ComparisonKind.LessThan; default: throw new ArgumentOutOfRangeException(); } } public static BinaryOperatorType ToBinaryOperatorType(this ComparisonKind kind) { switch (kind) { case ComparisonKind.Equality: return BinaryOperatorType.Equality; case ComparisonKind.Inequality: return BinaryOperatorType.InEquality; case ComparisonKind.LessThan: return BinaryOperatorType.LessThan; case ComparisonKind.LessThanOrEqual: return BinaryOperatorType.LessThanOrEqual; case ComparisonKind.GreaterThan: return BinaryOperatorType.GreaterThan; case ComparisonKind.GreaterThanOrEqual: return BinaryOperatorType.GreaterThanOrEqual; default: throw new ArgumentOutOfRangeException(); } } public static string GetToken(this ComparisonKind kind) { return BinaryOperatorExpression.GetOperatorRole(kind.ToBinaryOperatorType()).Token; } } public enum ComparisonLiftingKind { /// /// Not a lifted comparison. /// None, /// /// C#-style lifted comparison: /// * operands that have a ResultType != this.InputType are expected to return a value of /// type Nullable{T}, where T.GetStackType() == this.InputType. /// * if both operands are null, equality comparisons evaluate to 1, all other comparisons to 0. /// * if one operand is null, inequality comparisons evaluate to 1, all other comparisons to 0. /// * if neither operand is null, the underlying comparison is performed. /// /// Note that even though C#-style lifted comparisons set IsLifted=true, /// the ResultType remains I4 as with normal comparisons. /// CSharp, /// /// SQL-style lifted comparison: works like a lifted binary numeric instruction, /// that is, if any input operand is null, the comparison evaluates to null. /// /// /// This lifting kind is currently only used for operator! on bool?. /// ThreeValuedLogic } partial class Comp : ILiftableInstruction { ComparisonKind kind; public ComparisonKind Kind { get { return kind; } set { kind = value; MakeDirty(); } } public ComparisonLiftingKind LiftingKind; /// /// Gets the stack type of the comparison inputs. /// For lifted comparisons, this is the underlying input type. /// public StackType InputType; /// /// If this is an integer comparison, specifies the sign used to interpret the integers. /// public readonly Sign Sign; public Comp(ComparisonKind kind, Sign sign, ILInstruction left, ILInstruction right) : base(OpCode.Comp, left, right) { this.kind = kind; this.LiftingKind = ComparisonLiftingKind.None; this.InputType = left.ResultType; this.Sign = sign; Debug.Assert(left.ResultType == right.ResultType); } public Comp(ComparisonKind kind, ComparisonLiftingKind lifting, StackType inputType, Sign sign, ILInstruction left, ILInstruction right) : base(OpCode.Comp, left, right) { this.kind = kind; this.LiftingKind = lifting; this.InputType = inputType; this.Sign = sign; } public override StackType ResultType => LiftingKind == ComparisonLiftingKind.ThreeValuedLogic ? StackType.O : StackType.I4; public bool IsLifted => LiftingKind != ComparisonLiftingKind.None; public StackType UnderlyingResultType => StackType.I4; internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); if (LiftingKind == ComparisonLiftingKind.None) { Debug.Assert(Left.ResultType == InputType); Debug.Assert(Right.ResultType == InputType); } else { Debug.Assert(Left.ResultType == InputType || Left.ResultType == StackType.O); Debug.Assert(Right.ResultType == InputType || Right.ResultType == StackType.O); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (options.UseLogicOperationSugar && MatchLogicNot(out var arg)) { output.Write("logic.not("); arg.WriteTo(output, options); output.Write(')'); return; } output.Write(OpCode); output.Write('.'); output.Write(InputType.ToString().ToLower()); switch (Sign) { case Sign.Signed: output.Write(".signed"); break; case Sign.Unsigned: output.Write(".unsigned"); break; } switch (LiftingKind) { case ComparisonLiftingKind.CSharp: output.Write(".lifted[C#]"); break; case ComparisonLiftingKind.ThreeValuedLogic: output.Write(".lifted[3VL]"); break; } output.Write('('); Left.WriteTo(output, options); output.Write(' '); output.Write(Kind.GetToken()); output.Write(' '); Right.WriteTo(output, options); output.Write(')'); } public static Comp LogicNot(ILInstruction arg) { return new Comp(ComparisonKind.Equality, Sign.None, arg, new LdcI4(0)); } public static Comp LogicNot(ILInstruction arg, bool isLifted) { var liftingKind = isLifted ? ComparisonLiftingKind.ThreeValuedLogic : ComparisonLiftingKind.None; return new Comp(ComparisonKind.Equality, liftingKind, StackType.I4, Sign.None, arg, new LdcI4(0)); } internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { // ExpressionBuilder translates comp.o(a op b) for op not in (==, !=) into // Unsafe.As(ref a) op Unsafe.As(ref b), which requires that a and b are variables // and not expressions. Returning false in those cases prevents inlining. // However if one of the arguments is LdNull, then we don't need the Unsafe.As trickery, and can always inline. if (kind.IsEqualityOrInequality() || this.InputType != StackType.O) { // OK, won't need Unsafe.As. return true; } if (expressionBeingMoved is LdLoc || expressionBeingMoved.MatchLdsFld(out _)) { // OK, can use variable/field name with Unsafe.As(ref x) return true; } if (Sign != Sign.Signed && (expressionBeingMoved is LdNull || Left is LdNull || Right is LdNull)) { // OK, this is the "compare with null" special case that doesn't need Unsafe.As() return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs ================================================ #nullable enable // Copyright (c) 2016 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.Diagnostics; using System.Linq.Expressions; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public enum CompoundEvalMode : byte { /// /// The compound.assign instruction will evaluate to the old value. /// This mode is used only for post-increment/decrement. /// EvaluatesToOldValue, /// /// The compound.assign instruction will evaluate to the new value. /// This mode is used for compound assignments and pre-increment/decrement. /// EvaluatesToNewValue } public enum CompoundTargetKind : byte { /// /// The target is an instruction computing an address, /// and the compound.assign will implicitly load/store from/to that address. /// Address, /// /// The Target must be a call to a property getter, /// and the compound.assign will implicitly call the corresponding property setter. /// Property, /// /// The target is a dynamic call. /// Dynamic } public abstract partial class CompoundAssignmentInstruction : ILInstruction { public readonly CompoundEvalMode EvalMode; /// /// If TargetIsProperty is true, the Target must be a call to a property getter, /// and the compound.assign will implicitly call the corresponding property setter. /// Otherwise, the Target can be any instruction that evaluates to an address, /// and the compound.assign will implicit load and store from/to that address. /// public readonly CompoundTargetKind TargetKind; public CompoundAssignmentInstruction(OpCode opCode, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) : base(opCode) { this.EvalMode = evalMode; this.Target = target; this.TargetKind = targetKind; this.Value = value; CheckValidTarget(); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); CheckValidTarget(); } [Conditional("DEBUG")] void CheckValidTarget() { switch (TargetKind) { case CompoundTargetKind.Address: Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); break; case CompoundTargetKind.Property: Debug.Assert(target.OpCode == OpCode.Call || target.OpCode == OpCode.CallVirt); var owner = ((CallInstruction)target).Method.AccessorOwner as IProperty; Debug.Assert(owner != null && owner.CanSet); break; case CompoundTargetKind.Dynamic: Debug.Assert(target.OpCode == OpCode.DynamicGetMemberInstruction || target.OpCode == OpCode.DynamicGetIndexInstruction); break; } } protected void WriteSuffix(ITextOutput output) { switch (TargetKind) { case CompoundTargetKind.Address: output.Write(".address"); break; case CompoundTargetKind.Property: output.Write(".property"); break; } switch (EvalMode) { case CompoundEvalMode.EvaluatesToNewValue: output.Write(".new"); break; case CompoundEvalMode.EvaluatesToOldValue: output.Write(".old"); break; } } } public partial class NumericCompoundAssign : CompoundAssignmentInstruction, ILiftableInstruction { /// /// Gets whether the instruction checks for overflow. /// public readonly bool CheckForOverflow; /// /// For integer operations that depend on the sign, specifies whether the operation /// is signed or unsigned. /// For instructions that produce the same result for either sign, returns Sign.None. /// public readonly Sign Sign; public readonly StackType LeftInputType; public readonly StackType RightInputType; public StackType UnderlyingResultType { get; } /// /// The operator used by this assignment operator instruction. /// public readonly BinaryNumericOperator Operator; public bool IsLifted { get; } public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode) : base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value) { Debug.Assert(IsBinaryCompatibleWithType(binary, type, null)); this.CheckForOverflow = binary.CheckForOverflow; this.Sign = binary.Sign; this.LeftInputType = binary.LeftInputType; this.RightInputType = binary.RightInputType; this.UnderlyingResultType = binary.UnderlyingResultType; this.Operator = binary.Operator; this.IsLifted = binary.IsLifted; this.type = type; this.AddILRange(binary); Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); } /// /// Gets whether the specific binary instruction is compatible with a compound operation on the specified type. /// internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type, DecompilerSettings? settings) { if (binary.IsLifted) { if (!NullableType.IsNullable(type)) return false; type = NullableType.GetUnderlyingType(type); } if (type.Kind == TypeKind.Unknown) { return false; // avoid introducing a potentially-incorrect compound assignment } else if (type.Kind == TypeKind.Enum) { switch (binary.Operator) { case BinaryNumericOperator.Add: case BinaryNumericOperator.Sub: case BinaryNumericOperator.BitAnd: case BinaryNumericOperator.BitOr: case BinaryNumericOperator.BitXor: break; // OK default: return false; // operator not supported on enum types } } else if (type.Kind == TypeKind.Pointer) { switch (binary.Operator) { case BinaryNumericOperator.Add: case BinaryNumericOperator.Sub: // ensure that the byte offset is a multiple of the pointer size return PointerArithmeticOffset.Detect( binary.Right, ((PointerType)type).ElementType, checkForOverflow: binary.CheckForOverflow ) != null; default: return false; // operator not supported on pointer types } } else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt) { // If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr): // "target.intptr *= 2;" is compiler error, but // "target.intptr *= (nint)2;" works if (settings != null && !settings.NativeIntegers) { // But if native integers are not available, we cannot use compound assignment. return false; } // The trick with casting the RHS to n(u)int doesn't work for shifts: switch (binary.Operator) { case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftRight: return false; } } if (binary.Sign != Sign.None) { bool signMismatchAllowed = (binary.Sign == Sign.Unsigned && binary.Operator == BinaryNumericOperator.ShiftRight && (settings == null || settings.UnsignedRightShift)); if (type.IsCSharpSmallIntegerType()) { // C# will use numeric promotion to int, binary op must be signed if (binary.Sign != Sign.Signed && !signMismatchAllowed) return false; } else { // C# will use sign from type; except for right shift with C# 11 >>> operator. if (type.GetSign() != binary.Sign && !signMismatchAllowed) return false; } } // Can't transform if the RHS value would be need to be truncated for the LHS type. if (Transforms.TransformAssignment.IsImplicitTruncation(binary.Right, type, null, binary.IsLifted)) return false; return true; } protected override InstructionFlags ComputeFlags() { var flags = Target.Flags | Value.Flags | InstructionFlags.SideEffect; if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) flags |= InstructionFlags.MayThrow; return flags; } public override InstructionFlags DirectFlags { get { var flags = InstructionFlags.SideEffect; if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) flags |= InstructionFlags.MayThrow; return flags; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator)); if (CheckForOverflow) { output.Write(".ovf"); } if (Sign == Sign.Unsigned) { output.Write(".unsigned"); } else if (Sign == Sign.Signed) { output.Write(".signed"); } output.Write('.'); output.Write(UnderlyingResultType.ToString().ToLowerInvariant()); if (IsLifted) { output.Write(".lifted"); } base.WriteSuffix(output); output.Write('('); Target.WriteTo(output, options); output.Write(", "); Value.WriteTo(output, options); output.Write(')'); } } public partial class UserDefinedCompoundAssign : CompoundAssignmentInstruction { public readonly IMethod Method; public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments public UserDefinedCompoundAssign(IMethod method, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) : base(OpCode.UserDefinedCompoundAssign, evalMode, target, targetKind, value) { this.Method = method; Debug.Assert(Method.IsOperator || IsStringConcat(method)); Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || IsIncrementOrDecrement(method)); } public static bool IsIncrementOrDecrement(IMethod method, DecompilerSettings? settings = null) { if (!(method.IsOperator && method.IsStatic)) return false; if (method.Name is "op_Increment" or "op_Decrement") return true; if (method.Name is "op_CheckedIncrement" or "op_CheckedDecrement") return settings?.CheckedOperators ?? true; return false; } public static bool IsStringConcat(IMethod method) { return method.Name == "Concat" && method.IsStatic && method.DeclaringType.IsKnownType(KnownTypeCode.String); } public override StackType ResultType => Method.ReturnType.GetStackType(); public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); base.WriteSuffix(output); output.Write(' '); Method.WriteTo(output); output.Write('('); this.Target.WriteTo(output, options); output.Write(", "); this.Value.WriteTo(output, options); output.Write(')'); } } public partial class DynamicCompoundAssign : CompoundAssignmentInstruction { public ExpressionType Operation { get; } public CSharpArgumentInfo TargetArgumentInfo { get; } public CSharpArgumentInfo ValueArgumentInfo { get; } public CSharpBinderFlags BinderFlags { get; } public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, ILInstruction target, CSharpArgumentInfo targetArgumentInfo, ILInstruction value, CSharpArgumentInfo valueArgumentInfo, CompoundTargetKind targetKind = CompoundTargetKind.Dynamic) : base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value) { if (!IsExpressionTypeSupported(op)) throw new ArgumentOutOfRangeException(nameof(op)); this.BinderFlags = binderFlags; this.Operation = op; this.TargetArgumentInfo = targetArgumentInfo; this.ValueArgumentInfo = valueArgumentInfo; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write("." + Operation.ToString().ToLower()); DynamicInstruction.WriteBinderFlags(BinderFlags, output, options); base.WriteSuffix(output); output.Write(' '); DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); } internal static bool IsExpressionTypeSupported(ExpressionType type) { return type == ExpressionType.AddAssign || type == ExpressionType.AddAssignChecked || type == ExpressionType.AndAssign || type == ExpressionType.DivideAssign || type == ExpressionType.ExclusiveOrAssign || type == ExpressionType.LeftShiftAssign || type == ExpressionType.ModuloAssign || type == ExpressionType.MultiplyAssign || type == ExpressionType.MultiplyAssignChecked || type == ExpressionType.OrAssign || type == ExpressionType.PostDecrementAssign || type == ExpressionType.PostIncrementAssign || type == ExpressionType.PreDecrementAssign || type == ExpressionType.PreIncrementAssign || type == ExpressionType.RightShiftAssign || type == ExpressionType.SubtractAssign || type == ExpressionType.SubtractAssignChecked; } static CompoundEvalMode CompoundEvalModeFromOperation(ExpressionType op) { switch (op) { case ExpressionType.PostIncrementAssign: case ExpressionType.PostDecrementAssign: return CompoundEvalMode.EvaluatesToOldValue; default: return CompoundEvalMode.EvaluatesToNewValue; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Conv.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { /// /// Semantic meaning of a Conv instruction. /// public enum ConversionKind : byte { /// /// Invalid conversion. /// Invalid, /// /// Conversion between two types of same size. /// Can be used to change the sign of integer types, which may involve overflow-checking. /// Nop, /// /// Integer-to-float conversion. /// Uses InputSign to decide whether the integer should be treated as signed or unsigned. /// IntToFloat, /// /// Float-to-integer conversion. /// Truncates toward zero; may perform overflow-checking. /// FloatToInt, /// /// Converts from the current precision available on the evaluation stack to the precision specified by /// the TargetType. /// Uses "round-to-nearest" mode if the precision is reduced. /// FloatPrecisionChange, /// /// Conversion of integer type to larger signed integer type. /// May involve overflow checking (when converting from U4 to I on 32-bit). /// SignExtend, /// /// Conversion of integer type to larger unsigned integer type. /// May involve overflow checking (when converting from a signed type). /// ZeroExtend, /// /// Conversion to smaller integer type. /// /// May involve overflow checking. /// /// /// If the target type is smaller than the minimum stack width of 4 bytes, /// then the result of the conversion is zero extended (if the target type is unsigned) /// or sign-extended (if the target type is signed). /// Truncate, /// /// Used to convert managed references/objects to unmanaged pointers. /// StopGCTracking, /// /// Used to convert unmanaged pointers to managed references. /// StartGCTracking, /// /// Converts from an object reference (O) to an interior pointer (Ref) pointing to the start of the object. /// /// /// C++/CLI emits "ldarg.1; stloc.0" where arg1 is a string and loc0 is "ref byte" (e.g. as part of the PtrToStringChars codegen); /// we represent this type conversion explicitly in the ILAst. /// ObjectInterior } partial class Conv : UnaryInstruction, ILiftableInstruction { /// /// Gets the conversion kind. /// public readonly ConversionKind Kind; /// /// Gets whether the conversion performs overflow-checking. /// public readonly bool CheckForOverflow; /// /// Gets whether this conversion is a lifted nullable conversion. /// /// /// A lifted conversion expects its argument to be a value of type Nullable{T}, where /// T.GetStackType() == conv.InputType. /// If the value is non-null: /// * it is sign/zero-extended to InputType (based on T's sign) /// * the underlying conversion is performed /// * the result is wrapped in a Nullable{TargetType}. /// If the value is null, the conversion evaluates to default(TargetType?). /// (this result type is underspecified, since there may be multiple C# types for the TargetType) /// public bool IsLifted { get; } /// /// Gets the stack type of the input type. /// /// /// For non-lifted conversions, this is equal to Argument.ResultType. /// For lifted conversions, corresponds to the underlying type of the argument. /// public readonly StackType InputType; /// /// Gets the sign of the input type. /// /// For conversions to integer types, the input Sign is set iff overflow-checking is enabled. /// For conversions to floating-point types, the input sign is always set. /// /// /// The input sign does not have any effect on whether the conversion zero-extends or sign-extends; /// that is purely determined by the TargetType. /// public readonly Sign InputSign; /// /// The target type of the conversion. /// /// /// For lifted conversions, corresponds to the underlying target type. /// /// Target type == PrimitiveType.None can happen for implicit conversions to O in invalid IL. /// public readonly PrimitiveType TargetType; public Conv(ILInstruction argument, PrimitiveType targetType, bool checkForOverflow, Sign inputSign) : this(argument, argument.ResultType, inputSign, targetType, checkForOverflow) { } public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false) : base(OpCode.Conv, argument) { bool needsSign = checkForOverflow || (!inputType.IsFloatType() && targetType.IsFloatType()); Debug.Assert(!(needsSign && inputSign == Sign.None)); this.InputSign = needsSign ? inputSign : Sign.None; this.InputType = inputType; this.TargetType = targetType; this.CheckForOverflow = checkForOverflow; this.Kind = GetConversionKind(targetType, this.InputType, this.InputSign); // Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references this.IsLifted = isLifted; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); // Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references Debug.Assert(Argument.ResultType == (IsLifted ? StackType.O : InputType)); Debug.Assert(!(IsLifted && Kind == ConversionKind.StopGCTracking)); } /// /// Implements Ecma-335 Table 8: Conversion Operators. /// static ConversionKind GetConversionKind(PrimitiveType targetType, StackType inputType, Sign inputSign) { switch (targetType) { case PrimitiveType.I1: case PrimitiveType.I2: case PrimitiveType.U1: case PrimitiveType.U2: switch (inputType) { case StackType.I4: case StackType.I8: case StackType.I: return ConversionKind.Truncate; case StackType.F4: case StackType.F8: return ConversionKind.FloatToInt; default: return ConversionKind.Invalid; } case PrimitiveType.I4: case PrimitiveType.U4: switch (inputType) { case StackType.I4: return ConversionKind.Nop; case StackType.I: case StackType.I8: return ConversionKind.Truncate; case StackType.F4: case StackType.F8: return ConversionKind.FloatToInt; default: return ConversionKind.Invalid; } case PrimitiveType.I8: case PrimitiveType.U8: switch (inputType) { case StackType.I4: case StackType.I: if (inputSign == Sign.None) return targetType == PrimitiveType.I8 ? ConversionKind.SignExtend : ConversionKind.ZeroExtend; else return inputSign == Sign.Signed ? ConversionKind.SignExtend : ConversionKind.ZeroExtend; case StackType.I8: return ConversionKind.Nop; case StackType.F4: case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: case StackType.O: return ConversionKind.StopGCTracking; default: return ConversionKind.Invalid; } case PrimitiveType.I: case PrimitiveType.U: switch (inputType) { case StackType.I4: if (inputSign == Sign.None) return targetType == PrimitiveType.I ? ConversionKind.SignExtend : ConversionKind.ZeroExtend; else return inputSign == Sign.Signed ? ConversionKind.SignExtend : ConversionKind.ZeroExtend; case StackType.I: return ConversionKind.Nop; case StackType.I8: return ConversionKind.Truncate; case StackType.F4: case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: case StackType.O: return ConversionKind.StopGCTracking; default: return ConversionKind.Invalid; } case PrimitiveType.R4: switch (inputType) { case StackType.I4: case StackType.I: case StackType.I8: return ConversionKind.IntToFloat; case StackType.F4: return ConversionKind.Nop; case StackType.F8: return ConversionKind.FloatPrecisionChange; default: return ConversionKind.Invalid; } case PrimitiveType.R: case PrimitiveType.R8: switch (inputType) { case StackType.I4: case StackType.I: case StackType.I8: return ConversionKind.IntToFloat; case StackType.F4: return ConversionKind.FloatPrecisionChange; case StackType.F8: return ConversionKind.Nop; default: return ConversionKind.Invalid; } case PrimitiveType.Ref: // There's no "conv.ref" in IL, but IL allows these conversions implicitly, // whereas we represent them explicitly in the ILAst. switch (inputType) { case StackType.I4: case StackType.I: case StackType.I8: return ConversionKind.StartGCTracking; case StackType.O: return ConversionKind.ObjectInterior; default: return ConversionKind.Invalid; } default: return ConversionKind.Invalid; } } public override StackType ResultType { get => IsLifted ? StackType.O : TargetType.GetStackType(); } public StackType UnderlyingResultType { get => TargetType.GetStackType(); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (CheckForOverflow) { output.Write(".ovf"); } if (InputSign == Sign.Unsigned) { output.Write(".unsigned"); } else if (InputSign == Sign.Signed) { output.Write(".signed"); } if (IsLifted) { output.Write(".lifted"); } output.Write(' '); output.Write(InputType); output.Write("->"); output.Write(TargetType); output.Write(' '); switch (Kind) { case ConversionKind.SignExtend: output.Write(""); break; case ConversionKind.ZeroExtend: output.Write(""); break; case ConversionKind.Invalid: output.Write(""); break; } output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } protected override InstructionFlags ComputeFlags() { var flags = base.ComputeFlags(); if (CheckForOverflow) flags |= InstructionFlags.MayThrow; return flags; } public override ILInstruction UnwrapConv(ConversionKind kind) { if (this.Kind == kind && !IsLifted) return Argument.UnwrapConv(kind); else return this; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs ================================================ // Copyright (c) 2020 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class DeconstructInstruction { public static readonly SlotInfo InitSlot = new SlotInfo("Init", canInlineInto: true, isCollection: true); public static readonly SlotInfo PatternSlot = new SlotInfo("Pattern", canInlineInto: true); public static readonly SlotInfo ConversionsSlot = new SlotInfo("Conversions"); public static readonly SlotInfo AssignmentsSlot = new SlotInfo("Assignments"); public DeconstructInstruction() : base(OpCode.DeconstructInstruction) { this.Init = new InstructionCollection(this, 0); } public readonly InstructionCollection Init; MatchInstruction pattern; public MatchInstruction Pattern { get { return this.pattern; } set { ValidateChild(value); SetChildInstruction(ref this.pattern, value, Init.Count); } } Block conversions; public Block Conversions { get { return this.conversions; } set { ValidateChild(value); SetChildInstruction(ref this.conversions, value, Init.Count + 1); } } Block assignments; public Block Assignments { get { return this.assignments; } set { ValidateChild(value); SetChildInstruction(ref this.assignments, value, Init.Count + 2); } } protected sealed override int GetChildCount() { return Init.Count + 3; } protected sealed override ILInstruction GetChild(int index) { switch (index - Init.Count) { case 0: return this.pattern; case 1: return this.conversions; case 2: return this.assignments; default: return this.Init[index]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index - Init.Count) { case 0: this.Pattern = (MatchInstruction)value; break; case 1: this.Conversions = (Block)value; break; case 2: this.Assignments = (Block)value; break; default: this.Init[index] = (StLoc)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index - Init.Count) { case 0: return PatternSlot; case 1: return ConversionsSlot; case 2: return AssignmentsSlot; default: return InitSlot; } } public sealed override ILInstruction Clone() { var clone = new DeconstructInstruction(); clone.Init.AddRange(this.Init.Select(inst => (StLoc)inst.Clone())); clone.Pattern = (MatchInstruction)this.pattern.Clone(); clone.Conversions = (Block)this.conversions.Clone(); clone.Assignments = (Block)this.assignments.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { var flags = InstructionFlags.None; foreach (var inst in Init) { flags |= inst.Flags; } flags |= pattern.Flags | conversions.Flags | assignments.Flags; return flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } protected internal override void InstructionCollectionUpdateComplete() { base.InstructionCollectionUpdateComplete(); if (pattern.Parent == this) pattern.ChildIndex = Init.Count; if (conversions.Parent == this) conversions.ChildIndex = Init.Count + 1; if (assignments.Parent == this) assignments.ChildIndex = Init.Count + 2; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("deconstruct "); output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); output.WriteLine("init:"); output.Indent(); foreach (var inst in this.Init) { inst.WriteTo(output, options); output.WriteLine(); } output.Unindent(); output.WriteLine("pattern:"); output.Indent(); pattern.WriteTo(output, options); output.Unindent(); output.WriteLine(); output.Write("conversions: "); conversions.WriteTo(output, options); output.WriteLine(); output.Write("assignments: "); assignments.WriteTo(output, options); output.Unindent(); output.WriteLine(); output.Write('}'); output.MarkFoldEnd(); } internal static bool IsConversionStLoc(ILInstruction inst, out ILVariable variable, out ILVariable inputVariable) { inputVariable = null; if (!inst.MatchStLoc(out variable, out var value)) return false; ILInstruction input; switch (value) { case Conv conv: input = conv.Argument; break; //case Call { Method: { IsOperator: true, Name: "op_Implicit" }, Arguments: { Count: 1 } } call: // input = call.Arguments[0]; // break; default: return false; } return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable); } internal static bool IsAssignment(ILInstruction inst, ICompilation typeSystem, out IType expectedType, out ILInstruction value) { expectedType = null; value = null; switch (inst) { case CallInstruction call: if (call.Method.AccessorKind != System.Reflection.MethodSemanticsAttributes.Setter) return false; for (int i = 0; i < call.Arguments.Count - 1; i++) { ILInstruction arg = call.Arguments[i]; if (arg.Flags == InstructionFlags.None) { // OK - we accept integer literals, etc. } else if (arg.MatchLdLoc(out var v)) { } else { return false; } } expectedType = call.Method.Parameters.Last().Type; value = call.Arguments.Last(); return true; case StLoc stloc: expectedType = stloc.Variable.Type; value = stloc.Value; return true; case StObj stobj: var target = stobj.Target; while (target.MatchLdFlda(out var nestedTarget, out _)) target = nestedTarget; if (target.Flags == InstructionFlags.None) { // OK - we accept integer literals, etc. } else if (target.MatchLdLoc(out var v)) { } else { return false; } if (stobj.Target.InferType(typeSystem) is ByReferenceType brt) expectedType = brt.ElementType; else expectedType = SpecialType.UnknownType; value = stobj.Value; return true; default: return false; } } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); var patternVariables = new HashSet(); var conversionVariables = new HashSet(); foreach (StLoc init in this.Init) { Debug.Assert(init.Variable.IsSingleDefinition && init.Variable.LoadCount == 1); Debug.Assert(init.Variable.LoadInstructions[0].IsDescendantOf(assignments)); } ValidatePattern(pattern); foreach (var inst in this.conversions.Instructions) { if (!IsConversionStLoc(inst, out var variable, out var inputVariable)) Debug.Fail("inst is not a conversion stloc!"); Debug.Assert(variable.IsSingleDefinition && variable.LoadCount == 1); Debug.Assert(variable.LoadInstructions[0].IsDescendantOf(assignments)); Debug.Assert(patternVariables.Contains(inputVariable)); conversionVariables.Add(variable); } Debug.Assert(this.conversions.FinalInstruction is Nop); foreach (var inst in assignments.Instructions) { if (!(IsAssignment(inst, typeSystem: null, out _, out var value) && value.MatchLdLoc(out var inputVariable))) throw new InvalidOperationException("inst is not an assignment!"); Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable)); } Debug.Assert(this.assignments.FinalInstruction is Nop); void ValidatePattern(MatchInstruction inst) { Debug.Assert(inst.IsDeconstructCall || inst.IsDeconstructTuple); Debug.Assert(!inst.CheckNotNull && !inst.CheckType); Debug.Assert(!inst.HasDesignator); foreach (var subPattern in inst.SubPatterns.Cast()) { if (subPattern.IsVar) { Debug.Assert(subPattern.Variable.IsSingleDefinition && subPattern.Variable.LoadCount <= 1); if (subPattern.Variable.LoadCount == 1) Debug.Assert(subPattern.Variable.LoadInstructions[0].IsDescendantOf(this)); patternVariables.Add(subPattern.Variable); } else { ValidatePattern(subPattern); } } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs ================================================ #nullable enable // Copyright (c) 2020 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class DeconstructResultInstruction { public int Index { get; } public override StackType ResultType { get; } public DeconstructResultInstruction(int index, StackType resultType, ILInstruction argument) : base(OpCode.DeconstructResultInstruction, argument) { Debug.Assert(index >= 0); Index = index; ResultType = resultType; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); output.Write(Index.ToString()); output.Write('('); this.Argument.WriteTo(output, options); output.Write(')'); } MatchInstruction? FindMatch() { for (ILInstruction? inst = this; inst != null; inst = inst.Parent) { if (inst.Parent is MatchInstruction match && inst != match.TestedOperand) return match; } return null; } void AdditionalInvariants() { var matchInst = FindMatch(); DebugAssert(matchInst != null && (matchInst.IsDeconstructCall || matchInst.IsDeconstructTuple)); DebugAssert(Argument.MatchLdLoc(matchInst.Variable)); var outParamType = matchInst.GetDeconstructResultType(this.Index); DebugAssert(outParamType.GetStackType() == ResultType); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/DefaultValue.cs ================================================ #nullable enable // Copyright (c) 2017 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; namespace ICSharpCode.Decompiler.IL { partial class DefaultValue { /// /// Gets whether the IL stack was empty at the point of this instruction. /// (not counting the argument of the instruction itself) /// public bool ILStackWasEmpty; } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs ================================================ #nullable enable // 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; 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) { WriteILRange(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) { this.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 TypeArguments { get; } public IReadOnlyList 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.Array; ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(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) { WriteILRange(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) { WriteILRange(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 ArgumentInfo { get; } public DynamicGetIndexInstruction(CSharpBinderFlags binderFlags, IType? context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicGetIndexInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(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 ArgumentInfo { get; } public DynamicSetIndexInstruction(CSharpBinderFlags binderFlags, IType? context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicSetIndexInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(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 { readonly IType? resultType; public IReadOnlyList ArgumentInfo { get; } public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType? type, IType? context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicInvokeConstructorInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); this.resultType = type; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); WriteBinderFlags(output, options); output.Write(' '); resultType?.WriteTo(output); output.Write(".ctor"); WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } public override StackType ResultType => resultType?.GetStackType() ?? StackType.Unknown; 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) { WriteILRange(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 DynamicLogicOperatorInstruction { public CSharpArgumentInfo LeftArgumentInfo { get; } public CSharpArgumentInfo RightArgumentInfo { get; } public ExpressionType Operation { get; } public DynamicLogicOperatorInstruction(CSharpBinderFlags binderFlags, ExpressionType operation, IType? context, CSharpArgumentInfo leftArgumentInfo, ILInstruction left, CSharpArgumentInfo rightArgumentInfo, ILInstruction right) : base(OpCode.DynamicLogicOperatorInstruction, binderFlags, context) { Operation = operation; LeftArgumentInfo = leftArgumentInfo; Left = left; RightArgumentInfo = rightArgumentInfo; Right = right; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(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; protected override InstructionFlags ComputeFlags() { return DirectFlags | Left.Flags | SemanticHelper.CombineBranches(Right.Flags, InstructionFlags.None); } public override InstructionFlags DirectFlags => InstructionFlags.MayThrow | InstructionFlags.SideEffect | InstructionFlags.ControlFlow; 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) { WriteILRange(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 ArgumentInfo { get; } public DynamicInvokeInstruction(CSharpBinderFlags binderFlags, IType? context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicInvokeInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(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) { WriteILRange(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); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/ExpressionTreeCast.cs ================================================ #nullable enable using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class ExpressionTreeCast { public bool IsChecked { get; set; } public ExpressionTreeCast(IType type, ILInstruction argument, bool isChecked) : base(OpCode.ExpressionTreeCast, argument) { this.type = type; this.IsChecked = isChecked; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (IsChecked) output.Write(".checked"); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { partial class ILFunction { /// /// Gets the method definition from metadata. /// May be null for functions that were not constructed from metadata, /// e.g., expression trees. /// public readonly IMethod? Method; /// /// Gets the generic context of this function. /// public readonly GenericContext GenericContext; /// /// Gets the name of this function, usually this returns the name from metadata. /// /// For local functions: /// This is the name that is used to declare and use the function. /// It may not conflict with the names of local variables of ancestor functions /// and may be overwritten by the AssignVariableNames step. /// /// For top-level functions, delegates and expressions trees modifying this usually /// has no effect, as the name should not be used in the final AST construction. /// /// public string? Name; /// /// Size of the IL code in this function. /// Note: after async/await transform, this is the code size of the MoveNext function. /// public int CodeSize; /// /// List of ILVariables used in this function. /// public readonly ILVariableCollection Variables; /// /// Gets /// public int LocalVariableSignatureLength; /// /// Gets the scope in which the local function is declared. /// Returns null, if this is not a local function. /// public BlockContainer? DeclarationScope { get; internal set; } /// /// Gets the set of captured variables by this ILFunction. /// /// This is populated by the step. public HashSet CapturedVariables { get; } = new HashSet(); /// /// List of warnings of ILReader. /// public List Warnings { get; } = new List(); /// /// Gets whether this function is a decompiled iterator (is using yield). /// This flag gets set by the YieldReturnDecompiler. /// /// If set, the 'return' instruction has the semantics of 'yield break;' /// instead of a normal return. /// public bool IsIterator; /// /// Gets whether the YieldReturnDecompiler determined that the Mono C# compiler was used to compile this function. /// 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. /// public bool IsAsync => AsyncReturnType != null; /// /// Return element type -- if the async method returns Task{T}, this field stores T. /// If the async method returns Task or void, this field stores void. /// public IType? AsyncReturnType; /// /// If this function is an iterator/async, this field stores the compiler-generated MoveNext() method. /// public IMethod? MoveNextMethod; /// /// If this function is a local function, this field stores the reduced version of the function. /// internal TypeSystem.Implementation.LocalFunctionMethod? ReducedMethod; public DebugInfo.AsyncDebugInfo AsyncDebugInfo; int ctorCallStart = int.MinValue; /// /// Returns the IL offset of the constructor call, -1 if this is not a constructor or no chained constructor call was found. /// internal int ChainedConstructorCallILOffset { get { if (ctorCallStart == int.MinValue) { if (this.Method == null || !this.Method.IsConstructor || this.Method.IsStatic) { ctorCallStart = -1; } else { ctorCallStart = this.Descendants.FirstOrDefault(d => d is CallInstruction call && !(call is NewObj) && call.Method.IsConstructor && call.Method.DeclaringType.IsReferenceType == true && call.Parent is Block)?.StartILOffset ?? -1; } } return ctorCallStart; } } /// /// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T. /// T is the delegate type that matches the signature of this method. /// Otherwise this must be null. /// public IType? DelegateType; ILFunctionKind kind; /// /// Gets which kind of function this is. /// public ILFunctionKind Kind { get => kind; internal set { if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction) throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed."); kind = value; } } /// /// Return type of this function. /// public readonly IType ReturnType; /// /// List of parameters of this function. /// public readonly IReadOnlyList Parameters; /// /// List of candidate locations for sequence points. Includes any offset /// where the stack is empty, nop instructions, and the instruction following /// a call instruction /// public List? SequencePointCandidates { get; set; } /// /// Constructs a new ILFunction from the given metadata and with the given ILAst body. /// /// /// Use to create ILAst. /// public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; this.Name = method.Name; this.CodeSize = codeSize; this.GenericContext = genericContext; this.Body = body; this.ReturnType = method.ReturnType; this.Parameters = method.Parameters; this.Variables = new ILVariableCollection(this); this.LocalFunctions = new InstructionCollection(this, 1); this.kind = kind; } /// /// This constructor is only to be used by the TransformExpressionTrees step. /// internal ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.GenericContext = genericContext; this.Body = body; this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType)); this.Parameters = parameters ?? throw new ArgumentNullException(nameof(parameters)); this.Variables = new ILVariableCollection(this); this.LocalFunctions = new InstructionCollection(this, 1); this.kind = kind; } internal override void CheckInvariant(ILPhase phase) { switch (kind) { case ILFunctionKind.TopLevelFunction: Debug.Assert(Parent == null); Debug.Assert(DelegateType == null); Debug.Assert(DeclarationScope == null); Debug.Assert(Method != null); break; case ILFunctionKind.Delegate: Debug.Assert(DelegateType != null); Debug.Assert(DeclarationScope == null); Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1)); break; case ILFunctionKind.ExpressionTree: Debug.Assert(DelegateType != null); Debug.Assert(DeclarationScope == null); Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); break; case ILFunctionKind.LocalFunction: Debug.Assert(Parent is ILFunction && SlotInfo == ILFunction.LocalFunctionsSlot); Debug.Assert(DeclarationScope != null); Debug.Assert(DelegateType == null); Debug.Assert(Method != null); break; } for (int i = 0; i < Variables.Count; i++) { Debug.Assert(Variables[i].Function == this); Debug.Assert(Variables[i].IndexInFunction == i); Variables[i].CheckInvariant(); } base.CheckInvariant(phase); } void CloneVariables() { throw new NotSupportedException("ILFunction.CloneVariables is currently not supported!"); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (Method != null) { output.Write(' '); Method.WriteTo(output); } switch (kind) { case ILFunctionKind.ExpressionTree: output.Write(".ET"); break; case ILFunctionKind.LocalFunction: output.Write(".local"); break; } if (DelegateType != null) { output.Write("["); DelegateType.WriteTo(output); output.Write("]"); } output.WriteLine(" {"); output.Indent(); if (IsAsync) { output.WriteLine(".async"); } if (IsIterator) { output.WriteLine(".iterator"); } if (DeclarationScope != null) { output.Write("declared as " + Name + " in "); output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope); output.WriteLine(); } output.MarkFoldStart(Variables.Count + " variable(s)", true); foreach (var variable in Variables) { variable.WriteDefinitionTo(output); output.WriteLine(); } output.MarkFoldEnd(); output.WriteLine(); foreach (string warning in Warnings) { output.WriteLine("//" + warning); } body.WriteTo(output, options); output.WriteLine(); foreach (var localFunction in LocalFunctions) { output.WriteLine(); localFunction.WriteTo(output, options); } if (options.ShowILRanges) { var unusedILRanges = FindUnusedILRanges(); if (!unusedILRanges.IsEmpty) { output.Write("// Unused IL Ranges: "); output.Write(string.Join(", ", unusedILRanges.Intervals.Select( range => $"[{range.Start:x4}..{range.InclusiveEnd:x4}]"))); output.WriteLine(); } } output.Unindent(); output.WriteLine("}"); } LongSet FindUnusedILRanges() { var usedILRanges = new List(); MarkUsedILRanges(body); return new LongSet(new LongInterval(0, CodeSize)).ExceptWith(new LongSet(usedILRanges)); void MarkUsedILRanges(ILInstruction inst) { if (CSharp.SequencePointBuilder.HasUsableILRange(inst)) { usedILRanges.Add(new LongInterval(inst.StartILOffset, inst.EndILOffset)); } if (!(inst is ILFunction)) { foreach (var child in inst.Children) { MarkUsedILRanges(child); } } } } protected override InstructionFlags ComputeFlags() { // Creating a lambda may throw OutOfMemoryException // We intentionally don't propagate any flags from the lambda body! return InstructionFlags.MayThrow | InstructionFlags.ControlFlow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.ControlFlow; } } internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { // With expression trees, we occasionally need to inline constants into an existing expression tree. // Only allow this for completely pure constants; a MayReadLocals effect would already be problematic // because we're essentially delaying evaluation of the expression until the ILFunction is called. Debug.Assert(childIndex == 0); return kind == ILFunctionKind.ExpressionTree && expressionBeingMoved.Flags == InstructionFlags.None; } /// /// Apply a list of transforms to this function. /// public void RunTransforms(IEnumerable transforms, ILTransformContext context) { this.CheckInvariant(ILPhase.Normal); foreach (var transform in transforms) { context.CancellationToken.ThrowIfCancellationRequested(); if (transform is BlockILTransform blockTransform) { context.StepStartGroup(blockTransform.ToString()); } else { context.StepStartGroup(transform.GetType().Name); } transform.Run(this, context); this.CheckInvariant(ILPhase.Normal); context.StepEndGroup(keepIfEmpty: true); } } int helperVariableCount; public ILVariable RegisterVariable(VariableKind kind, IType type, string? name = null) { var variable = new ILVariable(kind, type); if (string.IsNullOrWhiteSpace(name)) { name = "I_" + (helperVariableCount++); variable.HasGeneratedName = true; } variable.Name = name; Variables.Add(variable); return variable; } /// /// Recombine split variables by replacing all occurrences of variable2 with variable1. /// internal void RecombineVariables(ILVariable variable1, ILVariable variable2) { if (variable1 == variable2) return; Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2)); foreach (var ldloc in variable2.LoadInstructions.ToArray()) { ldloc.Variable = variable1; } foreach (var store in variable2.StoreInstructions.ToArray()) { store.Variable = variable1; } foreach (var ldloca in variable2.AddressInstructions.ToArray()) { ldloca.Variable = variable1; } bool ok = Variables.Remove(variable2); Debug.Assert(ok); } } public enum ILFunctionKind { /// /// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator. /// TopLevelFunction, /// /// ILFunction is a delegate or lambda expression. /// /// /// This kind is introduced by the DelegateConstruction and TransformExpressionTrees steps in the decompiler pipeline. /// Delegate, /// /// ILFunction is an expression tree lambda. /// /// /// This kind is introduced by the TransformExpressionTrees step in the decompiler pipeline. /// ExpressionTree, /// /// ILFunction is a C# 7.0 local function. /// /// /// This kind is introduced by the LocalFunctionDecompiler step in the decompiler pipeline. /// LocalFunction } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using ICSharpCode.Decompiler.IL.Patterns; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { internal enum ILPhase { /// /// Reading the individual instructions. /// * Variables don't have scopes yet as the ILFunction is not created yet. /// * Branches point to IL offsets, not blocks. /// InILReader, /// /// The usual invariants are established. /// Normal, /// /// Special phase within the async-await decompiler, where a few selected invariants /// are temporarily suspended. (see Leave.CheckInvariant) /// InAsyncAwait } /// /// Represents a decoded IL instruction /// public abstract partial class ILInstruction { public readonly OpCode OpCode; protected ILInstruction(OpCode opCode) { this.OpCode = opCode; } protected void ValidateChild(ILInstruction? inst) { if (inst == null) throw new ArgumentNullException(nameof(inst)); Debug.Assert(!this.IsDescendantOf(inst), "ILAst must form a tree"); // If a call to ReplaceWith() triggers the "ILAst must form a tree" assertion, // make sure to read the remarks on the ReplaceWith() method. } internal static void DebugAssert([DoesNotReturnIf(false)] bool b) { Debug.Assert(b); } internal static void DebugAssert([DoesNotReturnIf(false)] bool b, string msg) { Debug.Assert(b, msg); } [Conditional("DEBUG")] internal virtual void CheckInvariant(ILPhase phase) { foreach (var child in Children) { Debug.Assert(child.Parent == this); Debug.Assert(this.GetChild(child.ChildIndex) == child); // if child flags are invalid, parent flags must be too // exception: nested ILFunctions (lambdas) Debug.Assert(this is ILFunction || child.flags != invalidFlags || this.flags == invalidFlags); Debug.Assert(child.IsConnected == this.IsConnected); child.CheckInvariant(phase); } Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags"); } /// /// Gets whether this node is a descendant of . /// Also returns true if this==. /// /// /// This method uses the Parent property, so it may produce surprising results /// when called on orphaned nodes or with a possibleAncestor that contains stale positions /// (see remarks on Parent property). /// public bool IsDescendantOf(ILInstruction possibleAncestor) { for (ILInstruction? ancestor = this; ancestor != null; ancestor = ancestor.Parent) { if (ancestor == possibleAncestor) return true; } return false; } public ILInstruction? GetCommonParent(ILInstruction other) { if (other == null) throw new ArgumentNullException(nameof(other)); ILInstruction? a = this; ILInstruction? b = other; int levelA = a.CountAncestors(); int levelB = b.CountAncestors(); while (levelA > levelB) { a = a!.Parent; levelA--; } while (levelB > levelA) { b = b!.Parent; levelB--; } while (a != b) { a = a!.Parent; b = b!.Parent; } return a; } /// /// Returns whether this appears before other in a post-order walk of the whole tree. /// public bool IsBefore(ILInstruction other) { if (other == null) throw new ArgumentNullException(nameof(other)); ILInstruction a = this; ILInstruction b = other; int levelA = a.CountAncestors(); int levelB = b.CountAncestors(); int originalLevelA = levelA; int originalLevelB = levelB; while (levelA > levelB) { a = a.Parent!; levelA--; } while (levelB > levelA) { b = b.Parent!; levelB--; } if (a == b) { // a or b is a descendant of the other, // whichever node has the higher level comes first in post-order walk. return originalLevelA > originalLevelB; } while (a.Parent != b.Parent) { a = a.Parent!; b = b.Parent!; } // now a and b have the same parent or are both root nodes return a.ChildIndex < b.ChildIndex; } private int CountAncestors() { int level = 0; for (ILInstruction? ancestor = this; ancestor != null; ancestor = ancestor.Parent) { level++; } return level; } /// /// Gets the stack type of the value produced by this instruction. /// public abstract StackType ResultType { get; } /* Not sure if it's a good idea to offer this on all instructions -- * e.g. ldloc for a local of type `int?` would return StackType.O (because it's not a lifted operation), * even though the underlying type is int = StackType.I4. /// /// Gets the underlying result type of the value produced by this instruction. /// /// If this is a lifted operation, the ResultType will be `StackType.O` (because Nullable{T} is a struct), /// and UnderlyingResultType will be result type of the corresponding non-lifted operation. /// /// If this is not a lifted operation, the underlying result type is equal to the result type. /// public virtual StackType UnderlyingResultType { get => ResultType; } */ internal static StackType CommonResultType(StackType a, StackType b) { if (a == StackType.I || b == StackType.I) return StackType.I; Debug.Assert(a == b); return a; } #if DEBUG /// /// Gets whether this node (or any subnode) was modified since the last ResetDirty() call. /// /// /// IsDirty is used by the StatementTransform, and must not be used by individual transforms within the loop. /// internal bool IsDirty { get; private set; } /// /// Marks this node (and all subnodes) as IsDirty=false. /// internal void ResetDirty() { foreach (ILInstruction inst in Descendants) inst.IsDirty = false; } #endif [Conditional("DEBUG")] protected private void MakeDirty() { #if DEBUG for (ILInstruction? inst = this; inst != null && !inst.IsDirty; inst = inst.parent) { inst.IsDirty = true; } #endif } const InstructionFlags invalidFlags = (InstructionFlags)(-1); InstructionFlags flags = invalidFlags; /// /// Gets the flags describing the behavior of this instruction. /// This property computes the flags on-demand and caches them /// until some change to the ILAst invalidates the cache. /// /// /// Flag cache invalidation makes use of the Parent property, /// so it is possible for this property to return a stale value /// if the instruction contains "stale positions" (see remarks on Parent property). /// public InstructionFlags Flags { get { if (flags == invalidFlags) { flags = ComputeFlags(); } return flags; } } /// /// Returns whether the instruction (or one of its child instructions) has at least one of the specified flags. /// public bool HasFlag(InstructionFlags flags) { return (this.Flags & flags) != 0; } /// /// Returns whether the instruction (without considering child instructions) has at least one of the specified flags. /// public bool HasDirectFlag(InstructionFlags flags) { return (this.DirectFlags & flags) != 0; } protected void InvalidateFlags() { for (ILInstruction? inst = this; inst != null && inst.flags != invalidFlags; inst = inst.parent) inst.flags = invalidFlags; } protected abstract InstructionFlags ComputeFlags(); /// /// Gets the flags for this instruction only, without considering the child instructions. /// public abstract InstructionFlags DirectFlags { get; } /// /// Gets the ILRange for this instruction alone, ignoring the operands. /// private Interval ILRange; public void AddILRange(Interval newRange) { this.ILRange = CombineILRange(this.ILRange, newRange); } protected static Interval CombineILRange(Interval oldRange, Interval newRange) { if (oldRange.IsEmpty) { return newRange; } if (newRange.IsEmpty) { return oldRange; } if (newRange.Start <= oldRange.Start) { if (newRange.End < oldRange.Start) { return newRange; // use the earlier range } else { // join overlapping ranges return new Interval(newRange.Start, Math.Max(newRange.End, oldRange.End)); } } else if (newRange.Start <= oldRange.End) { // join overlapping ranges return new Interval(oldRange.Start, Math.Max(newRange.End, oldRange.End)); } return oldRange; } public void AddILRange(ILInstruction sourceInstruction) { AddILRange(sourceInstruction.ILRange); } public void SetILRange(ILInstruction sourceInstruction) { ILRange = sourceInstruction.ILRange; } public void SetILRange(Interval range) { ILRange = range; } public int StartILOffset => ILRange.Start; public int EndILOffset => ILRange.End; public bool ILRangeIsEmpty => ILRange.IsEmpty; public IEnumerable ILRanges => new[] { ILRange }; public void WriteILRange(ITextOutput output, ILAstWritingOptions options) { ILRange.WriteTo(output, options); } /// /// Writes the ILAst to the text output. /// public abstract void WriteTo(ITextOutput output, ILAstWritingOptions options); public override string ToString() { var output = new PlainTextOutput(); WriteTo(output, new ILAstWritingOptions()); if (!ILRange.IsEmpty) { output.Write(" at IL_" + ILRange.Start.ToString("x4")); } return output.ToString(); } /// /// Calls the Visit*-method on the visitor corresponding to the concrete type of this instruction. /// public abstract void AcceptVisitor(ILVisitor visitor); /// /// Calls the Visit*-method on the visitor corresponding to the concrete type of this instruction. /// public abstract T AcceptVisitor(ILVisitor visitor); /// /// Calls the Visit*-method on the visitor corresponding to the concrete type of this instruction. /// public abstract T AcceptVisitor(ILVisitor visitor, C context); /// /// Gets the child nodes of this instruction. /// /// /// The ChildrenCollection does not actually store the list of children, /// it merely allows accessing the children stored in the various slots. /// public ChildrenCollection Children { get { return new ChildrenCollection(this); } } protected abstract int GetChildCount(); protected abstract ILInstruction GetChild(int index); protected abstract void SetChild(int index, ILInstruction value); protected abstract SlotInfo GetChildSlot(int index); #region ChildrenCollection + ChildrenEnumerator public readonly struct ChildrenCollection : IReadOnlyList { readonly ILInstruction inst; internal ChildrenCollection(ILInstruction inst) { Debug.Assert(inst != null); this.inst = inst!; } public int Count { get { return inst.GetChildCount(); } } public ILInstruction this[int index] { get { return inst.GetChild(index); } set { inst.SetChild(index, value); } } public ChildrenEnumerator GetEnumerator() { return new ChildrenEnumerator(inst); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } #if DEBUG int activeEnumerators; [Conditional("DEBUG")] internal void StartEnumerator() { activeEnumerators++; } [Conditional("DEBUG")] internal void StopEnumerator() { Debug.Assert(activeEnumerators > 0); activeEnumerators--; } #endif [Conditional("DEBUG")] internal void AssertNoEnumerators() { #if DEBUG Debug.Assert(activeEnumerators == 0); #endif } /// /// Enumerator over the children of an ILInstruction. /// Warning: even though this is a struct, it is invalid to copy: /// the number of constructor calls must match the number of dispose calls. /// public struct ChildrenEnumerator : IEnumerator { ILInstruction? inst; readonly int end; int pos; internal ChildrenEnumerator(ILInstruction inst) { DebugAssert(inst != null); this.inst = inst; this.pos = -1; this.end = inst!.GetChildCount(); #if DEBUG inst.StartEnumerator(); #endif } public ILInstruction Current { get { return inst!.GetChild(pos); } } public bool MoveNext() { return ++pos < end; } public void Dispose() { #if DEBUG if (inst != null) { inst.StopEnumerator(); inst = null; } #endif } object System.Collections.IEnumerator.Current { get { return this.Current; } } void System.Collections.IEnumerator.Reset() { pos = -1; } } #endregion /// /// Replaces this ILInstruction with the given replacement instruction. /// /// /// It is temporarily possible for a node to be used in multiple places in the ILAst, /// this method only replaces this node at its primary position (see remarks on ). /// /// This means you cannot use ReplaceWith() to wrap an instruction in another node. /// For example, node.ReplaceWith(new BitNot(node)) will first call the BitNot constructor, /// which sets node.Parent to the BitNot instance. /// The ReplaceWith() call then attempts to set BitNot.Argument to the BitNot instance, /// which creates a cyclic ILAst. Meanwhile, node's original parent remains unmodified. /// /// The solution in this case is to avoid using ReplaceWith. /// If the parent node is unknown, the following trick can be used: /// /// node.Parent.Children[node.ChildIndex] = new BitNot(node); /// /// Unlike the ReplaceWith() call, this will evaluate node.Parent and node.ChildIndex /// before the BitNot constructor is called, thus modifying the expected position in the ILAst. /// public void ReplaceWith(ILInstruction replacement) { Debug.Assert(parent!.GetChild(ChildIndex) == this); if (replacement == this) return; parent.SetChild(ChildIndex, replacement); } /// /// Returns all descendants of the ILInstruction in post-order. /// (including the ILInstruction itself) /// /// /// Within a loop 'foreach (var node in inst.Descendants)', it is illegal to /// add or remove from the child collections of node's ancestors, as those are /// currently being enumerated. /// Note that it is valid to modify node's children as those were already previously visited. /// As a special case, it is also allowed to replace node itself with another node. /// public IEnumerable Descendants { get { // Copy of TreeTraversal.PostOrder() specialized for ChildrenEnumerator // We could potentially eliminate the stack by using Parent/ChildIndex, // but that makes it difficult to reason about the behavior in the cases // where Parent/ChildIndex is not accurate (stale positions), especially // if the ILAst is modified during enumeration. Stack stack = new Stack(); ChildrenEnumerator enumerator = new ChildrenEnumerator(this); try { while (true) { while (enumerator.MoveNext()) { var element = enumerator.Current; stack.Push(enumerator); enumerator = new ChildrenEnumerator(element); } enumerator.Dispose(); if (stack.Count > 0) { enumerator = stack.Pop(); yield return enumerator.Current; } else { break; } } } finally { enumerator.Dispose(); while (stack.Count > 0) { stack.Pop().Dispose(); } } yield return this; } } /// /// Gets the ancestors of this node (including the node itself as first element). /// public IEnumerable Ancestors { get { for (ILInstruction? node = this; node != null; node = node.Parent) { yield return node; } } } /// /// Number of parents that refer to this instruction and are connected to the root. /// Usually is 0 for unconnected nodes and 1 for connected nodes, but may temporarily increase to 2 /// when the ILAst is re-arranged (e.g. within SetChildInstruction), /// or possibly even more (re-arrangement with stale positions). /// byte refCount; internal void AddRef() { if (refCount++ == 0) { Connected(); } } internal void ReleaseRef() { Debug.Assert(refCount > 0); if (--refCount == 0) { Disconnected(); } } /// /// Gets whether this ILInstruction is connected to the root node of the ILAst. /// /// /// This property returns true if the ILInstruction is reachable from the root node /// of the ILAst; it does not make use of the Parent field so the considerations /// about orphaned nodes and stale positions don't apply. /// protected internal bool IsConnected { get { return refCount > 0; } } /// /// Called after the ILInstruction was connected to the root node of the ILAst. /// protected virtual void Connected() { foreach (var child in Children) child.AddRef(); } /// /// Called after the ILInstruction was disconnected from the root node of the ILAst. /// protected virtual void Disconnected() { foreach (var child in Children) child.ReleaseRef(); } ILInstruction? parent; /// /// Gets the parent of this ILInstruction. /// /// /// It is temporarily possible for a node to be used in multiple places in the ILAst /// (making the ILAst a DAG instead of a tree). /// The Parent and ChildIndex properties are written whenever /// a node is stored in a slot. /// The node's occurrence in that slot is termed the "primary position" of the node, /// and all other (older) uses of the nodes are termed "stale positions". /// /// A consistent ILAst must not contain any stale positions. /// Debug builds of ILSpy check the ILAst for consistency after every IL transform. /// /// If a slot containing a node is overwritten with another node, the Parent /// and ChildIndex of the old node are not modified. /// This allows overwriting stale positions to restore consistency of the ILAst. /// /// If a "primary position" is overwritten, the Parent of the old node also remains unmodified. /// This makes the old node an "orphaned node". /// Orphaned nodes may later be added back to the ILAst (or can just be garbage-collected). /// /// Note that is it is possible (though unusual) for a stale position to reference an orphaned node. /// public ILInstruction? Parent { get { return parent; } } /// /// Gets the index of this node in the Parent.Children collection. /// /// /// It is temporarily possible for a node to be used in multiple places in the ILAst, /// this property returns the index of the primary position of this node (see remarks on ). /// public int ChildIndex { get; internal set; } = -1; /// /// Gets information about the slot in which this instruction is stored. /// (i.e., the relation of this instruction to its parent instruction) /// /// /// It is temporarily possible for a node to be used in multiple places in the ILAst, /// this property returns the slot of the primary position of this node (see remarks on ). /// /// Precondition: this node must not be orphaned. /// public SlotInfo? SlotInfo { get { if (parent == null) return null; Debug.Assert(parent.GetChild(this.ChildIndex) == this); return parent.GetChildSlot(this.ChildIndex); } } /// /// Replaces a child of this ILInstruction. /// /// Reference to the field holding the child /// New child /// Index of the field in the Children collection protected internal void SetChildInstruction(ref T childPointer, T newValue, int index) where T : ILInstruction? { T oldValue = childPointer; Debug.Assert(oldValue == GetChild(index)); if (oldValue == newValue && newValue?.parent == this && newValue.ChildIndex == index) return; childPointer = newValue; if (newValue != null) { newValue.parent = this; newValue.ChildIndex = index; } InvalidateFlags(); MakeDirty(); if (refCount > 0) { // The new value may be a subtree of the old value. // We first call AddRef(), then ReleaseRef() to prevent the subtree // that stays connected from receiving a Disconnected() notification followed by a Connected() notification. if (newValue != null) newValue.AddRef(); if (oldValue != null) oldValue.ReleaseRef(); } } /// /// Called when a new child is added to a InstructionCollection. /// protected internal void InstructionCollectionAdded(ILInstruction newChild) { Debug.Assert(GetChild(newChild.ChildIndex) == newChild); Debug.Assert(!this.IsDescendantOf(newChild), "ILAst must form a tree"); // If a call to ReplaceWith() triggers the "ILAst must form a tree" assertion, // make sure to read the remarks on the ReplaceWith() method. newChild.parent = this; if (refCount > 0) newChild.AddRef(); } /// /// Called when a child is removed from a InstructionCollection. /// protected internal void InstructionCollectionRemoved(ILInstruction oldChild) { if (refCount > 0) oldChild.ReleaseRef(); } /// /// Called when a series of add/remove operations on the InstructionCollection is complete. /// protected internal virtual void InstructionCollectionUpdateComplete() { InvalidateFlags(); MakeDirty(); } /// /// Creates a deep clone of the ILInstruction. /// /// /// It is valid to clone nodes with stale positions (see remarks on Parent); /// the result of such a clone will not contain any stale positions (nodes at /// multiple positions will be cloned once per position). /// public abstract ILInstruction Clone(); /// /// Creates a shallow clone of the ILInstruction. /// /// /// Like MemberwiseClone(), except that the new instruction starts as disconnected. /// protected ILInstruction ShallowClone() { ILInstruction inst = (ILInstruction)MemberwiseClone(); // reset refCount and parent so that the cloned instruction starts as disconnected inst.refCount = 0; inst.parent = null; inst.flags = invalidFlags; #if DEBUG inst.activeEnumerators = 0; #endif return inst; } /// /// Attempts to match the specified node against the pattern. /// /// this: The syntactic pattern. /// The syntax node to test against the pattern. /// /// Returns a match object describing the result of the matching operation. /// Check the property to see whether the match was successful. /// For successful matches, the match object allows retrieving the nodes that were matched with the captured groups. /// public Match Match(ILInstruction node) { Match match = new Match(); match.Success = PerformMatch(node, ref match); return match; } /// /// Attempts matching this instruction against the other instruction. /// /// The instruction to compare with. /// The match object, used to store global state during the match (such as the results of capture groups). /// Returns whether the (partial) match was successful. /// If the method returns true, it adds the capture groups (if any) to the match. /// If the method returns false, the match object may remain in a partially-updated state and /// needs to be restored before it can be reused. protected internal abstract bool PerformMatch(ILInstruction? other, ref Match match); /// /// Attempts matching this instruction against a list of other instructions (or a part of said list). /// /// Stores state about the current list match. /// The match object, used to store global state during the match (such as the results of capture groups). /// Returns whether the (partial) match was successful. /// If the method returns true, it updates listMatch.SyntaxIndex to point to the next node that was not part of the match, /// and adds the capture groups (if any) to the match. /// If the method returns false, the listMatch and match objects remain in a partially-updated state and need to be restored /// before they can be reused. protected internal virtual bool PerformMatch(ref ListMatch listMatch, ref Match match) { // Base implementation expects the node to match a single element. // Any patterns matching 0 or more than 1 element must override this method. if (listMatch.SyntaxIndex < listMatch.SyntaxList.Count) { if (PerformMatch(listMatch.SyntaxList[listMatch.SyntaxIndex], ref match)) { listMatch.SyntaxIndex++; return true; } } return false; } /// /// Extracts the this instruction. /// The instruction is replaced with a load of a new temporary variable; /// and the instruction is moved to a store to said variable at block-level. /// Returns the new variable. /// /// If extraction is not possible, the ILAst is left unmodified and the function returns null. /// May return null if extraction is not possible. /// public ILVariable? Extract(ILTransformContext context) { return Transforms.ExtractionContext.Extract(this, context); } /// /// Prepares "extracting" a descendant instruction out of this instruction. /// This is the opposite of ILInlining. It may involve re-compiling high-level constructs into lower-level constructs. /// /// True if extraction is possible; false otherwise. internal virtual bool PrepareExtract(int childIndex, Transforms.ExtractionContext ctx) { if (!GetChildSlot(childIndex).CanInlineInto) { return false; } // Check whether re-ordering with predecessors is valid: for (int i = childIndex - 1; i >= 0; --i) { ILInstruction predecessor = GetChild(i); if (!GetChildSlot(i).CanInlineInto) { return false; } ctx.RegisterMoveIfNecessary(predecessor); } return true; } /// /// Gets whether the expressionBeingMoved, which previously executes prior to `this`, /// may be moved into a descendant of the specified slot. /// (i.e. this controls whether FindLoadInNext may descent into the specified slot) /// /// Note: this does not check whether reordering with the previous slots is valid; only whether the target slot supports inlining at all! /// internal virtual bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { return GetChildSlot(childIndex).CanInlineInto; } /// /// Some slots in the ILAst have restrictions on which instructions can appear in them. /// Used to suppress inlining if the new child does not satisfy the restrictions. /// Unlike `CanInlineIntoSlot`, this is not about descendants of the slot, only about /// whether SetChild(childIndex, newChild) is valid. /// (i.e. this controls whether FindLoadInNext may return the specified slot as a final result) /// /// Warning: after newChild is inlined, other nodes may be inlined into newChild's sub-instructions /// without asking this function again. This means this function is not suitable for protecting /// a slot from having side effects, use `CanInlineIntoSlot` for that. /// internal virtual bool SatisfiesSlotRestrictionForInlining(int childIndex, ILInstruction newChild) { return true; } } public interface IInstructionWithTypeOperand { IType Type { get; } } public interface IInstructionWithFieldOperand { IField Field { get; } } public interface IInstructionWithMethodOperand { IMethod? Method { get; } } public interface ILiftableInstruction { /// /// Gets whether the instruction was lifted; that is, whether is accepts /// potentially nullable arguments. /// bool IsLifted { get; } /// /// If the instruction is lifted and returns a nullable result, /// gets the underlying result type. /// /// Note that not all lifted instructions return a nullable result: /// C# comparisons always return a bool! /// StackType UnderlyingResultType { get; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs ================================================ #nullable enable // Copyright (c) 2016 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.IL { /// /// The collection of variables in a ILFunction. /// public class ILVariableCollection : ICollection, IReadOnlyList { readonly ILFunction scope; readonly List list = new List(); internal ILVariableCollection(ILFunction scope) { this.scope = scope; } /// /// Gets a variable given its IndexInFunction. /// public ILVariable this[int index] { get { return list[index]; } } public bool Add(ILVariable item) { if (item.Function != null) { if (item.Function == scope) return false; else throw new ArgumentException("Variable already belongs to another scope"); } item.Function = scope; item.IndexInFunction = list.Count; list.Add(item); return true; } void ICollection.Add(ILVariable item) { Add(item); } public void Clear() { foreach (var v in list) { v.Function = null; } list.Clear(); } public bool Contains(ILVariable item) { Debug.Assert(item.Function != scope || list[item.IndexInFunction] == item); return item.Function == scope; } public bool Remove(ILVariable item) { if (item.Function != scope) return false; Debug.Assert(list[item.IndexInFunction] == item); RemoveAt(item.IndexInFunction); return true; } void RemoveAt(int index) { list[index].Function = null; // swap-remove index list[index] = list[list.Count - 1]; list[index].IndexInFunction = index; list.RemoveAt(list.Count - 1); } /// /// Remove variables that have StoreCount == LoadCount == AddressCount == 0. /// public void RemoveDead() { for (int i = 0; i < list.Count;) { if (ShouldRemoveVariable(list[i])) { RemoveAt(i); } else { i++; } } static bool ShouldRemoveVariable(ILVariable v) { if (!v.IsDead) return false; // Note: we cannot remove display-class locals from the collection, // even if they are unused - which is always the case, if TDCU succeeds, // because they are necessary for PDB generation to produce correct results. if (v.Kind == VariableKind.DisplayClassLocal) return false; // Do not remove parameter variables, as these are defined even if unused. if (v.Kind == VariableKind.Parameter) { // However, remove unused this-parameters of delegates, expression trees, etc. // These will be replaced with the top-level function's this-parameter. if (v.Index == -1 && v.Function!.Kind != ILFunctionKind.TopLevelFunction) return true; return false; } return true; } } public int Count { get { return list.Count; } } public void CopyTo(ILVariable[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } bool ICollection.IsReadOnly { get { return false; } } public List.Enumerator GetEnumerator() { return list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { /// If statement / conditional expression. if (condition) trueExpr else falseExpr /// /// The condition must return StackType.I4, use comparison instructions like Ceq to check if other types are non-zero. /// /// If the condition evaluates to non-zero, the TrueInst is executed. /// If the condition evaluates to zero, the FalseInst is executed. /// The return value of the IfInstruction is the return value of the TrueInst or FalseInst. /// /// IfInstruction is also used to represent logical operators: /// "a || b" ==> if (a) (ldc.i4 1) else (b) /// "a && b" ==> if (a) (b) else (ldc.i4 0) /// "a ? b : c" ==> if (a) (b) else (c) /// partial class IfInstruction : ILInstruction { public IfInstruction(ILInstruction condition, ILInstruction trueInst, ILInstruction? falseInst = null) : base(OpCode.IfInstruction) { this.Condition = condition; this.TrueInst = trueInst; this.FalseInst = falseInst ?? new Nop(); } public static IfInstruction LogicAnd(ILInstruction lhs, ILInstruction rhs) { return new IfInstruction(lhs, rhs, new LdcI4(0)); } public static IfInstruction LogicOr(ILInstruction lhs, ILInstruction? rhs) { return new IfInstruction(lhs, new LdcI4(1), rhs); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(condition.ResultType == StackType.I4); Debug.Assert(trueInst.ResultType == falseInst.ResultType || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable) || falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); } public override StackType ResultType { get { if (trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) return falseInst.ResultType; else return trueInst.ResultType; } } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.ControlFlow | condition.Flags | SemanticHelper.CombineBranches(trueInst.Flags, falseInst.Flags); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (options.UseLogicOperationSugar) { if (MatchLogicAnd(out var lhs, out var rhs)) { output.Write("logic.and("); lhs.WriteTo(output, options); output.Write(", "); rhs.WriteTo(output, options); output.Write(')'); return; } if (MatchLogicOr(out lhs, out rhs)) { output.Write("logic.or("); lhs.WriteTo(output, options); output.Write(", "); rhs.WriteTo(output, options); output.Write(')'); return; } } output.Write(OpCode); output.Write(" ("); condition.WriteTo(output, options); output.Write(") "); trueInst.WriteTo(output, options); if (falseInst.OpCode != OpCode.Nop) { output.Write(" else "); falseInst.WriteTo(output, options); } } /// /// Gets whether the input instruction occurs in a context where it is being compared with 0. /// internal static bool IsInConditionSlot(ILInstruction inst) { var slot = inst.SlotInfo; if (slot == IfInstruction.ConditionSlot) return true; if (slot == IfInstruction.TrueInstSlot || slot == IfInstruction.FalseInstSlot || slot == NullCoalescingInstruction.FallbackInstSlot) return IsInConditionSlot(inst.Parent!); if (inst.Parent is Comp comp) { if (comp.Left == inst && comp.Right.MatchLdcI4(0)) return true; if (comp.Right == inst && comp.Left.MatchLdcI4(0)) return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.Decompiler.IL { public sealed class InstructionCollection : IList, IReadOnlyList where T : ILInstruction { readonly ILInstruction parentInstruction; readonly int firstChildIndex; readonly List list = new List(); public InstructionCollection(ILInstruction parentInstruction, int firstChildIndex) { if (parentInstruction == null) throw new ArgumentNullException(nameof(parentInstruction)); this.parentInstruction = parentInstruction; this.firstChildIndex = firstChildIndex; } public int Count { get { return list.Count; } } public T this[int index] { get { return list[index]; } set { T oldValue = list[index]; if (!(oldValue == value && value.Parent == parentInstruction && value.ChildIndex == index)) { list[index] = value; value.ChildIndex = index + firstChildIndex; parentInstruction.InstructionCollectionAdded(value); parentInstruction.InstructionCollectionRemoved(oldValue); parentInstruction.InstructionCollectionUpdateComplete(); } } } #region GetEnumerator public Enumerator GetEnumerator() { return new Enumerator(this); } /// /// Custom enumerator for InstructionCollection. /// Unlike List{T}.Enumerator, this enumerator allows replacing an item during the enumeration. /// Adding/removing items from the collection still is invalid (however, such /// invalid actions are only detected in debug builds). /// /// Warning: even though this is a struct, it is invalid to copy: /// the number of constructor calls must match the number of dispose calls. /// public struct Enumerator : IEnumerator { #if DEBUG ILInstruction? parentInstruction; #endif readonly List list; int pos; public Enumerator(InstructionCollection col) { this.list = col.list; this.pos = -1; #if DEBUG this.parentInstruction = col.parentInstruction; col.parentInstruction.StartEnumerator(); #endif } [DebuggerStepThrough] public bool MoveNext() { return ++pos < list.Count; } public T Current { [DebuggerStepThrough] get { return list[pos]; } } [DebuggerStepThrough] public void Dispose() { #if DEBUG if (parentInstruction != null) { parentInstruction.StopEnumerator(); parentInstruction = null; } #endif } void System.Collections.IEnumerator.Reset() { pos = -1; } object System.Collections.IEnumerator.Current { get { return this.Current; } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /// /// Gets the index of the instruction in this collection. /// Returns -1 if the instruction does not exist in the collection. /// /// /// Runs in O(1) if the item can be found using the Parent/ChildIndex properties. /// Otherwise, runs in O(N). /// public int IndexOf(T? item) { if (item == null) { // InstructionCollection can't contain nulls return -1; } // If this collection is the item's primary position, we can use ChildIndex: int index = item.ChildIndex - firstChildIndex; if (index >= 0 && index < list.Count && list[index] == item) return index; // But we still need to fall back on a full search, because the ILAst might be // in a state where item is in multiple locations. return list.IndexOf(item); } /// /// Gets whether the item is in this collection. /// /// /// This method searches the list. /// Usually it's more efficient to test item.Parent instead! /// public bool Contains(T? item) { return IndexOf(item) >= 0; } void ICollection.CopyTo(T[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } bool ICollection.IsReadOnly { get { return false; } } public void Add(T value) { parentInstruction.AssertNoEnumerators(); value.ChildIndex = list.Count + firstChildIndex; list.Add(value); parentInstruction.InstructionCollectionAdded(value); parentInstruction.InstructionCollectionUpdateComplete(); } public void AddRange(IEnumerable values) { parentInstruction.AssertNoEnumerators(); foreach (T value in values) { value.ChildIndex = list.Count + firstChildIndex; list.Add(value); parentInstruction.InstructionCollectionAdded(value); } parentInstruction.InstructionCollectionUpdateComplete(); } /// /// Replaces all entries in the InstructionCollection with the newList. /// /// /// Equivalent to Clear() followed by AddRange(newList), but slightly more efficient. /// public void ReplaceList(IEnumerable newList) { parentInstruction.AssertNoEnumerators(); int index = 0; foreach (T value in newList) { value.ChildIndex = index + firstChildIndex; if (index < list.Count) { T oldValue = list[index]; list[index] = value; parentInstruction.InstructionCollectionAdded(value); parentInstruction.InstructionCollectionRemoved(oldValue); } else { list.Add(value); parentInstruction.InstructionCollectionAdded(value); } index++; } for (int i = index; i < list.Count; i++) { parentInstruction.InstructionCollectionRemoved(list[i]); } list.RemoveRange(index, list.Count - index); parentInstruction.InstructionCollectionUpdateComplete(); } public void Insert(int index, T item) { parentInstruction.AssertNoEnumerators(); list.Insert(index, item); item.ChildIndex = index; parentInstruction.InstructionCollectionAdded(item); for (int i = index + 1; i < list.Count; i++) { T other_item = list[i]; // Update ChildIndex of items after the inserted one, but only if // that's their 'primary position' (in case of multiple parents) if (other_item.Parent == parentInstruction && other_item.ChildIndex == i + firstChildIndex - 1) other_item.ChildIndex = i + firstChildIndex; } parentInstruction.InstructionCollectionUpdateComplete(); } public void RemoveAt(int index) { parentInstruction.AssertNoEnumerators(); parentInstruction.InstructionCollectionRemoved(list[index]); list.RemoveAt(index); for (int i = index; i < list.Count; i++) { var other_item = list[i]; if (other_item.Parent == parentInstruction && other_item.ChildIndex == i + firstChildIndex + 1) other_item.ChildIndex = i + firstChildIndex; } parentInstruction.InstructionCollectionUpdateComplete(); } /// /// Remove item at index index in O(1) by swapping it with the last element in the collection. /// public void SwapRemoveAt(int index) { parentInstruction.AssertNoEnumerators(); parentInstruction.InstructionCollectionRemoved(list[index]); int removeIndex = list.Count - 1; T movedItem = list[index] = list[removeIndex]; list.RemoveAt(removeIndex); if (movedItem.Parent == parentInstruction && movedItem.ChildIndex == removeIndex + firstChildIndex) movedItem.ChildIndex = index + firstChildIndex; parentInstruction.InstructionCollectionUpdateComplete(); } public void Clear() { parentInstruction.AssertNoEnumerators(); foreach (var entry in list) { parentInstruction.InstructionCollectionRemoved(entry); } list.Clear(); parentInstruction.InstructionCollectionUpdateComplete(); } public bool Remove(T item) { int index = IndexOf(item); if (index >= 0) { RemoveAt(index); return true; } return false; } public void RemoveRange(int index, int count) { parentInstruction.AssertNoEnumerators(); for (int i = 0; i < count; i++) { parentInstruction.InstructionCollectionRemoved(list[index + i]); } list.RemoveRange(index, count); for (int i = index; i < list.Count; i++) { var other_item = list[i]; if (other_item.Parent == parentInstruction && other_item.ChildIndex == i + firstChildIndex + count) other_item.ChildIndex = i + firstChildIndex; } parentInstruction.InstructionCollectionUpdateComplete(); } /// /// Removes all elements for which the predicate returns true. /// /// /// This method runs in O(N), which is more efficient than calling RemoveAt() in a loop. /// The collection may be in an invalid state during the invocation of the predicate. /// public int RemoveAll(Predicate predicate) { parentInstruction.AssertNoEnumerators(); int j = 0; for (int i = 0; i < list.Count; i++) { T item = list[i]; if (predicate(item)) { parentInstruction.InstructionCollectionRemoved(item); } else { // keep the item if (item.Parent == parentInstruction && item.ChildIndex == i + firstChildIndex) item.ChildIndex = j + firstChildIndex; list[j] = item; j++; } } int removed = list.Count - j; if (removed > 0) { list.RemoveRange(j, removed); parentInstruction.InstructionCollectionUpdateComplete(); } return removed; } public void MoveElementToIndex(int oldIndex, int newIndex) { parentInstruction.AssertNoEnumerators(); var item = list[oldIndex]; Insert(newIndex, item); if (oldIndex < newIndex) RemoveAt(oldIndex); else RemoveAt(oldIndex + 1); } public void MoveElementToIndex(T item, int newIndex) { parentInstruction.AssertNoEnumerators(); int oldIndex = IndexOf(item); if (oldIndex >= 0) { Insert(newIndex, item); if (oldIndex < newIndex) RemoveAt(oldIndex); else RemoveAt(oldIndex + 1); } } public void MoveElementToEnd(int index) { MoveElementToIndex(index, list.Count); } public void MoveElementToEnd(T item) { MoveElementToIndex(item, list.Count); } // more efficient versions of some LINQ methods: public T First() { return list[0]; } public T? FirstOrDefault() { return list.Count > 0 ? list[0] : null; } public T Last() { return list[list.Count - 1]; } public T? LastOrDefault() { return list.Count > 0 ? list[list.Count - 1] : null; } public T? SecondToLastOrDefault() { return list.Count > 1 ? list[list.Count - 2] : null; } public T? ElementAtOrDefault(int index) { if (index >= 0 && index < list.Count) return list[index]; return null; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/IsInst.cs ================================================ #nullable enable // Copyright (c) 2025 Daniel Grunwald // // 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.Diagnostics; using ICSharpCode.Decompiler.CSharp; namespace ICSharpCode.Decompiler.IL; partial class IsInst { internal override bool CanInlineIntoSlot(int childIndex, ILInstruction newChild) { Debug.Assert(childIndex == 0); Debug.Assert(base.CanInlineIntoSlot(childIndex, newChild)); if (this.Type.IsReferenceType == true) { return true; // reference-type isinst is always supported } if (SemanticHelper.IsPure(newChild.Flags)) { return true; // emulated via "expr is T ? (T)expr : null" } else if (newChild is Box box && SemanticHelper.IsPure(box.Argument.Flags) && this.Argument.Children.Count == 0) { // Also emulated via "expr is T ? (T)expr : null". // This duplicates the boxing side-effect, but that's harmless as one of the boxes is only // used in the `expr is T` type test where the object identity can never be observed. // This appears as part of C# pattern matching, inlining early makes those code patterns easier to detect. // We can only do this if the Box appears directly top-level in the IsInst, we cannot inline Box instructions // deeper into our Argument subtree. So restricts to the case were the previous argument has no children // (which means inlining can only replace the argument, not insert within it). return true; } if (this.Parent is UnboxAny unboxAny && ExpressionBuilder.IsUnboxAnyWithIsInst(unboxAny, this.Type)) { return true; // supported pattern "expr as T?" } if (this.Parent != null && (this.Parent.MatchCompEqualsNull(out _) || this.Parent.MatchCompNotEqualsNull(out _))) { return true; // supported pattern "expr is T" } if (this.Parent is Block { Kind: BlockKind.ControlFlow }) { return true; // supported via StatementBuilder.VisitIsInst } return false; } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { public sealed partial class LdFlda { internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); switch (field.DeclaringType.IsReferenceType) { case true: Debug.Assert(target.ResultType == StackType.O, "Class fields can only be accessed with an object on the stack"); break; case false: Debug.Assert(target.ResultType == StackType.I || target.ResultType == StackType.Ref, "Struct fields can only be accessed with a pointer on the stack"); break; case null: // field of unresolved type Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I || target.ResultType == StackType.Ref || target.ResultType == StackType.Unknown, "Field of unresolved type with invalid target"); break; } } } public sealed partial class StObj { internal override bool SatisfiesSlotRestrictionForInlining(int childIndex, ILInstruction newChild) { if (childIndex == 0) // target slot { Debug.Assert(GetChildSlot(childIndex) == TargetSlot); // For a store to a field or array element, C# will only throw NullReferenceException/IndexOfBoundsException // after the value-to-be-stored has been computed. // This means a LdFlda/LdElema used as target for StObj must have DelayExceptions==true to allow a translation to C# // without changing the program semantics. See https://github.com/icsharpcode/ILSpy/issues/2050 switch (newChild.OpCode) { case OpCode.LdElema: case OpCode.LdFlda: { Debug.Assert(newChild.HasDirectFlag(InstructionFlags.MayThrow)); // If the ldelema/ldflda may throw a non-delayed exception, inlining will cause it // to turn into a delayed exception after the translation to C#. // This is only valid if the value computation doesn't involve any side effects. if (!SemanticHelper.IsPure(this.Value.Flags)) { return false; } // Note that after inlining such a ldelema/ldflda, the normal inlining rules will // prevent us from inlining an effectful instruction into the value slot. break; } } } return base.SatisfiesSlotRestrictionForInlining(childIndex, newChild); } /// /// called as part of CheckInvariant() /// void CheckTargetSlot() { switch (this.Target.OpCode) { case OpCode.LdElema: case OpCode.LdFlda: if (this.Target.HasDirectFlag(InstructionFlags.MayThrow)) { Debug.Assert(SemanticHelper.IsPure(this.Value.Flags)); } break; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/LdLen.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { /// /// Description of LdLen. /// public sealed partial class LdLen { readonly StackType resultType; public LdLen(StackType type, ILInstruction array) : base(OpCode.LdLen) { Debug.Assert(type == StackType.I || type == StackType.I4 || type == StackType.I8); this.resultType = type; this.Array = array; } public override StackType ResultType { get { return resultType; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('.'); output.Write(resultType); output.Write('('); this.array.WriteTo(output, options); output.Write(')'); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/Leave.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { /// /// Unconditional branch. goto target; /// /// /// When jumping to the entrypoint of the current block container, the branch represents a continue statement. /// /// Phase-1 execution of a branch is a no-op. /// Phase-2 execution removes PopCount elements from the evaluation stack /// and jumps to the target block. /// partial class Leave : ILInstruction, IBranchOrLeaveInstruction { BlockContainer? targetContainer; public Leave(BlockContainer? targetContainer, ILInstruction? value = null) : base(OpCode.Leave) { // Note: ILReader will create Leave instructions with targetContainer==null to represent 'endfinally', // the targetContainer will then be filled in by BlockBuilder this.targetContainer = targetContainer; this.Value = value ?? new Nop(); } protected override InstructionFlags ComputeFlags() { return value.Flags | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; } } public BlockContainer TargetContainer { get { return targetContainer!; } set { if (targetContainer != null && IsConnected) targetContainer.LeaveCount--; targetContainer = value; if (targetContainer != null && IsConnected) targetContainer.LeaveCount++; } } protected override void Connected() { base.Connected(); if (targetContainer != null) targetContainer.LeaveCount++; } protected override void Disconnected() { base.Disconnected(); if (targetContainer != null) targetContainer.LeaveCount--; } public string TargetLabel { get { return targetContainer?.EntryPoint != null ? targetContainer.EntryPoint.Label : string.Empty; } } /// /// Gets whether the leave instruction is directly leaving the whole ILFunction. /// (TargetContainer == main container of the function). /// /// This is only valid for functions returning void (representing value-less "return;"), /// and for iterators (representing "yield break;"). /// /// Note: returns false for leave instructions that indirectly leave the function /// (e.g. leaving a try block, and the try-finally construct is immediately followed /// by another leave instruction) /// public bool IsLeavingFunction { get { return targetContainer?.Parent is ILFunction; } } /// /// Gets whether this branch executes at least one finally block before jumping to the end of the target block container. /// public bool TriggersFinallyBlock { get { return Branch.GetExecutesFinallyBlock(this, TargetContainer); } } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(targetContainer!)); Debug.Assert(phase <= ILPhase.InILReader || phase == ILPhase.InAsyncAwait || value.ResultType == targetContainer!.ResultType); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (targetContainer != null) { output.Write(' '); output.WriteLocalReference(TargetLabel, targetContainer); output.Write(" ("); value.WriteTo(output, options); output.Write(')'); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs ================================================ #nullable enable // Copyright (c) 2017 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; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.IL { partial class LockInstruction { public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("lock ("); OnExpression.WriteTo(output, options); output.WriteLine(") {"); output.Indent(); Body.WriteTo(output, options); output.Unindent(); output.WriteLine(); output.Write("}"); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs ================================================ #nullable enable // Copyright (c) 2017 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { // Note: The comp instruction also supports three-valued logic via ComparisonLiftingKind.ThreeValuedLogic. // comp.i4.lifted[3VL](x == ldc.i4 0) is used to represent a lifted logic.not. partial class ThreeValuedBoolAnd : ILiftableInstruction { bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O); } } partial class ThreeValuedBoolOr : ILiftableInstruction { bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O); } } partial class UserDefinedLogicOperator { protected override InstructionFlags ComputeFlags() { // left is always executed; right only sometimes return DirectFlags | left.Flags | SemanticHelper.CombineBranches(InstructionFlags.None, right.Flags); } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect | InstructionFlags.ControlFlow; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs ================================================ #nullable enable // Copyright (c) 2020 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.Diagnostics; using System.Diagnostics.CodeAnalysis; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class MatchInstruction : ILInstruction { /* Pseudo-Code for interpreting a MatchInstruction: bool Eval() { var value = this.TestedOperand.Eval(); if (this.CheckNotNull && value == null) return false; if (this.CheckType && !(value is this.Variable.Type)) return false; if (this.IsDeconstructCall) { deconstructResult = new[numArgs]; EvalCall(this.Method, value, out deconstructResult[0], .., out deconstructResult[numArgs-1]); // any occurrences of 'deconstruct.result' in the subPatterns will refer // to the values provided by evaluating the call. } Variable.Value = value; foreach (var subPattern in this.SubPatterns) { if (!subPattern.Eval()) return false; } return true; } */ /* Examples of MatchInstructions: expr is var x: match(x = expr) expr is {} x: match.notnull(x = expr) expr is T x: match.type[T](x = expr) expr is C { A: var x } z: match.type[C](z = expr) { match(x = z.A) } expr is C { A: var x, B: 42, C: { A: 4 } } z: match.type[C](z = expr) { match(x = z.A), comp (z.B == 42), match.notnull(temp2 = z.C) { comp (temp2.A == 4) } } expr is C(var x, var y, <4): match.type[C].deconstruct[C.Deconstruct](tmp1 = expr) { match(x = deconstruct.result1(tmp1)), match(y = deconstruct.result2(tmp1)), comp(deconstruct.result3(tmp1) < 4), } expr is C(1, D(2, 3)): match.type[C].deconstruct(c = expr) { comp(deconstruct.result1(c) == 1), match.type[D].deconstruct(d = deconstruct.result2(c)) { comp(deconstruct.result1(d) == 2), comp(deconstruct.result2(d) == 3), } } */ public bool IsVar => !CheckType && !CheckNotNull && !IsDeconstructCall && !IsDeconstructTuple && SubPatterns.Count == 0; public bool HasDesignator => Variable.LoadCount + Variable.AddressCount > SubPatterns.Count; public int NumPositionalPatterns { get { if (IsDeconstructCall) return method!.Parameters.Count - (method.IsStatic ? 1 : 0); else if (IsDeconstructTuple) return TupleType.GetTupleElementTypes(variable.Type).Length; else return 0; } } public MatchInstruction(ILVariable variable, ILInstruction testedOperand) : this(variable, method: null, testedOperand) { } /// /// Checks whether the input instruction can represent a pattern matching operation. /// /// Any pattern matching instruction will first evaluate the `testedOperand` (a descendant of `inst`), /// and then match the value of that operand against the pattern encoded in the instruction. /// The matching may have side-effects on the newly-initialized pattern variables /// (even if the pattern fails to match!). /// The pattern matching instruction evaluates to 1 (as I4) if the pattern matches, or 0 otherwise. /// public static bool IsPatternMatch(ILInstruction? inst, [NotNullWhen(true)] out ILInstruction? testedOperand, DecompilerSettings? settings) { switch (inst) { case MatchInstruction m: testedOperand = m.testedOperand; return true; case Comp comp: if (comp.MatchLogicNot(out var operand) && IsPatternMatch(operand, out testedOperand, settings)) { return settings?.PatternCombinators ?? true; } else { testedOperand = comp.Left; if (!(settings?.RelationalPatterns ?? true)) { if (comp.Kind is not (ComparisonKind.Equality or ComparisonKind.Inequality)) return false; } if (!(settings?.PatternCombinators ?? true)) { if (comp.Kind is ComparisonKind.Inequality) return false; } return IsConstant(comp.Right); } case Call call when IsCallToOpEquality(call, KnownTypeCode.String): testedOperand = call.Arguments[0]; return call.Arguments[1].OpCode == OpCode.LdStr; case Call call when IsCallToOpEquality(call, KnownTypeCode.Decimal): testedOperand = call.Arguments[0]; return call.Arguments[1].OpCode == OpCode.LdcDecimal; default: testedOperand = null; return false; } } internal static bool IsCallToOpEquality(Call call, KnownTypeCode knownType) { return call.Method.IsOperator && call.Method.Name == "op_Equality" && call.Method.DeclaringType.IsKnownType(knownType) && call.Arguments.Count == 2; } internal static bool IsConstant(ILInstruction inst) { return inst.OpCode switch { OpCode.LdcDecimal => true, OpCode.LdcF4 => true, OpCode.LdcF8 => true, OpCode.LdcI4 => true, OpCode.LdcI8 => true, OpCode.LdNull => true, _ => false }; } internal IType GetDeconstructResultType(int index) { if (this.IsDeconstructCall) { int firstOutParam = (method!.IsStatic ? 1 : 0); var outParamType = method.Parameters[firstOutParam + index].Type; if (outParamType is not ByReferenceType brt) throw new InvalidOperationException("deconstruct out param must be by reference"); return brt.ElementType; } if (this.IsDeconstructTuple) { var elementTypes = TupleType.GetTupleElementTypes(this.variable.Type); return elementTypes[index]; } throw new InvalidOperationException("GetDeconstructResultType requires a deconstruct pattern"); } void AdditionalInvariants() { Debug.Assert(variable.Kind == VariableKind.PatternLocal); if (this.IsDeconstructCall) { Debug.Assert(IsDeconstructMethod(method)); } else { Debug.Assert(method == null); } if (this.IsDeconstructTuple) { Debug.Assert(variable.Type.Kind == TypeKind.Tuple); } Debug.Assert(SubPatterns.Count >= NumPositionalPatterns); foreach (var subPattern in SubPatterns) { if (!IsPatternMatch(subPattern, out ILInstruction? operand, null)) throw new InvalidOperationException("Sub-Pattern must be a valid pattern"); // the first child is TestedOperand int subPatternIndex = subPattern.ChildIndex - 1; if (subPatternIndex < NumPositionalPatterns) { // positional pattern Debug.Assert(operand is DeconstructResultInstruction result && result.Index == subPatternIndex); } else if (operand.MatchLdFld(out var target, out _)) { Debug.Assert(target.MatchLdLocRef(variable)); } else if (operand is CallInstruction call) { Debug.Assert(call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter); Debug.Assert(call.Arguments[0].MatchLdLocRef(variable)); } else { Debug.Fail("Tested operand of sub-pattern is invalid."); } } } internal static bool IsDeconstructMethod(IMethod? method) { if (method == null) return false; if (method.Name != "Deconstruct") return false; if (method.ReturnType.Kind != TypeKind.Void) return false; int firstOutParam = (method.IsStatic ? 1 : 0); if (method.IsStatic) { if (!method.IsExtensionMethod) return false; // TODO : check whether all type arguments can be inferred from the first argument } else { if (method.TypeParameters.Count != 0) return false; } // TODO : check whether the method is ambigious if (method.Parameters.Count < firstOutParam) return false; for (int i = firstOutParam; i < method.Parameters.Count; i++) { if (method.Parameters[i].ReferenceKind != ReferenceKind.Out) return false; } return true; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (CheckNotNull) { output.Write(".notnull"); } if (CheckType) { output.Write(".type["); variable.Type.WriteTo(output); output.Write(']'); } if (IsDeconstructCall) { output.Write(".deconstruct["); if (method == null) output.Write(""); else method.WriteTo(output); output.Write(']'); } if (IsDeconstructTuple) { output.Write(".tuple"); } output.Write(' '); output.Write('('); Variable.WriteTo(output); output.Write(" = "); TestedOperand.WriteTo(output, options); output.Write(')'); if (SubPatterns.Count > 0) { output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); foreach (var pattern in SubPatterns) { pattern.WriteTo(output, options); output.WriteLine(); } output.Unindent(); output.Write('}'); output.MarkFoldEnd(); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.IL { interface ISupportsUnalignedPrefix { /// /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. /// byte UnalignedPrefix { get; set; } } interface ISupportsVolatilePrefix { /// /// Gets/Sets whether the memory access is volatile. /// bool IsVolatile { get; set; } } partial class LdObj { public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { if (options.UseFieldSugar) { if (this.MatchLdFld(out var target, out var field)) { WriteILRange(output, options); output.Write("ldfld "); field.WriteTo(output); output.Write('('); target.WriteTo(output, options); output.Write(')'); return; } else if (this.MatchLdsFld(out field)) { WriteILRange(output, options); output.Write("ldsfld "); field.WriteTo(output); return; } } OriginalWriteTo(output, options); } } partial class StObj { public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { if (options.UseFieldSugar) { if (this.MatchStFld(out var target, out var field, out var value)) { WriteILRange(output, options); output.Write("stfld "); field.WriteTo(output); output.Write('('); target.WriteTo(output, options); output.Write(", "); value.WriteTo(output, options); output.Write(')'); return; } else if (this.MatchStsFld(out field, out value)) { WriteILRange(output, options); output.Write("stsfld "); field.WriteTo(output); output.Write('('); value.WriteTo(output, options); output.Write(')'); return; } } OriginalWriteTo(output, options); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs ================================================ #nullable enable // Copyright (c) 2017 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { /// /// Kind of null-coalescing operator. /// ILAst: if.notnull(valueInst, fallbackInst) /// C#: value ?? fallback /// public enum NullCoalescingKind { /// /// Both ValueInst and FallbackInst are of reference type. /// /// Semantics: equivalent to "valueInst != null ? valueInst : fallbackInst", /// except that valueInst is evaluated only once. /// Ref, /// /// Both ValueInst and FallbackInst are of type Nullable{T}. /// /// Semantics: equivalent to "valueInst.HasValue ? valueInst : fallbackInst", /// except that valueInst is evaluated only once. /// Nullable, /// /// ValueInst is Nullable{T}, but FallbackInst is non-nullable value type. /// /// Semantics: equivalent to "valueInst.HasValue ? valueInst.Value : fallbackInst", /// except that valueInst is evaluated only once. /// NullableWithValueFallback } partial class NullCoalescingInstruction { public readonly NullCoalescingKind Kind; public StackType UnderlyingResultType = StackType.O; public NullCoalescingInstruction(NullCoalescingKind kind, ILInstruction valueInst, ILInstruction fallbackInst) : base(OpCode.NullCoalescingInstruction) { this.Kind = kind; this.ValueInst = valueInst; this.FallbackInst = fallbackInst; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(valueInst.ResultType == StackType.O); // lhs is reference type or nullable type Debug.Assert(fallbackInst.ResultType == StackType.O || Kind == NullCoalescingKind.NullableWithValueFallback); Debug.Assert(ResultType == UnderlyingResultType || Kind == NullCoalescingKind.Nullable); } public override StackType ResultType { get { return fallbackInst.ResultType; } } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } protected override InstructionFlags ComputeFlags() { // valueInst is always executed; fallbackInst only sometimes return InstructionFlags.ControlFlow | valueInst.Flags | SemanticHelper.CombineBranches(InstructionFlags.None, fallbackInst.Flags); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write("("); valueInst.WriteTo(output, options); output.Write(", "); fallbackInst.WriteTo(output, options); output.Write(")"); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs ================================================ #nullable enable // Copyright (c) 2018 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; namespace ICSharpCode.Decompiler.IL { /// /// For a nullable input, gets the underlying value. /// /// There are three possible input types: /// * reference type: if input!=null, evaluates to the input /// * nullable value type: if input.Has_Value, evaluates to input.GetValueOrDefault() /// * generic type: behavior depends on the type at runtime. /// If non-nullable value type, unconditionally evaluates to the input. /// /// If the input is null, control-flow is tranferred to the nearest surrounding nullable.rewrap /// instruction. /// partial class NullableUnwrap { /// /// Whether the argument is dereferenced before checking for a null input. /// If true, the argument must be a managed reference to a valid input type. /// /// /// This mode exists because the C# compiler sometimes avoids copying the whole Nullable{T} struct /// before the null-check. /// The underlying struct T is still copied by the GetValueOrDefault() call, but only in the non-null case. /// public readonly bool RefInput; /// /// Consider the following code generated for t?.Method() on a generic t: /// if (comp(box ``0(ldloc t) != ldnull)) newobj Nullable..ctor(constrained[``0].callvirt Method(ldloca t)) else default.value Nullable /// Here, the method is called on the original reference, and any mutations performed by the method will be visible in the original variable. /// /// To represent this, we use a nullable.unwrap with ResultType==Ref: instead of returning the input value, /// the input reference is returned in the non-null case. /// Note that in case the generic type ends up being Nullable{T}, this means methods will end up being called on /// the nullable type, not on the underlying type. However, this ends up making no difference, because the only methods /// that can be called that way are those on System.Object. All the virtual methods are overridden in Nullable{T} /// and end up forwarding to T; and the non-virtual methods cause boxing which strips the Nullable{T} wrapper. /// /// RefOutput can only be used if RefInput is also used. /// public bool RefOutput { get => ResultType == StackType.Ref; } public NullableUnwrap(StackType unwrappedType, ILInstruction argument, bool refInput = false) : base(OpCode.NullableUnwrap, argument) { this.ResultType = unwrappedType; this.RefInput = refInput; if (unwrappedType == StackType.Ref) { Debug.Assert(refInput); } } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); if (this.RefInput) { Debug.Assert(Argument.ResultType == StackType.Ref, "nullable.unwrap expects reference to nullable type as input"); } else { Debug.Assert(Argument.ResultType == StackType.O, "nullable.unwrap expects nullable type as input"); } Debug.Assert(Ancestors.Any(a => a is NullableRewrap)); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { output.Write("nullable.unwrap."); if (RefInput) { output.Write("refinput."); } output.Write(ResultType); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override StackType ResultType { get; } } partial class NullableRewrap { internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull)); } public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow; protected override InstructionFlags ComputeFlags() { // Convert MayUnwrapNull flag to ControlFlow flag. // Also, remove EndpointUnreachable flag, because the end-point is reachable through // the implicit nullable.unwrap branch. const InstructionFlags flagsToRemove = InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable; return (Argument.Flags & ~flagsToRemove) | InstructionFlags.ControlFlow; } public override StackType ResultType { get { if (Argument.ResultType == StackType.Void) return StackType.Void; else return StackType.O; } } internal override bool PrepareExtract(int childIndex, ExtractionContext ctx) { return base.PrepareExtract(childIndex, ctx) && (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0; } internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { // Inlining into nullable.rewrap is OK unless the expression being inlined // contains a nullable.wrap that isn't being re-wrapped within the expression being inlined. return base.CanInlineIntoSlot(childIndex, expressionBeingMoved) && !expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics.CodeAnalysis; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class ILInstruction { public bool MatchLdcI4(int val) { return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val; } public bool MatchLdcF4(float value) { return MatchLdcF4(out var v) && v == value; } public bool MatchLdcF8(double value) { return MatchLdcF8(out var v) && v == value; } /// /// Matches ldc.i4, ldc.i8, and extending conversions. /// public bool MatchLdcI(out long val) { if (MatchLdcI8(out val)) return true; if (MatchLdcI4(out int intVal)) { val = intVal; return true; } if (this is Conv conv) { if (conv.Kind == ConversionKind.SignExtend) { return conv.Argument.MatchLdcI(out val); } else if (conv.Kind == ConversionKind.ZeroExtend && conv.InputType == StackType.I4) { if (conv.Argument.MatchLdcI(out val)) { // clear top 32 bits val &= uint.MaxValue; return true; } } } return false; } public bool MatchLdcI(long val) { return MatchLdcI(out long v) && v == val; } public bool MatchLdLoc(ILVariable? variable) { var inst = this as LdLoc; return inst != null && inst.Variable == variable; } public bool MatchLdLoca(ILVariable? variable) { var inst = this as LdLoca; return inst != null && inst.Variable == variable; } /// /// Matches either ldloc (if the variable is a reference type), or ldloca (otherwise). /// public bool MatchLdLocRef(ILVariable? variable) { return MatchLdLocRef(out var v) && v == variable; } /// /// Matches either ldloc (if the variable is a reference type), or ldloca (otherwise). /// public bool MatchLdLocRef([NotNullWhen(true)] out ILVariable? variable) { switch (this) { case LdLoc ldloc: variable = ldloc.Variable; return variable.Type.IsReferenceType == true; case LdLoca ldloca: variable = ldloca.Variable; return variable.Type.IsReferenceType != true || variable.Type.Kind == TypeKind.TypeParameter; default: variable = null; return false; } } public bool MatchLdThis() { var inst = this as LdLoc; return inst != null && inst.Variable.Kind == VariableKind.Parameter && inst.Variable.Index < 0; } public bool MatchStLoc([NotNullWhen(true)] out ILVariable? variable) { var inst = this as StLoc; if (inst != null) { variable = inst.Variable; return true; } variable = null; return false; } public bool MatchStLoc(ILVariable? variable, [NotNullWhen(true)] out ILInstruction? value) { var inst = this as StLoc; if (inst != null && inst.Variable == variable) { value = inst.Value; return true; } value = null; return false; } public bool MatchLdLen(StackType type, [NotNullWhen(true)] out ILInstruction? array) { var inst = this as LdLen; if (inst != null && inst.ResultType == type) { array = inst.Array; return true; } array = null; return false; } public bool MatchReturn([NotNullWhen(true)] out ILInstruction? value) { var inst = this as Leave; if (inst != null && inst.IsLeavingFunction) { value = inst.Value; return true; } value = default(ILInstruction); return false; } public bool MatchBranch([NotNullWhen(true)] out Block? targetBlock) { var inst = this as Branch; if (inst != null) { targetBlock = inst.TargetBlock; return true; } targetBlock = null; return false; } public bool MatchBranch(Block? targetBlock) { var inst = this as Branch; return inst != null && inst.TargetBlock == targetBlock; } public bool MatchLeave([NotNullWhen(true)] out BlockContainer? targetContainer, [NotNullWhen(true)] out ILInstruction? value) { var inst = this as Leave; if (inst != null) { targetContainer = inst.TargetContainer; value = inst.Value; return true; } targetContainer = null; value = null; return false; } public bool MatchLeave(BlockContainer? targetContainer, [NotNullWhen(true)] out ILInstruction? value) { var inst = this as Leave; if (inst != null && targetContainer == inst.TargetContainer) { value = inst.Value; return true; } value = null; return false; } public bool MatchLeave([NotNullWhen(true)] out BlockContainer? targetContainer) { var inst = this as Leave; if (inst != null && inst.Value.MatchNop()) { targetContainer = inst.TargetContainer; return true; } targetContainer = null; return false; } public bool MatchLeave(BlockContainer? targetContainer) { var inst = this as Leave; return inst != null && inst.TargetContainer == targetContainer && inst.Value.MatchNop(); } public bool MatchIfInstruction([NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst) { if (this is IfInstruction inst) { condition = inst.Condition; trueInst = inst.TrueInst; falseInst = inst.FalseInst; return true; } condition = null; trueInst = null; falseInst = null; return false; } public bool MatchIfInstructionPositiveCondition([NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst) { if (MatchIfInstruction(out condition, out trueInst, out falseInst)) { // Swap trueInst<>falseInst for every logic.not in the condition. while (condition.MatchLogicNot(out var arg)) { condition = arg; ILInstruction? tmp = trueInst; trueInst = falseInst; falseInst = tmp; } return true; } return false; } /// /// Matches an if instruction where the false instruction is a nop. /// public bool MatchIfInstruction([NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst) { var inst = this as IfInstruction; if (inst != null && inst.FalseInst.MatchNop()) { condition = inst.Condition; trueInst = inst.TrueInst; return true; } condition = null; trueInst = null; return false; } /// /// Matches a 'logic and' instruction ("if (a) b else ldc.i4 0"). /// Note: unlike C# '&&', this instruction is not limited to booleans, /// but allows passing through arbitrary I4 values on the rhs (but not on the lhs). /// public bool MatchLogicAnd([NotNullWhen(true)] out ILInstruction? lhs, [NotNullWhen(true)] out ILInstruction? rhs) { var inst = this as IfInstruction; if (inst != null && inst.FalseInst.MatchLdcI4(0)) { lhs = inst.Condition; rhs = inst.TrueInst; return true; } lhs = null; rhs = null; return false; } /// /// Matches a 'logic or' instruction ("if (a) ldc.i4 1 else b"). /// Note: unlike C# '||', this instruction is not limited to booleans, /// but allows passing through arbitrary I4 values on the rhs (but not on the lhs). /// public bool MatchLogicOr([NotNullWhen(true)] out ILInstruction? lhs, [NotNullWhen(true)] out ILInstruction? rhs) { var inst = this as IfInstruction; if (inst != null && inst.TrueInst.MatchLdcI4(1)) { lhs = inst.Condition; rhs = inst.FalseInst; return true; } lhs = null; rhs = null; return false; } /// /// Matches an logical negation. /// public bool MatchLogicNot([NotNullWhen(true)] out ILInstruction? arg) { if (this is Comp comp && comp.Kind == ComparisonKind.Equality && comp.LiftingKind == ComparisonLiftingKind.None && comp.Right.MatchLdcI4(0)) { arg = comp.Left; return true; } arg = null; return false; } public bool MatchTryCatchHandler([NotNullWhen(true)] out ILVariable? variable) { var inst = this as TryCatchHandler; if (inst != null) { variable = inst.Variable; return true; } variable = null; return false; } /// /// Matches comp(left == right) or logic.not(comp(left != right)). /// public bool MatchCompEquals([NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { ILInstruction thisInst = this; var compKind = ComparisonKind.Equality; while (thisInst.MatchLogicNot(out var arg) && arg is Comp) { thisInst = arg; if (compKind == ComparisonKind.Equality) compKind = ComparisonKind.Inequality; else compKind = ComparisonKind.Equality; } if (thisInst is Comp comp && comp.Kind == compKind && !comp.IsLifted) { left = comp.Left; right = comp.Right; return true; } else { left = null; right = null; return false; } } /// /// Matches 'comp(arg == ldnull)' /// public bool MatchCompEqualsNull([NotNullWhen(true)] out ILInstruction? arg) { if (!MatchCompEquals(out var left, out var right)) { arg = null; return false; } if (right.MatchLdNull()) { arg = left; return true; } else if (left.MatchLdNull()) { arg = right; return true; } else { arg = null; return false; } } /// /// Matches 'comp(arg != ldnull)' /// public bool MatchCompNotEqualsNull([NotNullWhen(true)] out ILInstruction? arg) { if (!MatchCompNotEquals(out var left, out var right)) { arg = null; return false; } if (right.MatchLdNull()) { arg = left; return true; } else if (left.MatchLdNull()) { arg = right; return true; } else { arg = null; return false; } } /// /// Matches comp(left != right) or logic.not(comp(left == right)). /// public bool MatchCompNotEquals([NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { ILInstruction thisInst = this; var compKind = ComparisonKind.Inequality; while (thisInst.MatchLogicNot(out var arg) && arg is Comp) { thisInst = arg; if (compKind == ComparisonKind.Equality) compKind = ComparisonKind.Inequality; else compKind = ComparisonKind.Equality; } if (thisInst is Comp comp && comp.Kind == compKind && !comp.IsLifted) { left = comp.Left; right = comp.Right; return true; } else { left = null; right = null; return false; } } public bool MatchLdFld([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IField? field) { if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) { field = ldflda.Field; if (field.DeclaringType.IsReferenceType == true || !ldflda.Target.MatchAddressOf(out target, out _)) { target = ldflda.Target; } return true; } target = null; field = null; return false; } public bool MatchLdsFld([NotNullWhen(true)] out IField? field) { if (this is LdObj ldobj && ldobj.Target is LdsFlda ldsflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) { field = ldsflda.Field; return true; } field = null; return false; } public bool MatchLdsFld(IField? field) { return MatchLdsFld(out var f) && f.Equals(field); } public bool MatchStsFld([NotNullWhen(true)] out IField? field, [NotNullWhen(true)] out ILInstruction? value) { if (this is StObj stobj && stobj.Target is LdsFlda ldsflda && stobj.UnalignedPrefix == 0 && !stobj.IsVolatile) { field = ldsflda.Field; value = stobj.Value; return true; } field = null; value = null; return false; } public bool MatchStFld([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IField? field, [NotNullWhen(true)] out ILInstruction? value) { if (this is StObj stobj && stobj.Target is LdFlda ldflda && stobj.UnalignedPrefix == 0 && !stobj.IsVolatile) { target = ldflda.Target; field = ldflda.Field; value = stobj.Value; return true; } target = null; field = null; value = null; return false; } public bool MatchBinaryNumericInstruction(BinaryNumericOperator @operator) { var op = this as BinaryNumericInstruction; return op != null && op.Operator == @operator; } public bool MatchBinaryNumericInstruction(BinaryNumericOperator @operator, [NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { var op = this as BinaryNumericInstruction; if (op != null && op.Operator == @operator) { left = op.Left; right = op.Right; return true; } left = null; right = null; return false; } public bool MatchBinaryNumericInstruction(out BinaryNumericOperator @operator, [NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { var op = this as BinaryNumericInstruction; if (op != null) { @operator = op.Operator; left = op.Left; right = op.Right; return true; } @operator = BinaryNumericOperator.None; left = null; right = null; return false; } public bool MatchDefaultOrNullOrZero() { switch (this) { case LdNull _: case LdcF4 { Value: 0 }: case LdcF8 { Value: 0 }: case LdcI4 { Value: 0 }: case LdcI8 { Value: 0 }: case DefaultValue _: return true; default: return false; } } /// /// If this instruction is a conversion of the specified kind, return its argument. /// Otherwise, return the instruction itself. /// /// /// Does not unwrap lifted conversions. /// public virtual ILInstruction UnwrapConv(ConversionKind kind) { return this; } public bool MatchLdObj([NotNullWhen(true)] out ILInstruction? target, IType type) { var inst = this as LdObj; if (inst != null && inst.Type.Equals(type)) { target = inst.Target; return true; } target = default(ILInstruction); return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.IL { /// /// A simple instruction that does not have any arguments. /// public abstract partial class SimpleInstruction : ILInstruction { public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); // the non-custom WriteTo would add useless parentheses } } public enum NopKind { Normal, Pop } partial class Nop { public string? Comment; public NopKind Kind; public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (Kind != NopKind.Normal) { output.Write("." + Kind.ToString().ToLowerInvariant()); } if (!string.IsNullOrEmpty(Comment)) { output.Write(" // " + Comment); } } } partial class InvalidBranch : SimpleInstruction { public string? Message; public StackType ExpectedResultType = StackType.Void; public InvalidBranch(string? message) : this() { this.Message = message; } public override StackType ResultType { get { return ExpectedResultType; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (!string.IsNullOrEmpty(Message)) { output.Write("(\""); output.Write(Message); output.Write("\")"); } } } partial class InvalidExpression : SimpleInstruction { public string Severity = "Error"; public string? Message; public StackType ExpectedResultType = StackType.Unknown; public InvalidExpression(string? message) : this() { this.Message = message; } public override StackType ResultType { get { return ExpectedResultType; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (!string.IsNullOrEmpty(Message)) { output.Write("(\""); output.Write(Message); output.Write("\")"); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/StLoc.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { partial class StLoc { /// /// If true, this stloc represents a stack type adjustment. /// This field is only used in ILReader and BlockBuilder, and should be ignored by ILAst transforms. /// internal bool IsStackAdjustment; /// /// Gets whether the IL stack was empty after this store. /// Only set for store instructions from the IL; not for stores to the stack /// or other stores generated by transforms. /// internal bool ILStackWasEmpty; internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); Debug.Assert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); Debug.Assert(value.ResultType == variable.StackType); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs ================================================ #nullable enable // Copyright (c) 2017 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.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { partial class StringToInt { public List<(string? Key, int Value)> Map { get; } public IType ExpectedType { get; } public StringToInt(ILInstruction argument, List<(string? Key, int Value)> map, IType expectedType) : base(OpCode.StringToInt) { this.Argument = argument; this.Map = map; this.ExpectedType = expectedType; } public StringToInt(ILInstruction argument, string?[] map, IType expectedType) : this(argument, ArrayToDictionary(map), expectedType) { } static List<(string? Key, int Value)> ArrayToDictionary(string?[] map) { var dict = new List<(string? Key, int Value)>(); for (int i = 0; i < map.Length; i++) { dict.Add((map[i], i)); } return dict; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("string.to.int "); ExpectedType.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(", { "); int i = 0; foreach (var entry in Map) { if (i > 0) output.Write(", "); if (entry.Key is null) output.Write($"[null] = {entry.Value}"); else output.Write($"[\"{entry.Key}\"] = {entry.Value}"); i++; } output.Write(" })"); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 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 // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { /// /// Generalization of IL switch-case: like a VB switch over integers, this instruction /// supports integer value ranges as labels. /// /// The section labels are using 'long' as integer type. /// If the Value instruction produces StackType.I4 or I, the value is implicitly sign-extended to I8. /// partial class SwitchInstruction { public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); public static readonly SlotInfo SectionSlot = new SlotInfo("Section", isCollection: true); /// /// If the switch instruction is lifted, the value instruction produces a value of type Nullable{T} for some /// integral type T. The section with SwitchSection.HasNullLabel is called if the value is null. /// public bool IsLifted; /// /// Additional type information used to interpret the value instruction. /// Set by ILInlining to preserve stack information that would otherwise be lost. /// public IType? Type; public SwitchInstruction(ILInstruction value) : base(OpCode.SwitchInstruction) { this.Value = value; this.Sections = new InstructionCollection(this, 1); } ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } public readonly InstructionCollection Sections; protected override InstructionFlags ComputeFlags() { var sectionFlags = InstructionFlags.EndPointUnreachable; // neutral element for CombineBranches() foreach (var section in Sections) { sectionFlags = SemanticHelper.CombineBranches(sectionFlags, section.Flags); } return value.Flags | InstructionFlags.ControlFlow | sectionFlags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("switch"); if (IsLifted) output.Write(".lifted"); output.Write(' '); Type?.WriteTo(output); output.Write('('); value.WriteTo(output, options); output.Write(") "); output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); foreach (var section in this.Sections) { section.WriteTo(output, options); output.WriteLine(); } output.Unindent(); output.Write('}'); output.MarkFoldEnd(); } protected override int GetChildCount() { return 1 + Sections.Count; } protected override ILInstruction GetChild(int index) { if (index == 0) return value; return Sections[index - 1]; } protected override void SetChild(int index, ILInstruction value) { if (index == 0) Value = value; else Sections[index - 1] = (SwitchSection)value; } protected override SlotInfo GetChildSlot(int index) { if (index == 0) return ValueSlot; return SectionSlot; } public override ILInstruction Clone() { var clone = new SwitchInstruction(value.Clone()); clone.AddILRange(this); clone.Value = value.Clone(); clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone())); return clone; } StackType resultType = StackType.Void; public override StackType ResultType => resultType; public void SetResultType(StackType resultType) { this.resultType = resultType; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); bool expectNullSection = this.IsLifted; LongSet sets = LongSet.Empty; foreach (var section in Sections) { if (section.HasNullLabel) { Debug.Assert(expectNullSection, "Duplicate 'case null' or 'case null' in non-lifted switch."); expectNullSection = false; } Debug.Assert(!section.Labels.IsEmpty || section.HasNullLabel); Debug.Assert(!section.Labels.Overlaps(sets)); Debug.Assert(section.Body.ResultType == this.ResultType); sets = sets.UnionWith(section.Labels); } Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases"); Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'"); Debug.Assert(this.IsLifted ? (value.ResultType == StackType.O) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8)); } public SwitchSection GetDefaultSection() { // Pick the section with the most labels as default section. IL.SwitchSection defaultSection = Sections.First(); foreach (var section in Sections) { if (section.Labels.Count() > defaultSection.Labels.Count()) { defaultSection = section; } } return defaultSection; } } partial class SwitchSection { public SwitchSection() : base(OpCode.SwitchSection) { this.Labels = LongSet.Empty; } /// /// If true, serves as 'case null' in a lifted switch. /// public bool HasNullLabel { get; set; } /// /// The set of labels that cause execution to jump to this switch section. /// public LongSet Labels { get; set; } protected override InstructionFlags ComputeFlags() { return body.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.WriteLocalReference("case", this, isDefinition: true); output.Write(' '); if (HasNullLabel) { output.Write("null"); if (!Labels.IsEmpty) { output.Write(", "); output.Write(Labels.ToString()); } } else { output.Write(Labels.ToString()); } output.Write(": "); body.WriteTo(output, options); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { public abstract class TryInstruction : ILInstruction { public static readonly SlotInfo TryBlockSlot = new SlotInfo("TryBlock"); protected TryInstruction(OpCode opCode, ILInstruction tryBlock) : base(opCode) { this.TryBlock = tryBlock; } ILInstruction tryBlock = null!; public ILInstruction TryBlock { get { return this.tryBlock; } set { ValidateChild(value); SetChildInstruction(ref this.tryBlock, value, 0); } } } /// /// Try-catch statement. /// /// /// The return value of the try or catch blocks is ignored, the TryCatch always returns void. /// partial class TryCatch : TryInstruction { public static readonly SlotInfo HandlerSlot = new SlotInfo("Handler", isCollection: true); public readonly InstructionCollection Handlers; public TryCatch(ILInstruction tryBlock) : base(OpCode.TryCatch, tryBlock) { this.Handlers = new InstructionCollection(this, 1); } public override ILInstruction Clone() { var clone = new TryCatch(TryBlock.Clone()); clone.AddILRange(this); clone.Handlers.AddRange(this.Handlers.Select(h => (TryCatchHandler)h.Clone())); return clone; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(".try "); TryBlock.WriteTo(output, options); foreach (var handler in Handlers) { output.Write(' '); handler.WriteTo(output, options); } } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { var flags = TryBlock.Flags; foreach (var handler in Handlers) flags = SemanticHelper.CombineBranches(flags, handler.Flags); return flags | InstructionFlags.ControlFlow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } protected override int GetChildCount() { return 1 + Handlers.Count; } protected override ILInstruction GetChild(int index) { if (index == 0) return TryBlock; else return Handlers[index - 1]; } protected override void SetChild(int index, ILInstruction value) { if (index == 0) TryBlock = value; else Handlers[index - 1] = (TryCatchHandler)value; } protected override SlotInfo GetChildSlot(int index) { if (index == 0) return TryBlockSlot; else return HandlerSlot; } } /// /// Catch handler within a try-catch statement. /// /// When an exception occurs in the try block of the parent try.catch statement, the runtime searches /// the nearest enclosing TryCatchHandler with a matching variable type and /// assigns the exception object to the , and executes the . /// If the filter evaluates to 0, the exception is not caught and the runtime looks for the next catch handler. /// If the filter evaluates to 1, the stack is unwound, the exception caught and assigned to the , /// and the is executed. /// partial class TryCatchHandler { internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(Parent is TryCatch); Debug.Assert(filter.ResultType == StackType.I4); Debug.Assert(this.IsDescendantOf(variable.Function!)); } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return filter.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals; } public override InstructionFlags DirectFlags { get { // the body is not evaluated if the filter returns 0 return InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("catch "); if (variable != null) { output.WriteLocalReference(variable.Name, variable, isDefinition: true); output.Write(" : "); Disassembler.DisassemblerHelpers.WriteOperand(output, variable.Type); } output.Write(" when ("); filter.WriteTo(output, options); output.Write(')'); output.Write(' '); body.WriteTo(output, options); } /// /// Gets the ILRange of the instructions at the start of the catch-block, /// that take the exception object and store it in the exception variable slot. /// Note: This range is empty, if Filter is not empty, i.e., ldloc 1. /// public Interval ExceptionSpecifierILRange { get; private set; } public void AddExceptionSpecifierILRange(Interval newRange) { ExceptionSpecifierILRange = CombineILRange(ExceptionSpecifierILRange, newRange); } } partial class TryFinally { public static readonly SlotInfo FinallyBlockSlot = new SlotInfo("FinallyBlock"); public TryFinally(ILInstruction tryBlock, ILInstruction finallyBlock) : base(OpCode.TryFinally, tryBlock) { this.FinallyBlock = finallyBlock; } ILInstruction finallyBlock = null!; public ILInstruction FinallyBlock { get { return this.finallyBlock; } set { ValidateChild(value); SetChildInstruction(ref this.finallyBlock, value, 1); } } public override ILInstruction Clone() { return new TryFinally(TryBlock.Clone(), finallyBlock.Clone()).WithILRange(this); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(".try "); TryBlock.WriteTo(output, options); output.Write(" finally "); finallyBlock.WriteTo(output, options); } public override StackType ResultType { get { return TryBlock.ResultType; } } protected override InstructionFlags ComputeFlags() { // if the endpoint of either the try or the finally is unreachable, the endpoint of the try-finally will be unreachable return TryBlock.Flags | finallyBlock.Flags | InstructionFlags.ControlFlow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } protected override int GetChildCount() { return 2; } protected override ILInstruction GetChild(int index) { switch (index) { case 0: return TryBlock; case 1: return finallyBlock; default: throw new IndexOutOfRangeException(); } } protected override void SetChild(int index, ILInstruction value) { switch (index) { case 0: TryBlock = value; break; case 1: FinallyBlock = value; break; default: throw new IndexOutOfRangeException(); } } protected override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TryBlockSlot; case 1: return FinallyBlockSlot; default: throw new IndexOutOfRangeException(); } } } partial class TryFault { public static readonly SlotInfo FaultBlockSlot = new SlotInfo("FaultBlock"); public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFinally, tryBlock) { this.FaultBlock = faultBlock; } ILInstruction faultBlock = null!; public ILInstruction FaultBlock { get { return this.faultBlock; } set { ValidateChild(value); SetChildInstruction(ref this.faultBlock, value, 1); } } public override ILInstruction Clone() { return new TryFault(TryBlock.Clone(), faultBlock.Clone()).WithILRange(this); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(".try "); TryBlock.WriteTo(output, options); output.Write(" fault "); faultBlock.WriteTo(output, options); } public override StackType ResultType { get { return TryBlock.ResultType; } } protected override InstructionFlags ComputeFlags() { // The endpoint of the try-fault is unreachable iff the try endpoint is unreachable return TryBlock.Flags | (faultBlock.Flags & ~InstructionFlags.EndPointUnreachable) | InstructionFlags.ControlFlow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow; } } protected override int GetChildCount() { return 2; } protected override ILInstruction GetChild(int index) { switch (index) { case 0: return TryBlock; case 1: return faultBlock; default: throw new IndexOutOfRangeException(); } } protected override void SetChild(int index, ILInstruction value) { switch (index) { case 0: TryBlock = value; break; case 1: FaultBlock = value; break; default: throw new IndexOutOfRangeException(); } } protected override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TryBlockSlot; case 1: return FaultBlockSlot; default: throw new IndexOutOfRangeException(); } } } public partial class Throw { internal StackType resultType = StackType.Void; } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Diagnostics; namespace ICSharpCode.Decompiler.IL { partial class BitNot : ILiftableInstruction { public BitNot(ILInstruction arg) : base(OpCode.BitNot, arg) { this.UnderlyingResultType = arg.ResultType; } public BitNot(ILInstruction arg, bool isLifted, StackType stackType) : base(OpCode.BitNot, arg) { this.IsLifted = isLifted; this.UnderlyingResultType = stackType; } public bool IsLifted { get; } public StackType UnderlyingResultType { get; } public override StackType ResultType { get { return Argument.ResultType; } } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); Debug.Assert(IsLifted == (ResultType == StackType.O)); Debug.Assert(IsLifted || ResultType == UnderlyingResultType); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (IsLifted) { output.Write(".lifted"); } output.Write('('); this.Argument.WriteTo(output, options); output.Write(')'); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs ================================================ #nullable enable // Copyright (c) 2017 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. namespace ICSharpCode.Decompiler.IL { /// /// IL using instruction. /// Equivalent to: /// /// stloc v(resourceExpression) /// try { /// body /// } finally { /// v?.Dispose(); /// } /// /// /// /// The value of v is undefined after the end of the body block. /// partial class UsingInstruction { public bool IsAsync { get; set; } public bool IsRefStruct { get; set; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write("using"); if (IsAsync) { output.Write(".async"); } if (IsRefStruct) { output.Write(".ref"); } output.Write(" ("); Variable.WriteTo(output); output.Write(" = "); ResourceExpression.WriteTo(output, options); output.WriteLine(") {"); output.Indent(); Body.WriteTo(output, options); output.Unindent(); output.WriteLine(); output.Write("}"); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions.cs ================================================ // Copyright (c) 2014-2020 Daniel Grunwald // // 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. #nullable enable using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { /// /// Enum representing the type of an . /// public enum OpCode : byte { /// Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception. InvalidBranch, /// Represents invalid IL. Semantically, this instruction is considered to produce some kind of value. InvalidExpression, /// No operation. Takes 0 arguments and returns void. Nop, /// A container of IL blocks. ILFunction, /// A container of IL blocks. BlockContainer, /// A block of IL instructions. Block, /// A region where a pinned variable is used (initial representation of future fixed statement). PinnedRegion, /// Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr. BinaryNumericInstruction, /// Common instruction for numeric compound assignments. NumericCompoundAssign, /// Common instruction for user-defined compound assignments. UserDefinedCompoundAssign, /// Common instruction for dynamic compound assignments. DynamicCompoundAssign, /// Bitwise NOT BitNot, /// Retrieves the RuntimeArgumentHandle. Arglist, /// Unconditional branch. goto target; Branch, /// Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction. Leave, /// If statement / conditional expression. if (condition) trueExpr else falseExpr IfInstruction, /// Null coalescing operator expression. if.notnull(valueInst, fallbackInst) NullCoalescingInstruction, /// Switch statement SwitchInstruction, /// Switch section within a switch statement SwitchSection, /// Try-catch statement. TryCatch, /// Catch handler within a try-catch statement. TryCatchHandler, /// Try-finally statement TryFinally, /// Try-fault statement TryFault, /// Lock statement LockInstruction, /// Using statement UsingInstruction, /// Breakpoint instruction DebugBreak, /// Comparison. The inputs must be both integers; or both floats; or both object references. Object references can only be compared for equality or inequality. Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which evaluates to 1 (true). Comp, /// Non-virtual method call. Call, /// Virtual method call. CallVirt, /// Unsafe function pointer call. CallIndirect, /// Checks that the input float is not NaN or infinite. Ckfinite, /// Numeric cast. Conv, /// Loads the value of a local variable. (ldarg/ldloc) LdLoc, /// Loads the address of a local variable. (ldarga/ldloca) LdLoca, /// Stores a value into a local variable. (IL: starg/stloc) /// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign()) StLoc, /// Stores the value into an anonymous temporary variable, and returns the address of that variable. AddressOf, /// Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior. ThreeValuedBoolAnd, /// Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior. ThreeValuedBoolOr, /// The input operand must be one of: /// 1. a nullable value type /// 2. a reference type /// 3. a managed reference to a type parameter. /// If the input is non-null, evaluates to the (unwrapped) input. /// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction. /// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null). NullableUnwrap, /// Serves as jump target for the nullable.unwrap instruction. /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. NullableRewrap, /// Loads a constant string. LdStr, /// Loads a constant byte string (as ReadOnlySpan<byte>). LdStrUtf8, /// Loads a constant 32-bit integer. LdcI4, /// Loads a constant 64-bit integer. LdcI8, /// Loads a constant 32-bit floating-point number. LdcF4, /// Loads a constant 64-bit floating-point number. LdcF8, /// Loads a constant decimal. LdcDecimal, /// Loads the null reference. LdNull, /// Load method pointer LdFtn, /// Load method pointer LdVirtFtn, /// Virtual delegate construction LdVirtDelegate, /// Loads runtime representation of metadata token LdTypeToken, /// Loads runtime representation of metadata token LdMemberToken, /// Allocates space in the stack frame LocAlloc, /// Allocates space in the stack frame and wraps it in a Span LocAllocSpan, /// memcpy(destAddress, sourceAddress, size); Cpblk, /// memset(address, value, size) Initblk, /// Load address of instance field LdFlda, /// Load static field address LdsFlda, /// Casts an object to a class. CastClass, /// Test if object is instance of class or interface. IsInst, /// Indirect load (ref/pointer dereference). LdObj, /// If argument is a ref to a reference type, loads the object reference, stores it in a temporary, and evaluates to the address of that temporary (address.of(ldobj(arg))). Otherwise, returns the argument ref as-is.This instruction represents the memory-load semantics of callvirt with a generic type as receiver (where the IL always takes a ref, but only methods on value types expect one, for method on reference types there's an implicit ldobj, which this instruction makes explicit in order to preserve the order-of-evaluation). LdObjIfRef, /// Indirect store (store to ref/pointer). /// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign()) StObj, /// Boxes a value. Box, /// Compute address inside box. Unbox, /// Unbox a value. UnboxAny, /// Creates an object instance and calls the constructor. NewObj, /// Creates an array instance. NewArr, /// Returns the default value for a type. DefaultValue, /// Throws an exception. Throw, /// Rethrows the current exception. Rethrow, /// Gets the size of a type in bytes. SizeOf, /// Returns the length of an array as 'native unsigned int'. LdLen, /// Load address of array element. LdElema, /// Load address of inline array element. LdElemaInlineArray, /// Retrieves a pinnable reference for the input object. /// The input must be an object reference (O). /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty. /// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null. /// GetPinnableReference, /// Maps a string value to an integer. This is used in switch(string). StringToInt, /// ILAst representation of Expression.Convert. ExpressionTreeCast, /// Use of user-defined && or || operator. UserDefinedLogicOperator, /// ILAst representation of a short-circuiting binary operator inside a dynamic expression. DynamicLogicOperatorInstruction, /// ILAst representation of a binary operator inside a dynamic expression (maps to Binder.BinaryOperation). DynamicBinaryOperatorInstruction, /// ILAst representation of a unary operator inside a dynamic expression (maps to Binder.UnaryOperation). DynamicUnaryOperatorInstruction, /// ILAst representation of a cast inside a dynamic expression (maps to Binder.Convert). DynamicConvertInstruction, /// ILAst representation of a property get method call inside a dynamic expression (maps to Binder.GetMember). DynamicGetMemberInstruction, /// ILAst representation of a property set method call inside a dynamic expression (maps to Binder.SetMember). DynamicSetMemberInstruction, /// ILAst representation of an indexer get method call inside a dynamic expression (maps to Binder.GetIndex). DynamicGetIndexInstruction, /// ILAst representation of an indexer set method call inside a dynamic expression (maps to Binder.SetIndex). DynamicSetIndexInstruction, /// ILAst representation of a method call inside a dynamic expression (maps to Binder.InvokeMember). DynamicInvokeMemberInstruction, /// ILAst representation of a constuctor invocation inside a dynamic expression (maps to Binder.InvokeConstructor). DynamicInvokeConstructorInstruction, /// ILAst representation of a delegate invocation inside a dynamic expression (maps to Binder.Invoke). DynamicInvokeInstruction, /// ILAst representation of a call to the Binder.IsEvent method inside a dynamic expression. DynamicIsEventInstruction, /// ILAst representation of C# patterns MatchInstruction, /// Push a typed reference of type class onto the stack. MakeRefAny, /// Push the type token stored in a typed reference. RefAnyType, /// Push the address stored in a typed reference. RefAnyValue, /// Yield an element from an iterator. YieldReturn, /// C# await operator. Await, /// Deconstruction statement DeconstructInstruction, /// Represents a deconstructed value DeconstructResultInstruction, /// Matches any node AnyNode, } } namespace ICSharpCode.Decompiler.IL { /// Instruction without any arguments public abstract partial class SimpleInstruction : ILInstruction { protected SimpleInstruction(OpCode opCode) : base(opCode) { } protected sealed override int GetChildCount() { return 0; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (SimpleInstruction)ShallowClone(); return clone; } protected override InstructionFlags ComputeFlags() { return InstructionFlags.None; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } } } namespace ICSharpCode.Decompiler.IL { /// Instruction with a single argument public abstract partial class UnaryInstruction : ILInstruction { protected UnaryInstruction(OpCode opCode, ILInstruction argument) : base(opCode) { this.Argument = argument; } public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); ILInstruction argument = null!; public ILInstruction Argument { get { return this.argument; } set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.argument; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Argument = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArgumentSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (UnaryInstruction)ShallowClone(); clone.Argument = this.argument.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return argument.Flags | InstructionFlags.None; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); this.argument.WriteTo(output, options); output.Write(')'); } } } namespace ICSharpCode.Decompiler.IL { /// Instruction with two arguments: Left and Right public abstract partial class BinaryInstruction : ILInstruction { protected BinaryInstruction(OpCode opCode, ILInstruction left, ILInstruction right) : base(opCode) { this.Left = left; this.Right = right; } public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true); ILInstruction left = null!; public ILInstruction Left { get { return this.left; } set { ValidateChild(value); SetChildInstruction(ref this.left, value, 0); } } public static readonly SlotInfo RightSlot = new SlotInfo("Right", canInlineInto: true); ILInstruction right = null!; public ILInstruction Right { get { return this.right; } set { ValidateChild(value); SetChildInstruction(ref this.right, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.left; case 1: return this.right; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Left = value; break; case 1: this.Right = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return LeftSlot; case 1: return RightSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (BinaryInstruction)ShallowClone(); clone.Left = this.left.Clone(); clone.Right = this.right.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return left.Flags | right.Flags | InstructionFlags.None; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); this.left.WriteTo(output, options); output.Write(", "); this.right.WriteTo(output, options); output.Write(')'); } } } namespace ICSharpCode.Decompiler.IL { /// Instruction with a list of arguments. public abstract partial class CallInstruction : ILInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (CallInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } } } namespace ICSharpCode.Decompiler.IL.Patterns { /// Base class for pattern matching in ILAst. public abstract partial class PatternInstruction : ILInstruction { protected PatternInstruction(OpCode opCode) : base(opCode) { } public override StackType ResultType { get { return StackType.Unknown; } } } } namespace ICSharpCode.Decompiler.IL { /// Common instruction for compound assignments. public abstract partial class CompoundAssignmentInstruction : ILInstruction { public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; case 1: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; case 1: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; case 1: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (CompoundAssignmentInstruction)ShallowClone(); clone.Target = this.target.Clone(); clone.Value = this.value.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return target.Flags | value.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); this.target.WriteTo(output, options); output.Write(", "); this.value.WriteTo(output, options); output.Write(')'); } } } namespace ICSharpCode.Decompiler.IL { /// Instruction representing a dynamic call site. public abstract partial class DynamicInstruction : ILInstruction { protected DynamicInstruction(OpCode opCode) : base(opCode) { } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } } } namespace ICSharpCode.Decompiler.IL { /// Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception. public sealed partial class InvalidBranch : SimpleInstruction { public InvalidBranch() : base(OpCode.InvalidBranch) { } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayThrow | InstructionFlags.SideEffect | InstructionFlags.EndPointUnreachable; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect | InstructionFlags.EndPointUnreachable; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitInvalidBranch(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitInvalidBranch(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitInvalidBranch(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as InvalidBranch; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Represents invalid IL. Semantically, this instruction is considered to produce some kind of value. public sealed partial class InvalidExpression : SimpleInstruction { public InvalidExpression() : base(OpCode.InvalidExpression) { } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitInvalidExpression(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitInvalidExpression(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitInvalidExpression(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as InvalidExpression; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// No operation. Takes 0 arguments and returns void. public sealed partial class Nop : SimpleInstruction { public Nop() : base(OpCode.Nop) { } public override StackType ResultType { get { return StackType.Void; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNop(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNop(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNop(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Nop; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// A container of IL blocks. public sealed partial class ILFunction : ILInstruction { public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 0); } } public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions"); public InstructionCollection LocalFunctions { get; private set; } protected sealed override int GetChildCount() { return 1 + LocalFunctions.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.body; default: return this.LocalFunctions[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Body = value; break; default: this.LocalFunctions[index - 1] = (ILFunction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return BodySlot; default: return LocalFunctionsSlot; } } public sealed override ILInstruction Clone() { var clone = (ILFunction)ShallowClone(); clone.Body = this.body.Clone(); clone.LocalFunctions = new InstructionCollection(clone, 1); clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone())); clone.CloneVariables(); return clone; } public override StackType ResultType { get { return DelegateType?.GetStackType() ?? StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitILFunction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitILFunction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitILFunction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as ILFunction; return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// A container of IL blocks. public sealed partial class BlockContainer : ILInstruction { public override StackType ResultType { get { return this.ExpectedResultType; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBlockContainer(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBlockContainer(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBlockContainer(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as BlockContainer; return o != null && Patterns.ListMatch.DoMatch(this.Blocks, o.Blocks, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// A block of IL instructions. public sealed partial class Block : ILInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBlock(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBlock(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBlock(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Block; return o != null && this.Kind == o.Kind && Patterns.ListMatch.DoMatch(this.Instructions, o.Instructions, ref match) && this.FinalInstruction.PerformMatch(o.FinalInstruction, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// A region where a pinned variable is used (initial representation of future fixed statement). public sealed partial class PinnedRegion : ILInstruction, IStoreInstruction { public PinnedRegion(ILVariable variable, ILInstruction init, ILInstruction body) : base(OpCode.PinnedRegion) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); this.Init = init; this.Body = body; } public override StackType ResultType { get { return StackType.Void; } } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) variable.AddStoreInstruction(this); } } public int IndexInStoreInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddStoreInstruction(this); } protected override void Disconnected() { variable.RemoveStoreInstruction(this); base.Disconnected(); } public static readonly SlotInfo InitSlot = new SlotInfo("Init", canInlineInto: true); ILInstruction init = null!; public ILInstruction Init { get { return this.init; } set { ValidateChild(value); SetChildInstruction(ref this.init, value, 0); } } public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.init; case 1: return this.body; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Init = value; break; case 1: this.Body = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return InitSlot; case 1: return BodySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (PinnedRegion)ShallowClone(); clone.Init = this.init.Clone(); clone.Body = this.body.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | init.Flags | body.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayWriteLocals; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); variable.WriteTo(output); output.Write('('); this.init.WriteTo(output, options); output.Write(", "); this.body.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitPinnedRegion(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitPinnedRegion(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitPinnedRegion(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as PinnedRegion; return o != null && variable == o.variable && this.init.PerformMatch(o.init, ref match) && this.body.PerformMatch(o.body, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); DebugAssert(Variable.Kind == VariableKind.PinnedRegionLocal); } } } namespace ICSharpCode.Decompiler.IL { /// Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr. public sealed partial class BinaryNumericInstruction : BinaryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBinaryNumericInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBinaryNumericInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBinaryNumericInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as BinaryNumericInstruction; return o != null && this.Left.PerformMatch(o.Left, ref match) && this.Right.PerformMatch(o.Right, ref match) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted; } } } namespace ICSharpCode.Decompiler.IL { /// Common instruction for numeric compound assignments. public sealed partial class NumericCompoundAssign : CompoundAssignmentInstruction { IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return type.GetStackType(); } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNumericCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNumericCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNumericCompoundAssign(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NumericCompoundAssign; return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Common instruction for user-defined compound assignments. public sealed partial class UserDefinedCompoundAssign : CompoundAssignmentInstruction { protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitUserDefinedCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitUserDefinedCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitUserDefinedCompoundAssign(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as UserDefinedCompoundAssign; return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Common instruction for dynamic compound assignments. public sealed partial class DynamicCompoundAssign : CompoundAssignmentInstruction { public override StackType ResultType { get { return StackType.O; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicCompoundAssign(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicCompoundAssign(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicCompoundAssign; return o != null && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Bitwise NOT public sealed partial class BitNot : UnaryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBitNot(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBitNot(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBitNot(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as BitNot; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType; } } } namespace ICSharpCode.Decompiler.IL { /// Retrieves the RuntimeArgumentHandle. public sealed partial class Arglist : SimpleInstruction { public Arglist() : base(OpCode.Arglist) { } public override StackType ResultType { get { return StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitArglist(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitArglist(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitArglist(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Arglist; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Unconditional branch. goto target; public sealed partial class Branch : SimpleInstruction { public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch; } public override InstructionFlags DirectFlags { get { return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBranch(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBranch(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBranch(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Branch; return o != null && this.TargetBlock == o.TargetBlock; } } } namespace ICSharpCode.Decompiler.IL { /// Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction. public sealed partial class Leave : ILInstruction { public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (Leave)ShallowClone(); clone.Value = this.value.Clone(); return clone; } public override StackType ResultType { get { return StackType.Void; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLeave(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLeave(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLeave(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Leave; return o != null && this.value.PerformMatch(o.value, ref match) && this.TargetContainer == o.TargetContainer; } } } namespace ICSharpCode.Decompiler.IL { /// If statement / conditional expression. if (condition) trueExpr else falseExpr public sealed partial class IfInstruction : ILInstruction { public static readonly SlotInfo ConditionSlot = new SlotInfo("Condition", canInlineInto: true); ILInstruction condition = null!; public ILInstruction Condition { get { return this.condition; } set { ValidateChild(value); SetChildInstruction(ref this.condition, value, 0); } } public static readonly SlotInfo TrueInstSlot = new SlotInfo("TrueInst"); ILInstruction trueInst = null!; public ILInstruction TrueInst { get { return this.trueInst; } set { ValidateChild(value); SetChildInstruction(ref this.trueInst, value, 1); } } public static readonly SlotInfo FalseInstSlot = new SlotInfo("FalseInst"); ILInstruction falseInst = null!; public ILInstruction FalseInst { get { return this.falseInst; } set { ValidateChild(value); SetChildInstruction(ref this.falseInst, value, 2); } } protected sealed override int GetChildCount() { return 3; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.condition; case 1: return this.trueInst; case 2: return this.falseInst; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Condition = value; break; case 1: this.TrueInst = value; break; case 2: this.FalseInst = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ConditionSlot; case 1: return TrueInstSlot; case 2: return FalseInstSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (IfInstruction)ShallowClone(); clone.Condition = this.condition.Clone(); clone.TrueInst = this.trueInst.Clone(); clone.FalseInst = this.falseInst.Clone(); return clone; } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitIfInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitIfInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitIfInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as IfInstruction; return o != null && this.condition.PerformMatch(o.condition, ref match) && this.trueInst.PerformMatch(o.trueInst, ref match) && this.falseInst.PerformMatch(o.falseInst, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Null coalescing operator expression. if.notnull(valueInst, fallbackInst) public sealed partial class NullCoalescingInstruction : ILInstruction { public static readonly SlotInfo ValueInstSlot = new SlotInfo("ValueInst", canInlineInto: true); ILInstruction valueInst = null!; public ILInstruction ValueInst { get { return this.valueInst; } set { ValidateChild(value); SetChildInstruction(ref this.valueInst, value, 0); } } public static readonly SlotInfo FallbackInstSlot = new SlotInfo("FallbackInst"); ILInstruction fallbackInst = null!; public ILInstruction FallbackInst { get { return this.fallbackInst; } set { ValidateChild(value); SetChildInstruction(ref this.fallbackInst, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.valueInst; case 1: return this.fallbackInst; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.ValueInst = value; break; case 1: this.FallbackInst = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueInstSlot; case 1: return FallbackInstSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (NullCoalescingInstruction)ShallowClone(); clone.ValueInst = this.valueInst.Clone(); clone.FallbackInst = this.fallbackInst.Clone(); return clone; } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNullCoalescingInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNullCoalescingInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNullCoalescingInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NullCoalescingInstruction; return o != null && this.valueInst.PerformMatch(o.valueInst, ref match) && this.fallbackInst.PerformMatch(o.fallbackInst, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Switch statement public sealed partial class SwitchInstruction : ILInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitSwitchInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitSwitchInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitSwitchInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as SwitchInstruction; return o != null && IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Switch section within a switch statement public sealed partial class SwitchSection : ILInstruction { public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.body; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Body = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return BodySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (SwitchSection)ShallowClone(); clone.Body = this.body.Clone(); return clone; } public override StackType ResultType { get { return StackType.Void; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitSwitchSection(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitSwitchSection(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitSwitchSection(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as SwitchSection; return o != null && this.body.PerformMatch(o.body, ref match) && this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel; } } } namespace ICSharpCode.Decompiler.IL { /// Try-catch statement. public sealed partial class TryCatch : TryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitTryCatch(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitTryCatch(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitTryCatch(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as TryCatch; return o != null && TryBlock.PerformMatch(o.TryBlock, ref match) && Patterns.ListMatch.DoMatch(Handlers, o.Handlers, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Catch handler within a try-catch statement. public sealed partial class TryCatchHandler : ILInstruction, IStoreInstruction { public TryCatchHandler(ILInstruction filter, ILInstruction body, ILVariable variable) : base(OpCode.TryCatchHandler) { this.Filter = filter; this.Body = body; this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); } public static readonly SlotInfo FilterSlot = new SlotInfo("Filter"); ILInstruction filter = null!; public ILInstruction Filter { get { return this.filter; } set { ValidateChild(value); SetChildInstruction(ref this.filter, value, 0); } } public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.filter; case 1: return this.body; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Filter = value; break; case 1: this.Body = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return FilterSlot; case 1: return BodySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (TryCatchHandler)ShallowClone(); clone.Filter = this.filter.Clone(); clone.Body = this.body.Clone(); return clone; } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) variable.AddStoreInstruction(this); } } public int IndexInStoreInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddStoreInstruction(this); } protected override void Disconnected() { variable.RemoveStoreInstruction(this); base.Disconnected(); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitTryCatchHandler(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitTryCatchHandler(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitTryCatchHandler(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as TryCatchHandler; return o != null && this.filter.PerformMatch(o.filter, ref match) && this.body.PerformMatch(o.body, ref match) && variable == o.variable; } } } namespace ICSharpCode.Decompiler.IL { /// Try-finally statement public sealed partial class TryFinally : TryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitTryFinally(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitTryFinally(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitTryFinally(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as TryFinally; return o != null && TryBlock.PerformMatch(o.TryBlock, ref match) && finallyBlock.PerformMatch(o.finallyBlock, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Try-fault statement public sealed partial class TryFault : TryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitTryFault(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitTryFault(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitTryFault(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as TryFault; return o != null && TryBlock.PerformMatch(o.TryBlock, ref match) && faultBlock.PerformMatch(o.faultBlock, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Lock statement public sealed partial class LockInstruction : ILInstruction { public LockInstruction(ILInstruction onExpression, ILInstruction body) : base(OpCode.LockInstruction) { this.OnExpression = onExpression; this.Body = body; } public static readonly SlotInfo OnExpressionSlot = new SlotInfo("OnExpression", canInlineInto: true); ILInstruction onExpression = null!; public ILInstruction OnExpression { get { return this.onExpression; } set { ValidateChild(value); SetChildInstruction(ref this.onExpression, value, 0); } } public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.onExpression; case 1: return this.body; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.OnExpression = value; break; case 1: this.Body = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return OnExpressionSlot; case 1: return BodySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (LockInstruction)ShallowClone(); clone.OnExpression = this.onExpression.Clone(); clone.Body = this.body.Clone(); return clone; } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return onExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.ControlFlow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLockInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLockInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLockInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LockInstruction; return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(onExpression.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// Using statement public sealed partial class UsingInstruction : ILInstruction, IStoreInstruction { public UsingInstruction(ILVariable variable, ILInstruction resourceExpression, ILInstruction body) : base(OpCode.UsingInstruction) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); this.ResourceExpression = resourceExpression; this.Body = body; } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) variable.AddStoreInstruction(this); } } public int IndexInStoreInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddStoreInstruction(this); } protected override void Disconnected() { variable.RemoveStoreInstruction(this); base.Disconnected(); } public static readonly SlotInfo ResourceExpressionSlot = new SlotInfo("ResourceExpression", canInlineInto: true); ILInstruction resourceExpression = null!; public ILInstruction ResourceExpression { get { return this.resourceExpression; } set { ValidateChild(value); SetChildInstruction(ref this.resourceExpression, value, 0); } } public static readonly SlotInfo BodySlot = new SlotInfo("Body"); ILInstruction body = null!; public ILInstruction Body { get { return this.body; } set { ValidateChild(value); SetChildInstruction(ref this.body, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.resourceExpression; case 1: return this.body; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.ResourceExpression = value; break; case 1: this.Body = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ResourceExpressionSlot; case 1: return BodySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (UsingInstruction)ShallowClone(); clone.ResourceExpression = this.resourceExpression.Clone(); clone.Body = this.body.Clone(); return clone; } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | resourceExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayWriteLocals | InstructionFlags.ControlFlow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitUsingInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitUsingInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitUsingInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as UsingInstruction; return o != null && variable == o.variable && this.resourceExpression.PerformMatch(o.resourceExpression, ref match) && this.body.PerformMatch(o.body, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); DebugAssert(resourceExpression.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// Breakpoint instruction public sealed partial class DebugBreak : SimpleInstruction { public DebugBreak() : base(OpCode.DebugBreak) { } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDebugBreak(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDebugBreak(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDebugBreak(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DebugBreak; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Comparison. The inputs must be both integers; or both floats; or both object references. Object references can only be compared for equality or inequality. Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which evaluates to 1 (true). public sealed partial class Comp : BinaryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitComp(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitComp(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitComp(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Comp; return o != null && this.Left.PerformMatch(o.Left, ref match) && this.Right.PerformMatch(o.Right, ref match) && this.Kind == o.Kind && this.Sign == o.Sign && this.LiftingKind == o.LiftingKind; } } } namespace ICSharpCode.Decompiler.IL { /// Non-virtual method call. public sealed partial class Call : CallInstruction { public Call(IMethod method) : base(OpCode.Call, method) { } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCall(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCall(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCall(this, context); } } } namespace ICSharpCode.Decompiler.IL { /// Virtual method call. public sealed partial class CallVirt : CallInstruction { public CallVirt(IMethod method) : base(OpCode.CallVirt, method) { } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCallVirt(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCallVirt(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCallVirt(this, context); } } } namespace ICSharpCode.Decompiler.IL { /// Unsafe function pointer call. public sealed partial class CallIndirect : ILInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCallIndirect(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCallIndirect(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCallIndirect(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as CallIndirect; return o != null && EqualSignature(o) && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match) && this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Checks that the input float is not NaN or infinite. public sealed partial class Ckfinite : UnaryInstruction { public Ckfinite(ILInstruction argument) : base(OpCode.Ckfinite, argument) { } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCkfinite(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCkfinite(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCkfinite(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Ckfinite; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Numeric cast. public sealed partial class Conv : UnaryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitConv(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitConv(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitConv(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Conv; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && CheckForOverflow == o.CheckForOverflow && Kind == o.Kind && InputSign == o.InputSign && TargetType == o.TargetType && IsLifted == o.IsLifted; } } } namespace ICSharpCode.Decompiler.IL { /// Loads the value of a local variable. (ldarg/ldloc) public sealed partial class LdLoc : SimpleInstruction, ILoadInstruction { public LdLoc(ILVariable variable) : base(OpCode.LdLoc) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveLoadInstruction(this); variable = value; if (IsConnected) variable.AddLoadInstruction(this); } } public int IndexInLoadInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((ILoadInstruction)this).IndexInLoadInstructionList; } set { ((ILoadInstruction)this).IndexInLoadInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddLoadInstruction(this); } protected override void Disconnected() { variable.RemoveLoadInstruction(this); base.Disconnected(); } public override StackType ResultType { get { return variable.StackType; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayReadLocals; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayReadLocals; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); variable.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdLoc(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdLoc(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdLoc(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdLoc; return o != null && variable == o.variable; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); } } } namespace ICSharpCode.Decompiler.IL { /// Loads the address of a local variable. (ldarga/ldloca) public sealed partial class LdLoca : SimpleInstruction, IAddressInstruction { public LdLoca(ILVariable variable) : base(OpCode.LdLoca) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); } public override StackType ResultType { get { return StackType.Ref; } } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveAddressInstruction(this); variable = value; if (IsConnected) variable.AddAddressInstruction(this); } } public int IndexInAddressInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IAddressInstruction)this).IndexInAddressInstructionList; } set { ((IAddressInstruction)this).IndexInAddressInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddAddressInstruction(this); } protected override void Disconnected() { variable.RemoveAddressInstruction(this); base.Disconnected(); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); variable.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdLoca(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdLoca(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdLoca(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdLoca; return o != null && variable == o.variable; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); } } } namespace ICSharpCode.Decompiler.IL { /// Stores a value into a local variable. (IL: starg/stloc) /// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign()) public sealed partial class StLoc : ILInstruction, IStoreInstruction { public StLoc(ILVariable variable, ILInstruction value) : base(OpCode.StLoc) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); this.Value = value; } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) variable.AddStoreInstruction(this); } } public int IndexInStoreInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddStoreInstruction(this); } protected override void Disconnected() { variable.RemoveStoreInstruction(this); base.Disconnected(); } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (StLoc)ShallowClone(); clone.Value = this.value.Clone(); return clone; } public override StackType ResultType { get { return variable.StackType; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | value.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayWriteLocals; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); variable.WriteTo(output); output.Write('('); this.value.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitStLoc(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitStLoc(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitStLoc(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as StLoc; return o != null && variable == o.variable && this.value.PerformMatch(o.value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Stores the value into an anonymous temporary variable, and returns the address of that variable. public sealed partial class AddressOf : ILInstruction { public AddressOf(ILInstruction value, IType type) : base(OpCode.AddressOf) { this.Value = value; this.type = type; } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (AddressOf)ShallowClone(); clone.Value = this.value.Clone(); return clone; } public override StackType ResultType { get { return StackType.Ref; } } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } protected override InstructionFlags ComputeFlags() { return value.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.value.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitAddressOf(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitAddressOf(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitAddressOf(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as AddressOf; return o != null && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior. public sealed partial class ThreeValuedBoolAnd : BinaryInstruction { public ThreeValuedBoolAnd(ILInstruction left, ILInstruction right) : base(OpCode.ThreeValuedBoolAnd, left, right) { } public override StackType ResultType { get { return StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitThreeValuedBoolAnd(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitThreeValuedBoolAnd(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitThreeValuedBoolAnd(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as ThreeValuedBoolAnd; return o != null && this.Left.PerformMatch(o.Left, ref match) && this.Right.PerformMatch(o.Right, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior. public sealed partial class ThreeValuedBoolOr : BinaryInstruction { public ThreeValuedBoolOr(ILInstruction left, ILInstruction right) : base(OpCode.ThreeValuedBoolOr, left, right) { } public override StackType ResultType { get { return StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitThreeValuedBoolOr(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitThreeValuedBoolOr(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitThreeValuedBoolOr(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as ThreeValuedBoolOr; return o != null && this.Left.PerformMatch(o.Left, ref match) && this.Right.PerformMatch(o.Right, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// The input operand must be one of: /// 1. a nullable value type /// 2. a reference type /// 3. a managed reference to a type parameter. /// If the input is non-null, evaluates to the (unwrapped) input. /// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction. /// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null). public sealed partial class NullableUnwrap : UnaryInstruction { protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayUnwrapNull; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayUnwrapNull; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNullableUnwrap(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNullableUnwrap(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNullableUnwrap(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NullableUnwrap; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Serves as jump target for the nullable.unwrap instruction. /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. public sealed partial class NullableRewrap : UnaryInstruction { public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) { } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNullableRewrap(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNullableRewrap(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNullableRewrap(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NullableRewrap; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant string. public sealed partial class LdStr : SimpleInstruction { public LdStr(string value) : base(OpCode.LdStr) { this.Value = value; } public readonly string Value; public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdStr(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdStr(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdStr(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdStr; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant byte string (as ReadOnlySpan<byte>). public sealed partial class LdStrUtf8 : SimpleInstruction { public LdStrUtf8(string value) : base(OpCode.LdStrUtf8) { this.Value = value; } public readonly string Value; public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdStrUtf8(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdStrUtf8(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdStrUtf8(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdStrUtf8; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant 32-bit integer. public sealed partial class LdcI4 : SimpleInstruction { public LdcI4(int value) : base(OpCode.LdcI4) { this.Value = value; } public readonly int Value; public override StackType ResultType { get { return StackType.I4; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdcI4(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdcI4(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdcI4(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdcI4; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant 64-bit integer. public sealed partial class LdcI8 : SimpleInstruction { public LdcI8(long value) : base(OpCode.LdcI8) { this.Value = value; } public readonly long Value; public override StackType ResultType { get { return StackType.I8; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdcI8(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdcI8(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdcI8(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdcI8; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant 32-bit floating-point number. public sealed partial class LdcF4 : SimpleInstruction { public LdcF4(float value) : base(OpCode.LdcF4) { this.Value = value; } public readonly float Value; public override StackType ResultType { get { return StackType.F4; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdcF4(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdcF4(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdcF4(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdcF4; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant 64-bit floating-point number. public sealed partial class LdcF8 : SimpleInstruction { public LdcF8(double value) : base(OpCode.LdcF8) { this.Value = value; } public readonly double Value; public override StackType ResultType { get { return StackType.F8; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdcF8(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdcF8(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdcF8(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdcF8; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads a constant decimal. public sealed partial class LdcDecimal : SimpleInstruction { public LdcDecimal(decimal value) : base(OpCode.LdcDecimal) { this.Value = value; } public readonly decimal Value; public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdcDecimal(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdcDecimal(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdcDecimal(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdcDecimal; return o != null && this.Value == o.Value; } } } namespace ICSharpCode.Decompiler.IL { /// Loads the null reference. public sealed partial class LdNull : SimpleInstruction { public LdNull() : base(OpCode.LdNull) { } public override StackType ResultType { get { return StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdNull(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdNull(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdNull(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdNull; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Load method pointer public sealed partial class LdFtn : SimpleInstruction, IInstructionWithMethodOperand { public LdFtn(IMethod method) : base(OpCode.LdFtn) { this.method = method; } readonly IMethod method; /// Returns the method operand. public IMethod Method => method; public override StackType ResultType { get { return StackType.I; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (method != null) { output.Write(' '); method.WriteTo(output); } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdFtn(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdFtn(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdFtn(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdFtn; return o != null && object.Equals(method, o.method); } } } namespace ICSharpCode.Decompiler.IL { /// Load method pointer public sealed partial class LdVirtFtn : UnaryInstruction, IInstructionWithMethodOperand { public LdVirtFtn(ILInstruction argument, IMethod method) : base(OpCode.LdVirtFtn, argument) { this.method = method; } readonly IMethod method; /// Returns the method operand. public IMethod Method => method; public override StackType ResultType { get { return StackType.I; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (method != null) { output.Write(' '); method.WriteTo(output); } output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdVirtFtn(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdVirtFtn(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdVirtFtn(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdVirtFtn; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && object.Equals(method, o.method); } } } namespace ICSharpCode.Decompiler.IL { /// Virtual delegate construction public sealed partial class LdVirtDelegate : UnaryInstruction, IInstructionWithMethodOperand { public LdVirtDelegate(ILInstruction argument, IType type, IMethod method) : base(OpCode.LdVirtDelegate, argument) { this.type = type; this.method = method; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } readonly IMethod method; /// Returns the method operand. public IMethod Method => method; public override StackType ResultType { get { return StackType.O; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); if (method != null) { output.Write(' '); method.WriteTo(output); } output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdVirtDelegate(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdVirtDelegate(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdVirtDelegate(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdVirtDelegate; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && object.Equals(method, o.method); } } } namespace ICSharpCode.Decompiler.IL { /// Loads runtime representation of metadata token public sealed partial class LdTypeToken : SimpleInstruction { public LdTypeToken(IType type) : base(OpCode.LdTypeToken) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdTypeToken(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdTypeToken(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdTypeToken(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdTypeToken; return o != null && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Loads runtime representation of metadata token public sealed partial class LdMemberToken : SimpleInstruction { public LdMemberToken(IMember member) : base(OpCode.LdMemberToken) { this.member = member; } readonly IMember member; /// Returns the token operand. public IMember Member { get { return member; } } public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); member.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdMemberToken(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdMemberToken(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdMemberToken(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdMemberToken; return o != null && member.Equals(o.member); } } } namespace ICSharpCode.Decompiler.IL { /// Allocates space in the stack frame public sealed partial class LocAlloc : UnaryInstruction { public LocAlloc(ILInstruction argument) : base(OpCode.LocAlloc, argument) { } public override StackType ResultType { get { return StackType.I; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLocAlloc(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLocAlloc(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLocAlloc(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LocAlloc; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Allocates space in the stack frame and wraps it in a Span public sealed partial class LocAllocSpan : UnaryInstruction { public LocAllocSpan(ILInstruction argument, IType type) : base(OpCode.LocAllocSpan, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLocAllocSpan(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLocAllocSpan(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLocAllocSpan(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LocAllocSpan; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// memcpy(destAddress, sourceAddress, size); public sealed partial class Cpblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix { public Cpblk(ILInstruction destAddress, ILInstruction sourceAddress, ILInstruction size) : base(OpCode.Cpblk) { this.DestAddress = destAddress; this.SourceAddress = sourceAddress; this.Size = size; } public static readonly SlotInfo DestAddressSlot = new SlotInfo("DestAddress", canInlineInto: true); ILInstruction destAddress = null!; public ILInstruction DestAddress { get { return this.destAddress; } set { ValidateChild(value); SetChildInstruction(ref this.destAddress, value, 0); } } public static readonly SlotInfo SourceAddressSlot = new SlotInfo("SourceAddress", canInlineInto: true); ILInstruction sourceAddress = null!; public ILInstruction SourceAddress { get { return this.sourceAddress; } set { ValidateChild(value); SetChildInstruction(ref this.sourceAddress, value, 1); } } public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true); ILInstruction size = null!; public ILInstruction Size { get { return this.size; } set { ValidateChild(value); SetChildInstruction(ref this.size, value, 2); } } protected sealed override int GetChildCount() { return 3; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.destAddress; case 1: return this.sourceAddress; case 2: return this.size; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.DestAddress = value; break; case 1: this.SourceAddress = value; break; case 2: this.Size = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return DestAddressSlot; case 1: return SourceAddressSlot; case 2: return SizeSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (Cpblk)ShallowClone(); clone.DestAddress = this.destAddress.Clone(); clone.SourceAddress = this.sourceAddress.Clone(); clone.Size = this.size.Clone(); return clone; } /// Gets/Sets whether the memory access is volatile. public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return destAddress.Flags | sourceAddress.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (IsVolatile) output.Write("volatile."); if (UnalignedPrefix > 0) output.Write("unaligned(" + UnalignedPrefix + ")."); output.Write(OpCode); output.Write('('); this.destAddress.WriteTo(output, options); output.Write(", "); this.sourceAddress.WriteTo(output, options); output.Write(", "); this.size.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCpblk(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCpblk(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCpblk(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Cpblk; return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(destAddress.ResultType == StackType.I || destAddress.ResultType == StackType.Ref); DebugAssert(sourceAddress.ResultType == StackType.I || sourceAddress.ResultType == StackType.Ref); DebugAssert(size.ResultType == StackType.I4); } } } namespace ICSharpCode.Decompiler.IL { /// memset(address, value, size) public sealed partial class Initblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix { public Initblk(ILInstruction address, ILInstruction value, ILInstruction size) : base(OpCode.Initblk) { this.Address = address; this.Value = value; this.Size = size; } public static readonly SlotInfo AddressSlot = new SlotInfo("Address", canInlineInto: true); ILInstruction address = null!; public ILInstruction Address { get { return this.address; } set { ValidateChild(value); SetChildInstruction(ref this.address, value, 0); } } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 1); } } public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true); ILInstruction size = null!; public ILInstruction Size { get { return this.size; } set { ValidateChild(value); SetChildInstruction(ref this.size, value, 2); } } protected sealed override int GetChildCount() { return 3; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.address; case 1: return this.value; case 2: return this.size; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Address = value; break; case 1: this.Value = value; break; case 2: this.Size = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return AddressSlot; case 1: return ValueSlot; case 2: return SizeSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (Initblk)ShallowClone(); clone.Address = this.address.Clone(); clone.Value = this.value.Clone(); clone.Size = this.size.Clone(); return clone; } /// Gets/Sets whether the memory access is volatile. public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return address.Flags | value.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (IsVolatile) output.Write("volatile."); if (UnalignedPrefix > 0) output.Write("unaligned(" + UnalignedPrefix + ")."); output.Write(OpCode); output.Write('('); this.address.WriteTo(output, options); output.Write(", "); this.value.WriteTo(output, options); output.Write(", "); this.size.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitInitblk(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitInitblk(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitInitblk(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Initblk; return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(address.ResultType == StackType.I || address.ResultType == StackType.Ref); DebugAssert(value.ResultType == StackType.I4); DebugAssert(size.ResultType == StackType.I4); } } } namespace ICSharpCode.Decompiler.IL { /// Load address of instance field public sealed partial class LdFlda : ILInstruction, IInstructionWithFieldOperand { public LdFlda(ILInstruction target, IField @field) : base(OpCode.LdFlda) { this.Target = target; this.@field = @field; } public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (LdFlda)ShallowClone(); clone.Target = this.target.Clone(); return clone; } public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced readonly IField @field; /// Returns the field operand. public IField Field { get { return @field; } } public override StackType ResultType { get { return target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref; } } protected override InstructionFlags ComputeFlags() { return target.Flags | (DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow); } public override InstructionFlags DirectFlags { get { return (DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (DelayExceptions) output.Write("delayex."); output.Write(OpCode); output.Write(' '); @field.WriteTo(output); output.Write('('); this.target.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdFlda(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdFlda(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdFlda(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdFlda; return o != null && this.target.PerformMatch(o.target, ref match) && DelayExceptions == o.DelayExceptions && @field.Equals(o.@field); } } } namespace ICSharpCode.Decompiler.IL { /// Load static field address public sealed partial class LdsFlda : SimpleInstruction, IInstructionWithFieldOperand { public LdsFlda(IField @field) : base(OpCode.LdsFlda) { this.@field = @field; } public override StackType ResultType { get { return StackType.Ref; } } readonly IField @field; /// Returns the field operand. public IField Field { get { return @field; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); @field.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdsFlda(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdsFlda(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdsFlda(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdsFlda; return o != null && @field.Equals(o.@field); } } } namespace ICSharpCode.Decompiler.IL { /// Casts an object to a class. public sealed partial class CastClass : UnaryInstruction { public CastClass(ILInstruction argument, IType type) : base(OpCode.CastClass, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return type.GetStackType(); } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitCastClass(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitCastClass(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitCastClass(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as CastClass; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Test if object is instance of class or interface. public sealed partial class IsInst : UnaryInstruction { public IsInst(ILInstruction argument, IType type) : base(OpCode.IsInst, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitIsInst(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitIsInst(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitIsInst(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as IsInst; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Indirect load (ref/pointer dereference). public sealed partial class LdObj : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix { public LdObj(ILInstruction target, IType type) : base(OpCode.LdObj) { this.Target = target; this.type = type; } public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (LdObj)ShallowClone(); clone.Target = this.target.Clone(); return clone; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } /// Gets/Sets whether the memory access is volatile. public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } public override StackType ResultType { get { return type.GetStackType(); } } protected override InstructionFlags ComputeFlags() { return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.SideEffect | InstructionFlags.MayThrow; } } void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (IsVolatile) output.Write("volatile."); if (UnalignedPrefix > 0) output.Write("unaligned(" + UnalignedPrefix + ")."); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.target.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdObj(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdObj(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdObj(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdObj; return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); } } } namespace ICSharpCode.Decompiler.IL { /// If argument is a ref to a reference type, loads the object reference, stores it in a temporary, and evaluates to the address of that temporary (address.of(ldobj(arg))). Otherwise, returns the argument ref as-is.This instruction represents the memory-load semantics of callvirt with a generic type as receiver (where the IL always takes a ref, but only methods on value types expect one, for method on reference types there's an implicit ldobj, which this instruction makes explicit in order to preserve the order-of-evaluation). public sealed partial class LdObjIfRef : ILInstruction { public LdObjIfRef(ILInstruction target, IType type) : base(OpCode.LdObjIfRef) { this.Target = target; this.type = type; } public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (LdObjIfRef)ShallowClone(); clone.Target = this.target.Clone(); return clone; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.Ref; } } protected override InstructionFlags ComputeFlags() { return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.SideEffect | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.target.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdObjIfRef(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdObjIfRef(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdObjIfRef(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdObjIfRef; return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); } } } namespace ICSharpCode.Decompiler.IL { /// Indirect store (store to ref/pointer). /// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign()) public sealed partial class StObj : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix { public StObj(ILInstruction target, ILInstruction value, IType type) : base(OpCode.StObj) { this.Target = target; this.Value = value; this.type = type; } public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; case 1: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; case 1: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; case 1: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (StObj)ShallowClone(); clone.Target = this.target.Clone(); clone.Value = this.value.Clone(); return clone; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } /// Gets/Sets whether the memory access is volatile. public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } public override StackType ResultType { get { return UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void; } } protected override InstructionFlags ComputeFlags() { return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.SideEffect | InstructionFlags.MayThrow; } } void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (IsVolatile) output.Write("volatile."); if (UnalignedPrefix > 0) output.Write("unaligned(" + UnalignedPrefix + ")."); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.target.WriteTo(output, options); output.Write(", "); this.value.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitStObj(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitStObj(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitStObj(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as StObj; return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); DebugAssert(value.ResultType == type.GetStackType()); CheckTargetSlot(); } } } namespace ICSharpCode.Decompiler.IL { /// Boxes a value. public sealed partial class Box : UnaryInstruction { public Box(ILInstruction argument, IType type) : base(OpCode.Box, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBox(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitBox(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitBox(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Box; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Compute address inside box. public sealed partial class Unbox : UnaryInstruction { public Unbox(ILInstruction argument, IType type) : base(OpCode.Unbox, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.Ref; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitUnbox(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitUnbox(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitUnbox(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Unbox; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Unbox a value. public sealed partial class UnboxAny : UnaryInstruction { public UnboxAny(ILInstruction argument, IType type) : base(OpCode.UnboxAny, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return type.GetStackType(); } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitUnboxAny(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitUnboxAny(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitUnboxAny(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as UnboxAny; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Creates an object instance and calls the constructor. public sealed partial class NewObj : CallInstruction { public NewObj(IMethod method) : base(OpCode.NewObj, method) { } public override StackType ResultType { get { return Method.DeclaringType.GetStackType(); } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNewObj(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNewObj(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNewObj(this, context); } } } namespace ICSharpCode.Decompiler.IL { /// Creates an array instance. public sealed partial class NewArr : ILInstruction { public NewArr(IType type, params ILInstruction[] indices) : base(OpCode.NewArr) { this.type = type; this.Indices = new InstructionCollection(this, 0); this.Indices.AddRange(indices); } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true); public InstructionCollection Indices { get; private set; } protected sealed override int GetChildCount() { return Indices.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Indices[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Indices[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return IndicesSlot; } } public sealed override ILInstruction Clone() { var clone = (NewArr)ShallowClone(); clone.Indices = new InstructionCollection(clone, 0); clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public override StackType ResultType { get { return StackType.O; } } protected override InstructionFlags ComputeFlags() { return Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); bool first = true; foreach (var indices in Indices) { if (!first) output.Write(", "); else first = false; indices.WriteTo(output, options); } output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNewArr(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitNewArr(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitNewArr(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NewArr; return o != null && type.Equals(o.type) && Patterns.ListMatch.DoMatch(this.Indices, o.Indices, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Returns the default value for a type. public sealed partial class DefaultValue : SimpleInstruction { public DefaultValue(IType type) : base(OpCode.DefaultValue) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return type.GetStackType(); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDefaultValue(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDefaultValue(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDefaultValue(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DefaultValue; return o != null && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Throws an exception. public sealed partial class Throw : UnaryInstruction { public Throw(ILInstruction argument) : base(OpCode.Throw, argument) { } public override StackType ResultType { get { return this.resultType; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitThrow(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitThrow(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitThrow(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Throw; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Rethrows the current exception. public sealed partial class Rethrow : SimpleInstruction { public Rethrow() : base(OpCode.Rethrow) { } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitRethrow(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitRethrow(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitRethrow(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Rethrow; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Gets the size of a type in bytes. public sealed partial class SizeOf : SimpleInstruction { public SizeOf(IType type) : base(OpCode.SizeOf) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.I4; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitSizeOf(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitSizeOf(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitSizeOf(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as SizeOf; return o != null && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Returns the length of an array as 'native unsigned int'. public sealed partial class LdLen : ILInstruction { public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true); ILInstruction array = null!; public ILInstruction Array { get { return this.array; } set { ValidateChild(value); SetChildInstruction(ref this.array, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.array; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Array = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArraySlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (LdLen)ShallowClone(); clone.Array = this.array.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return array.Flags | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdLen(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdLen(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdLen(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdLen; return o != null && this.array.PerformMatch(o.array, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(array.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// Load address of array element. public sealed partial class LdElema : ILInstruction { public LdElema(IType type, ILInstruction array, params ILInstruction[] indices) : base(OpCode.LdElema) { this.type = type; this.Array = array; this.Indices = new InstructionCollection(this, 1); this.Indices.AddRange(indices); } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true); ILInstruction array = null!; public ILInstruction Array { get { return this.array; } set { ValidateChild(value); SetChildInstruction(ref this.array, value, 0); } } public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true); public InstructionCollection Indices { get; private set; } protected sealed override int GetChildCount() { return 1 + Indices.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.array; default: return this.Indices[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Array = value; break; default: this.Indices[index - 1] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArraySlot; default: return IndicesSlot; } } public sealed override ILInstruction Clone() { var clone = (LdElema)ShallowClone(); clone.Array = this.array.Clone(); clone.Indices = new InstructionCollection(clone, 1); clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public bool WithSystemIndex; public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced public override StackType ResultType { get { return StackType.Ref; } } /// Gets whether the 'readonly' prefix was applied to this instruction. public bool IsReadOnly { get; set; } protected override InstructionFlags ComputeFlags() { return array.Flags | Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | (DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow); } public override InstructionFlags DirectFlags { get { return (DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow); } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (WithSystemIndex) output.Write("withsystemindex."); if (DelayExceptions) output.Write("delayex."); if (IsReadOnly) output.Write("readonly."); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.array.WriteTo(output, options); foreach (var indices in Indices) { output.Write(", "); indices.WriteTo(output, options); } output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdElema(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdElema(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdElema(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdElema; return o != null && type.Equals(o.type) && this.array.PerformMatch(o.array, ref match) && Patterns.ListMatch.DoMatch(this.Indices, o.Indices, ref match) && this.WithSystemIndex == o.WithSystemIndex && DelayExceptions == o.DelayExceptions && IsReadOnly == o.IsReadOnly; } } } namespace ICSharpCode.Decompiler.IL { /// Load address of inline array element. public sealed partial class LdElemaInlineArray : ILInstruction { public LdElemaInlineArray(IType type, ILInstruction array, params ILInstruction[] indices) : base(OpCode.LdElemaInlineArray) { this.type = type; this.Array = array; this.Indices = new InstructionCollection(this, 1); this.Indices.AddRange(indices); } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true); ILInstruction array = null!; public ILInstruction Array { get { return this.array; } set { ValidateChild(value); SetChildInstruction(ref this.array, value, 0); } } public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true); public InstructionCollection Indices { get; private set; } protected sealed override int GetChildCount() { return 1 + Indices.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.array; default: return this.Indices[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Array = value; break; default: this.Indices[index - 1] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArraySlot; default: return IndicesSlot; } } public sealed override ILInstruction Clone() { var clone = (LdElemaInlineArray)ShallowClone(); clone.Array = this.array.Clone(); clone.Indices = new InstructionCollection(clone, 1); clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public override StackType ResultType { get { return StackType.Ref; } } /// Gets whether the 'readonly' prefix was applied to this instruction. public bool IsReadOnly { get; set; } protected override InstructionFlags ComputeFlags() { return array.Flags | Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); if (IsReadOnly) output.Write("readonly."); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); this.array.WriteTo(output, options); foreach (var indices in Indices) { output.Write(", "); indices.WriteTo(output, options); } output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdElemaInlineArray(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitLdElemaInlineArray(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitLdElemaInlineArray(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as LdElemaInlineArray; return o != null && type.Equals(o.type) && this.array.PerformMatch(o.array, ref match) && Patterns.ListMatch.DoMatch(this.Indices, o.Indices, ref match) && IsReadOnly == o.IsReadOnly; } } } namespace ICSharpCode.Decompiler.IL { /// Retrieves a pinnable reference for the input object. /// The input must be an object reference (O). /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty. /// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null. /// public sealed partial class GetPinnableReference : ILInstruction, IInstructionWithMethodOperand { public GetPinnableReference(ILInstruction argument, IMethod? method) : base(OpCode.GetPinnableReference) { this.Argument = argument; this.method = method; } public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); ILInstruction argument = null!; public ILInstruction Argument { get { return this.argument; } set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.argument; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Argument = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArgumentSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (GetPinnableReference)ShallowClone(); clone.Argument = this.argument.Clone(); return clone; } public override StackType ResultType { get { return StackType.Ref; } } readonly IMethod? method; /// Returns the method operand. public IMethod? Method => method; protected override InstructionFlags ComputeFlags() { return argument.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (method != null) { output.Write(' '); method.WriteTo(output); } output.Write('('); this.argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitGetPinnableReference(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitGetPinnableReference(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitGetPinnableReference(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as GetPinnableReference; return o != null && this.argument.PerformMatch(o.argument, ref match) && object.Equals(method, o.method); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(argument.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// Maps a string value to an integer. This is used in switch(string). public sealed partial class StringToInt : ILInstruction { public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); ILInstruction argument = null!; public ILInstruction Argument { get { return this.argument; } set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.argument; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Argument = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArgumentSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (StringToInt)ShallowClone(); clone.Argument = this.argument.Clone(); return clone; } public override StackType ResultType { get { return StackType.I4; } } protected override InstructionFlags ComputeFlags() { return argument.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.None; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitStringToInt(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitStringToInt(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitStringToInt(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as StringToInt; return o != null && this.argument.PerformMatch(o.argument, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(argument.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of Expression.Convert. public sealed partial class ExpressionTreeCast : UnaryInstruction { IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return type.GetStackType(); } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitExpressionTreeCast(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitExpressionTreeCast(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitExpressionTreeCast(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as ExpressionTreeCast; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && this.IsChecked == o.IsChecked; } } } namespace ICSharpCode.Decompiler.IL { /// Use of user-defined && or || operator. public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand { public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator) { this.method = method; this.Left = left; this.Right = right; } readonly IMethod method; /// Returns the method operand. public IMethod Method => method; public override StackType ResultType { get { return StackType.O; } } public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true); ILInstruction left = null!; public ILInstruction Left { get { return this.left; } set { ValidateChild(value); SetChildInstruction(ref this.left, value, 0); } } public static readonly SlotInfo RightSlot = new SlotInfo("Right"); ILInstruction right = null!; public ILInstruction Right { get { return this.right; } set { ValidateChild(value); SetChildInstruction(ref this.right, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.left; case 1: return this.right; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Left = value; break; case 1: this.Right = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return LeftSlot; case 1: return RightSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (UserDefinedLogicOperator)ShallowClone(); clone.Left = this.left.Clone(); clone.Right = this.right.Clone(); return clone; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); if (method != null) { output.Write(' '); method.WriteTo(output); } output.Write('('); this.left.WriteTo(output, options); output.Write(", "); this.right.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitUserDefinedLogicOperator(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitUserDefinedLogicOperator(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitUserDefinedLogicOperator(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as UserDefinedLogicOperator; return o != null && object.Equals(method, o.method) && this.left.PerformMatch(o.left, ref match) && this.right.PerformMatch(o.right, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a short-circuiting binary operator inside a dynamic expression. public sealed partial class DynamicLogicOperatorInstruction : DynamicInstruction { public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true); ILInstruction left = null!; public ILInstruction Left { get { return this.left; } set { ValidateChild(value); SetChildInstruction(ref this.left, value, 0); } } public static readonly SlotInfo RightSlot = new SlotInfo("Right"); ILInstruction right = null!; public ILInstruction Right { get { return this.right; } set { ValidateChild(value); SetChildInstruction(ref this.right, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.left; case 1: return this.right; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Left = value; break; case 1: this.Right = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return LeftSlot; case 1: return RightSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicLogicOperatorInstruction)ShallowClone(); clone.Left = this.left.Clone(); clone.Right = this.right.Clone(); return clone; } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicLogicOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicLogicOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicLogicOperatorInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicLogicOperatorInstruction; return o != null && this.left.PerformMatch(o.left, ref match) && this.right.PerformMatch(o.right, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a binary operator inside a dynamic expression (maps to Binder.BinaryOperation). public sealed partial class DynamicBinaryOperatorInstruction : DynamicInstruction { public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true); ILInstruction left = null!; public ILInstruction Left { get { return this.left; } set { ValidateChild(value); SetChildInstruction(ref this.left, value, 0); } } public static readonly SlotInfo RightSlot = new SlotInfo("Right", canInlineInto: true); ILInstruction right = null!; public ILInstruction Right { get { return this.right; } set { ValidateChild(value); SetChildInstruction(ref this.right, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.left; case 1: return this.right; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Left = value; break; case 1: this.Right = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return LeftSlot; case 1: return RightSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicBinaryOperatorInstruction)ShallowClone(); clone.Left = this.left.Clone(); clone.Right = this.right.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | left.Flags | right.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicBinaryOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicBinaryOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicBinaryOperatorInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicBinaryOperatorInstruction; return o != null && this.left.PerformMatch(o.left, ref match) && this.right.PerformMatch(o.right, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a unary operator inside a dynamic expression (maps to Binder.UnaryOperation). public sealed partial class DynamicUnaryOperatorInstruction : DynamicInstruction { public static readonly SlotInfo OperandSlot = new SlotInfo("Operand", canInlineInto: true); ILInstruction operand = null!; public ILInstruction Operand { get { return this.operand; } set { ValidateChild(value); SetChildInstruction(ref this.operand, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.operand; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Operand = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return OperandSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicUnaryOperatorInstruction)ShallowClone(); clone.Operand = this.operand.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | operand.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicUnaryOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicUnaryOperatorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicUnaryOperatorInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicUnaryOperatorInstruction; return o != null && this.operand.PerformMatch(o.operand, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a cast inside a dynamic expression (maps to Binder.Convert). public sealed partial class DynamicConvertInstruction : DynamicInstruction { IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); ILInstruction argument = null!; public ILInstruction Argument { get { return this.argument; } set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.argument; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Argument = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArgumentSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicConvertInstruction)ShallowClone(); clone.Argument = this.argument.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | argument.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicConvertInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicConvertInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicConvertInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicConvertInstruction; return o != null && type.Equals(o.type) && this.argument.PerformMatch(o.argument, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(argument.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a property get method call inside a dynamic expression (maps to Binder.GetMember). public sealed partial class DynamicGetMemberInstruction : DynamicInstruction { public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicGetMemberInstruction)ShallowClone(); clone.Target = this.target.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | target.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicGetMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicGetMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicGetMemberInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicGetMemberInstruction; return o != null && this.target.PerformMatch(o.target, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(target.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a property set method call inside a dynamic expression (maps to Binder.SetMember). public sealed partial class DynamicSetMemberInstruction : DynamicInstruction { public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); ILInstruction target = null!; public ILInstruction Target { get { return this.target; } set { ValidateChild(value); SetChildInstruction(ref this.target, value, 0); } } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 1); } } protected sealed override int GetChildCount() { return 2; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.target; case 1: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Target = value; break; case 1: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TargetSlot; case 1: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicSetMemberInstruction)ShallowClone(); clone.Target = this.target.Clone(); clone.Value = this.value.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | target.Flags | value.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicSetMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicSetMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicSetMemberInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicSetMemberInstruction; return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(target.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of an indexer get method call inside a dynamic expression (maps to Binder.GetIndex). public sealed partial class DynamicGetIndexInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (DynamicGetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags); } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicGetIndexInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicGetIndexInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicGetIndexInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicGetIndexInstruction; return o != null && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of an indexer set method call inside a dynamic expression (maps to Binder.SetIndex). public sealed partial class DynamicSetIndexInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (DynamicSetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags); } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicSetIndexInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicSetIndexInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicSetIndexInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicSetIndexInstruction; return o != null && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a method call inside a dynamic expression (maps to Binder.InvokeMember). public sealed partial class DynamicInvokeMemberInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (DynamicInvokeMemberInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags); } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicInvokeMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicInvokeMemberInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicInvokeMemberInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicInvokeMemberInstruction; return o != null && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a constuctor invocation inside a dynamic expression (maps to Binder.InvokeConstructor). public sealed partial class DynamicInvokeConstructorInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (DynamicInvokeConstructorInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags); } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicInvokeConstructorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicInvokeConstructorInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicInvokeConstructorInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicInvokeConstructorInstruction; return o != null && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a delegate invocation inside a dynamic expression (maps to Binder.Invoke). public sealed partial class DynamicInvokeInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true); public InstructionCollection Arguments { get; private set; } protected sealed override int GetChildCount() { return Arguments.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: return this.Arguments[index - 0]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: this.Arguments[index - 0] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: return ArgumentsSlot; } } public sealed override ILInstruction Clone() { var clone = (DynamicInvokeInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags); } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicInvokeInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicInvokeInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicInvokeInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicInvokeInstruction; return o != null && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of a call to the Binder.IsEvent method inside a dynamic expression. public sealed partial class DynamicIsEventInstruction : DynamicInstruction { public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); ILInstruction argument = null!; public ILInstruction Argument { get { return this.argument; } set { ValidateChild(value); SetChildInstruction(ref this.argument, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.argument; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Argument = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ArgumentSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (DynamicIsEventInstruction)ShallowClone(); clone.Argument = this.argument.Clone(); return clone; } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect | argument.Flags; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDynamicIsEventInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDynamicIsEventInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDynamicIsEventInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DynamicIsEventInstruction; return o != null && this.argument.PerformMatch(o.argument, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(argument.ResultType == StackType.O); } } } namespace ICSharpCode.Decompiler.IL { /// ILAst representation of C# patterns public sealed partial class MatchInstruction : ILInstruction, IStoreInstruction, IInstructionWithMethodOperand { public MatchInstruction(ILVariable variable, IMethod? method, ILInstruction testedOperand, params ILInstruction[] subPatterns) : base(OpCode.MatchInstruction) { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); this.method = method; this.TestedOperand = testedOperand; this.SubPatterns = new InstructionCollection(this, 1); this.SubPatterns.AddRange(subPatterns); } ILVariable variable; public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) variable.AddStoreInstruction(this); } } public int IndexInStoreInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddStoreInstruction(this); } protected override void Disconnected() { variable.RemoveStoreInstruction(this); base.Disconnected(); } readonly IMethod? method; /// Returns the method operand. public IMethod? Method => method; public bool IsDeconstructCall; public bool IsDeconstructTuple; public bool CheckType; public bool CheckNotNull; public static readonly SlotInfo TestedOperandSlot = new SlotInfo("TestedOperand", canInlineInto: true); ILInstruction testedOperand = null!; public ILInstruction TestedOperand { get { return this.testedOperand; } set { ValidateChild(value); SetChildInstruction(ref this.testedOperand, value, 0); } } public static readonly SlotInfo SubPatternsSlot = new SlotInfo("SubPatterns"); public InstructionCollection SubPatterns { get; private set; } protected sealed override int GetChildCount() { return 1 + SubPatterns.Count; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.testedOperand; default: return this.SubPatterns[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.TestedOperand = value; break; default: this.SubPatterns[index - 1] = (ILInstruction)value; break; } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return TestedOperandSlot; default: return SubPatternsSlot; } } public sealed override ILInstruction Clone() { var clone = (MatchInstruction)ShallowClone(); clone.TestedOperand = this.testedOperand.Clone(); clone.SubPatterns = new InstructionCollection(clone, 1); clone.SubPatterns.AddRange(this.SubPatterns.Select(arg => (ILInstruction)arg.Clone())); return clone; } public override StackType ResultType { get { return StackType.I4; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | testedOperand.Flags | SubPatterns.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.SideEffect | InstructionFlags.MayThrow | InstructionFlags.ControlFlow; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayWriteLocals | InstructionFlags.SideEffect | InstructionFlags.MayThrow | InstructionFlags.ControlFlow; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitMatchInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitMatchInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitMatchInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as MatchInstruction; return o != null && variable == o.variable && object.Equals(method, o.method) && this.IsDeconstructCall == o.IsDeconstructCall && this.IsDeconstructTuple == o.IsDeconstructTuple && this.CheckType == o.CheckType && this.CheckNotNull == o.CheckNotNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!)); DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable); AdditionalInvariants(); } } } namespace ICSharpCode.Decompiler.IL { /// Push a typed reference of type class onto the stack. public sealed partial class MakeRefAny : UnaryInstruction { public MakeRefAny(ILInstruction argument, IType type) : base(OpCode.MakeRefAny, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.O; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitMakeRefAny(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitMakeRefAny(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitMakeRefAny(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as MakeRefAny; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Push the type token stored in a typed reference. public sealed partial class RefAnyType : UnaryInstruction { public RefAnyType(ILInstruction argument) : base(OpCode.RefAnyType, argument) { } public override StackType ResultType { get { return StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitRefAnyType(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitRefAnyType(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitRefAnyType(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as RefAnyType; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Push the address stored in a typed reference. public sealed partial class RefAnyValue : UnaryInstruction { public RefAnyValue(ILInstruction argument, IType type) : base(OpCode.RefAnyValue, argument) { this.type = type; } IType type; /// Returns the type operand. public IType Type { get { return type; } set { type = value; InvalidateFlags(); } } public override StackType ResultType { get { return StackType.Ref; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; } public override InstructionFlags DirectFlags { get { return base.DirectFlags | InstructionFlags.MayThrow; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write(' '); type.WriteTo(output); output.Write('('); Argument.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitRefAnyValue(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitRefAnyValue(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitRefAnyValue(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as RefAnyValue; return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } namespace ICSharpCode.Decompiler.IL { /// Yield an element from an iterator. public sealed partial class YieldReturn : ILInstruction { public YieldReturn(ILInstruction value) : base(OpCode.YieldReturn) { this.Value = value; } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (YieldReturn)ShallowClone(); clone.Value = this.value.Clone(); return clone; } public override StackType ResultType { get { return StackType.Void; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayBranch | InstructionFlags.SideEffect | value.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.MayBranch | InstructionFlags.SideEffect; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); this.value.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitYieldReturn(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitYieldReturn(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitYieldReturn(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as YieldReturn; return o != null && this.value.PerformMatch(o.value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// C# await operator. public sealed partial class Await : ILInstruction { public Await(ILInstruction value) : base(OpCode.Await) { this.Value = value; } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value = null!; public ILInstruction Value { get { return this.value; } set { ValidateChild(value); SetChildInstruction(ref this.value, value, 0); } } protected sealed override int GetChildCount() { return 1; } protected sealed override ILInstruction GetChild(int index) { switch (index) { case 0: return this.value; default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { case 0: this.Value = value; break; default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { case 0: return ValueSlot; default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (Await)ShallowClone(); clone.Value = this.value.Clone(); return clone; } public override StackType ResultType { get { return GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown; } } protected override InstructionFlags ComputeFlags() { return InstructionFlags.SideEffect | value.Flags; } public override InstructionFlags DirectFlags { get { return InstructionFlags.SideEffect; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); this.value.WriteTo(output, options); output.Write(')'); } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitAwait(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitAwait(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitAwait(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as Await; return o != null && this.value.PerformMatch(o.value, ref match); } } } namespace ICSharpCode.Decompiler.IL { /// Deconstruction statement public sealed partial class DeconstructInstruction : ILInstruction { public override StackType ResultType { get { return StackType.Void; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDeconstructInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDeconstructInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDeconstructInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DeconstructInstruction; return o != null; } } } namespace ICSharpCode.Decompiler.IL { /// Represents a deconstructed value public sealed partial class DeconstructResultInstruction : UnaryInstruction { public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDeconstructResultInstruction(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.VisitDeconstructResultInstruction(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.VisitDeconstructResultInstruction(this, context); } protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as DeconstructResultInstruction; return o != null && this.Argument.PerformMatch(o.Argument, ref match); } internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); AdditionalInvariants(); } } } namespace ICSharpCode.Decompiler.IL.Patterns { /// Matches any node public sealed partial class AnyNode : PatternInstruction { protected sealed override int GetChildCount() { return 0; } protected sealed override ILInstruction GetChild(int index) { switch (index) { default: throw new IndexOutOfRangeException(); } } protected sealed override void SetChild(int index, ILInstruction value) { switch (index) { default: throw new IndexOutOfRangeException(); } } protected sealed override SlotInfo GetChildSlot(int index) { switch (index) { default: throw new IndexOutOfRangeException(); } } public sealed override ILInstruction Clone() { var clone = (AnyNode)ShallowClone(); return clone; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); output.Write(OpCode); output.Write('('); output.Write(')'); } } } namespace ICSharpCode.Decompiler.IL { /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract void Default(ILInstruction inst); protected internal virtual void VisitInvalidBranch(InvalidBranch inst) { Default(inst); } protected internal virtual void VisitInvalidExpression(InvalidExpression inst) { Default(inst); } protected internal virtual void VisitNop(Nop inst) { Default(inst); } protected internal virtual void VisitILFunction(ILFunction function) { Default(function); } protected internal virtual void VisitBlockContainer(BlockContainer container) { Default(container); } protected internal virtual void VisitBlock(Block block) { Default(block); } protected internal virtual void VisitPinnedRegion(PinnedRegion inst) { Default(inst); } protected internal virtual void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { Default(inst); } protected internal virtual void VisitNumericCompoundAssign(NumericCompoundAssign inst) { Default(inst); } protected internal virtual void VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst) { Default(inst); } protected internal virtual void VisitDynamicCompoundAssign(DynamicCompoundAssign inst) { Default(inst); } protected internal virtual void VisitBitNot(BitNot inst) { Default(inst); } protected internal virtual void VisitArglist(Arglist inst) { Default(inst); } protected internal virtual void VisitBranch(Branch inst) { Default(inst); } protected internal virtual void VisitLeave(Leave inst) { Default(inst); } protected internal virtual void VisitIfInstruction(IfInstruction inst) { Default(inst); } protected internal virtual void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) { Default(inst); } protected internal virtual void VisitSwitchInstruction(SwitchInstruction inst) { Default(inst); } protected internal virtual void VisitSwitchSection(SwitchSection inst) { Default(inst); } protected internal virtual void VisitTryCatch(TryCatch inst) { Default(inst); } protected internal virtual void VisitTryCatchHandler(TryCatchHandler inst) { Default(inst); } protected internal virtual void VisitTryFinally(TryFinally inst) { Default(inst); } protected internal virtual void VisitTryFault(TryFault inst) { Default(inst); } protected internal virtual void VisitLockInstruction(LockInstruction inst) { Default(inst); } protected internal virtual void VisitUsingInstruction(UsingInstruction inst) { Default(inst); } protected internal virtual void VisitDebugBreak(DebugBreak inst) { Default(inst); } protected internal virtual void VisitComp(Comp inst) { Default(inst); } protected internal virtual void VisitCall(Call inst) { Default(inst); } protected internal virtual void VisitCallVirt(CallVirt inst) { Default(inst); } protected internal virtual void VisitCallIndirect(CallIndirect inst) { Default(inst); } protected internal virtual void VisitCkfinite(Ckfinite inst) { Default(inst); } protected internal virtual void VisitConv(Conv inst) { Default(inst); } protected internal virtual void VisitLdLoc(LdLoc inst) { Default(inst); } protected internal virtual void VisitLdLoca(LdLoca inst) { Default(inst); } protected internal virtual void VisitStLoc(StLoc inst) { Default(inst); } protected internal virtual void VisitAddressOf(AddressOf inst) { Default(inst); } protected internal virtual void VisitThreeValuedBoolAnd(ThreeValuedBoolAnd inst) { Default(inst); } protected internal virtual void VisitThreeValuedBoolOr(ThreeValuedBoolOr inst) { Default(inst); } protected internal virtual void VisitNullableUnwrap(NullableUnwrap inst) { Default(inst); } protected internal virtual void VisitNullableRewrap(NullableRewrap inst) { Default(inst); } protected internal virtual void VisitLdStr(LdStr inst) { Default(inst); } protected internal virtual void VisitLdStrUtf8(LdStrUtf8 inst) { Default(inst); } protected internal virtual void VisitLdcI4(LdcI4 inst) { Default(inst); } protected internal virtual void VisitLdcI8(LdcI8 inst) { Default(inst); } protected internal virtual void VisitLdcF4(LdcF4 inst) { Default(inst); } protected internal virtual void VisitLdcF8(LdcF8 inst) { Default(inst); } protected internal virtual void VisitLdcDecimal(LdcDecimal inst) { Default(inst); } protected internal virtual void VisitLdNull(LdNull inst) { Default(inst); } protected internal virtual void VisitLdFtn(LdFtn inst) { Default(inst); } protected internal virtual void VisitLdVirtFtn(LdVirtFtn inst) { Default(inst); } protected internal virtual void VisitLdVirtDelegate(LdVirtDelegate inst) { Default(inst); } protected internal virtual void VisitLdTypeToken(LdTypeToken inst) { Default(inst); } protected internal virtual void VisitLdMemberToken(LdMemberToken inst) { Default(inst); } protected internal virtual void VisitLocAlloc(LocAlloc inst) { Default(inst); } protected internal virtual void VisitLocAllocSpan(LocAllocSpan inst) { Default(inst); } protected internal virtual void VisitCpblk(Cpblk inst) { Default(inst); } protected internal virtual void VisitInitblk(Initblk inst) { Default(inst); } protected internal virtual void VisitLdFlda(LdFlda inst) { Default(inst); } protected internal virtual void VisitLdsFlda(LdsFlda inst) { Default(inst); } protected internal virtual void VisitCastClass(CastClass inst) { Default(inst); } protected internal virtual void VisitIsInst(IsInst inst) { Default(inst); } protected internal virtual void VisitLdObj(LdObj inst) { Default(inst); } protected internal virtual void VisitLdObjIfRef(LdObjIfRef inst) { Default(inst); } protected internal virtual void VisitStObj(StObj inst) { Default(inst); } protected internal virtual void VisitBox(Box inst) { Default(inst); } protected internal virtual void VisitUnbox(Unbox inst) { Default(inst); } protected internal virtual void VisitUnboxAny(UnboxAny inst) { Default(inst); } protected internal virtual void VisitNewObj(NewObj inst) { Default(inst); } protected internal virtual void VisitNewArr(NewArr inst) { Default(inst); } protected internal virtual void VisitDefaultValue(DefaultValue inst) { Default(inst); } protected internal virtual void VisitThrow(Throw inst) { Default(inst); } protected internal virtual void VisitRethrow(Rethrow inst) { Default(inst); } protected internal virtual void VisitSizeOf(SizeOf inst) { Default(inst); } protected internal virtual void VisitLdLen(LdLen inst) { Default(inst); } protected internal virtual void VisitLdElema(LdElema inst) { Default(inst); } protected internal virtual void VisitLdElemaInlineArray(LdElemaInlineArray inst) { Default(inst); } protected internal virtual void VisitGetPinnableReference(GetPinnableReference inst) { Default(inst); } protected internal virtual void VisitStringToInt(StringToInt inst) { Default(inst); } protected internal virtual void VisitExpressionTreeCast(ExpressionTreeCast inst) { Default(inst); } protected internal virtual void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) { Default(inst); } protected internal virtual void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicBinaryOperatorInstruction(DynamicBinaryOperatorInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicUnaryOperatorInstruction(DynamicUnaryOperatorInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicConvertInstruction(DynamicConvertInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicGetMemberInstruction(DynamicGetMemberInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicGetIndexInstruction(DynamicGetIndexInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicInvokeMemberInstruction(DynamicInvokeMemberInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicInvokeConstructorInstruction(DynamicInvokeConstructorInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicInvokeInstruction(DynamicInvokeInstruction inst) { Default(inst); } protected internal virtual void VisitDynamicIsEventInstruction(DynamicIsEventInstruction inst) { Default(inst); } protected internal virtual void VisitMatchInstruction(MatchInstruction inst) { Default(inst); } protected internal virtual void VisitMakeRefAny(MakeRefAny inst) { Default(inst); } protected internal virtual void VisitRefAnyType(RefAnyType inst) { Default(inst); } protected internal virtual void VisitRefAnyValue(RefAnyValue inst) { Default(inst); } protected internal virtual void VisitYieldReturn(YieldReturn inst) { Default(inst); } protected internal virtual void VisitAwait(Await inst) { Default(inst); } protected internal virtual void VisitDeconstructInstruction(DeconstructInstruction inst) { Default(inst); } protected internal virtual void VisitDeconstructResultInstruction(DeconstructResultInstruction inst) { Default(inst); } } /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract T Default(ILInstruction inst); protected internal virtual T VisitInvalidBranch(InvalidBranch inst) { return Default(inst); } protected internal virtual T VisitInvalidExpression(InvalidExpression inst) { return Default(inst); } protected internal virtual T VisitNop(Nop inst) { return Default(inst); } protected internal virtual T VisitILFunction(ILFunction function) { return Default(function); } protected internal virtual T VisitBlockContainer(BlockContainer container) { return Default(container); } protected internal virtual T VisitBlock(Block block) { return Default(block); } protected internal virtual T VisitPinnedRegion(PinnedRegion inst) { return Default(inst); } protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { return Default(inst); } protected internal virtual T VisitNumericCompoundAssign(NumericCompoundAssign inst) { return Default(inst); } protected internal virtual T VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst) { return Default(inst); } protected internal virtual T VisitDynamicCompoundAssign(DynamicCompoundAssign inst) { return Default(inst); } protected internal virtual T VisitBitNot(BitNot inst) { return Default(inst); } protected internal virtual T VisitArglist(Arglist inst) { return Default(inst); } protected internal virtual T VisitBranch(Branch inst) { return Default(inst); } protected internal virtual T VisitLeave(Leave inst) { return Default(inst); } protected internal virtual T VisitIfInstruction(IfInstruction inst) { return Default(inst); } protected internal virtual T VisitNullCoalescingInstruction(NullCoalescingInstruction inst) { return Default(inst); } protected internal virtual T VisitSwitchInstruction(SwitchInstruction inst) { return Default(inst); } protected internal virtual T VisitSwitchSection(SwitchSection inst) { return Default(inst); } protected internal virtual T VisitTryCatch(TryCatch inst) { return Default(inst); } protected internal virtual T VisitTryCatchHandler(TryCatchHandler inst) { return Default(inst); } protected internal virtual T VisitTryFinally(TryFinally inst) { return Default(inst); } protected internal virtual T VisitTryFault(TryFault inst) { return Default(inst); } protected internal virtual T VisitLockInstruction(LockInstruction inst) { return Default(inst); } protected internal virtual T VisitUsingInstruction(UsingInstruction inst) { return Default(inst); } protected internal virtual T VisitDebugBreak(DebugBreak inst) { return Default(inst); } protected internal virtual T VisitComp(Comp inst) { return Default(inst); } protected internal virtual T VisitCall(Call inst) { return Default(inst); } protected internal virtual T VisitCallVirt(CallVirt inst) { return Default(inst); } protected internal virtual T VisitCallIndirect(CallIndirect inst) { return Default(inst); } protected internal virtual T VisitCkfinite(Ckfinite inst) { return Default(inst); } protected internal virtual T VisitConv(Conv inst) { return Default(inst); } protected internal virtual T VisitLdLoc(LdLoc inst) { return Default(inst); } protected internal virtual T VisitLdLoca(LdLoca inst) { return Default(inst); } protected internal virtual T VisitStLoc(StLoc inst) { return Default(inst); } protected internal virtual T VisitAddressOf(AddressOf inst) { return Default(inst); } protected internal virtual T VisitThreeValuedBoolAnd(ThreeValuedBoolAnd inst) { return Default(inst); } protected internal virtual T VisitThreeValuedBoolOr(ThreeValuedBoolOr inst) { return Default(inst); } protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst) { return Default(inst); } protected internal virtual T VisitNullableRewrap(NullableRewrap inst) { return Default(inst); } protected internal virtual T VisitLdStr(LdStr inst) { return Default(inst); } protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst) { return Default(inst); } protected internal virtual T VisitLdcI4(LdcI4 inst) { return Default(inst); } protected internal virtual T VisitLdcI8(LdcI8 inst) { return Default(inst); } protected internal virtual T VisitLdcF4(LdcF4 inst) { return Default(inst); } protected internal virtual T VisitLdcF8(LdcF8 inst) { return Default(inst); } protected internal virtual T VisitLdcDecimal(LdcDecimal inst) { return Default(inst); } protected internal virtual T VisitLdNull(LdNull inst) { return Default(inst); } protected internal virtual T VisitLdFtn(LdFtn inst) { return Default(inst); } protected internal virtual T VisitLdVirtFtn(LdVirtFtn inst) { return Default(inst); } protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst) { return Default(inst); } protected internal virtual T VisitLdTypeToken(LdTypeToken inst) { return Default(inst); } protected internal virtual T VisitLdMemberToken(LdMemberToken inst) { return Default(inst); } protected internal virtual T VisitLocAlloc(LocAlloc inst) { return Default(inst); } protected internal virtual T VisitLocAllocSpan(LocAllocSpan inst) { return Default(inst); } protected internal virtual T VisitCpblk(Cpblk inst) { return Default(inst); } protected internal virtual T VisitInitblk(Initblk inst) { return Default(inst); } protected internal virtual T VisitLdFlda(LdFlda inst) { return Default(inst); } protected internal virtual T VisitLdsFlda(LdsFlda inst) { return Default(inst); } protected internal virtual T VisitCastClass(CastClass inst) { return Default(inst); } protected internal virtual T VisitIsInst(IsInst inst) { return Default(inst); } protected internal virtual T VisitLdObj(LdObj inst) { return Default(inst); } protected internal virtual T VisitLdObjIfRef(LdObjIfRef inst) { return Default(inst); } protected internal virtual T VisitStObj(StObj inst) { return Default(inst); } protected internal virtual T VisitBox(Box inst) { return Default(inst); } protected internal virtual T VisitUnbox(Unbox inst) { return Default(inst); } protected internal virtual T VisitUnboxAny(UnboxAny inst) { return Default(inst); } protected internal virtual T VisitNewObj(NewObj inst) { return Default(inst); } protected internal virtual T VisitNewArr(NewArr inst) { return Default(inst); } protected internal virtual T VisitDefaultValue(DefaultValue inst) { return Default(inst); } protected internal virtual T VisitThrow(Throw inst) { return Default(inst); } protected internal virtual T VisitRethrow(Rethrow inst) { return Default(inst); } protected internal virtual T VisitSizeOf(SizeOf inst) { return Default(inst); } protected internal virtual T VisitLdLen(LdLen inst) { return Default(inst); } protected internal virtual T VisitLdElema(LdElema inst) { return Default(inst); } protected internal virtual T VisitLdElemaInlineArray(LdElemaInlineArray inst) { return Default(inst); } protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst) { return Default(inst); } protected internal virtual T VisitStringToInt(StringToInt inst) { return Default(inst); } protected internal virtual T VisitExpressionTreeCast(ExpressionTreeCast inst) { return Default(inst); } protected internal virtual T VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) { return Default(inst); } protected internal virtual T VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicBinaryOperatorInstruction(DynamicBinaryOperatorInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicUnaryOperatorInstruction(DynamicUnaryOperatorInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicConvertInstruction(DynamicConvertInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicGetMemberInstruction(DynamicGetMemberInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicGetIndexInstruction(DynamicGetIndexInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicInvokeMemberInstruction(DynamicInvokeMemberInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicInvokeConstructorInstruction(DynamicInvokeConstructorInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicInvokeInstruction(DynamicInvokeInstruction inst) { return Default(inst); } protected internal virtual T VisitDynamicIsEventInstruction(DynamicIsEventInstruction inst) { return Default(inst); } protected internal virtual T VisitMatchInstruction(MatchInstruction inst) { return Default(inst); } protected internal virtual T VisitMakeRefAny(MakeRefAny inst) { return Default(inst); } protected internal virtual T VisitRefAnyType(RefAnyType inst) { return Default(inst); } protected internal virtual T VisitRefAnyValue(RefAnyValue inst) { return Default(inst); } protected internal virtual T VisitYieldReturn(YieldReturn inst) { return Default(inst); } protected internal virtual T VisitAwait(Await inst) { return Default(inst); } protected internal virtual T VisitDeconstructInstruction(DeconstructInstruction inst) { return Default(inst); } protected internal virtual T VisitDeconstructResultInstruction(DeconstructResultInstruction inst) { return Default(inst); } } /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract T Default(ILInstruction inst, C context); protected internal virtual T VisitInvalidBranch(InvalidBranch inst, C context) { return Default(inst, context); } protected internal virtual T VisitInvalidExpression(InvalidExpression inst, C context) { return Default(inst, context); } protected internal virtual T VisitNop(Nop inst, C context) { return Default(inst, context); } protected internal virtual T VisitILFunction(ILFunction function, C context) { return Default(function, context); } protected internal virtual T VisitBlockContainer(BlockContainer container, C context) { return Default(container, context); } protected internal virtual T VisitBlock(Block block, C context) { return Default(block, context); } protected internal virtual T VisitPinnedRegion(PinnedRegion inst, C context) { return Default(inst, context); } protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitNumericCompoundAssign(NumericCompoundAssign inst, C context) { return Default(inst, context); } protected internal virtual T VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicCompoundAssign(DynamicCompoundAssign inst, C context) { return Default(inst, context); } protected internal virtual T VisitBitNot(BitNot inst, C context) { return Default(inst, context); } protected internal virtual T VisitArglist(Arglist inst, C context) { return Default(inst, context); } protected internal virtual T VisitBranch(Branch inst, C context) { return Default(inst, context); } protected internal virtual T VisitLeave(Leave inst, C context) { return Default(inst, context); } protected internal virtual T VisitIfInstruction(IfInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitNullCoalescingInstruction(NullCoalescingInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitSwitchInstruction(SwitchInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitSwitchSection(SwitchSection inst, C context) { return Default(inst, context); } protected internal virtual T VisitTryCatch(TryCatch inst, C context) { return Default(inst, context); } protected internal virtual T VisitTryCatchHandler(TryCatchHandler inst, C context) { return Default(inst, context); } protected internal virtual T VisitTryFinally(TryFinally inst, C context) { return Default(inst, context); } protected internal virtual T VisitTryFault(TryFault inst, C context) { return Default(inst, context); } protected internal virtual T VisitLockInstruction(LockInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitUsingInstruction(UsingInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDebugBreak(DebugBreak inst, C context) { return Default(inst, context); } protected internal virtual T VisitComp(Comp inst, C context) { return Default(inst, context); } protected internal virtual T VisitCall(Call inst, C context) { return Default(inst, context); } protected internal virtual T VisitCallVirt(CallVirt inst, C context) { return Default(inst, context); } protected internal virtual T VisitCallIndirect(CallIndirect inst, C context) { return Default(inst, context); } protected internal virtual T VisitCkfinite(Ckfinite inst, C context) { return Default(inst, context); } protected internal virtual T VisitConv(Conv inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdLoc(LdLoc inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdLoca(LdLoca inst, C context) { return Default(inst, context); } protected internal virtual T VisitStLoc(StLoc inst, C context) { return Default(inst, context); } protected internal virtual T VisitAddressOf(AddressOf inst, C context) { return Default(inst, context); } protected internal virtual T VisitThreeValuedBoolAnd(ThreeValuedBoolAnd inst, C context) { return Default(inst, context); } protected internal virtual T VisitThreeValuedBoolOr(ThreeValuedBoolOr inst, C context) { return Default(inst, context); } protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst, C context) { return Default(inst, context); } protected internal virtual T VisitNullableRewrap(NullableRewrap inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdStr(LdStr inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdcI4(LdcI4 inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdcI8(LdcI8 inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdcF4(LdcF4 inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdcF8(LdcF8 inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdcDecimal(LdcDecimal inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdNull(LdNull inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdFtn(LdFtn inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdVirtFtn(LdVirtFtn inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdTypeToken(LdTypeToken inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdMemberToken(LdMemberToken inst, C context) { return Default(inst, context); } protected internal virtual T VisitLocAlloc(LocAlloc inst, C context) { return Default(inst, context); } protected internal virtual T VisitLocAllocSpan(LocAllocSpan inst, C context) { return Default(inst, context); } protected internal virtual T VisitCpblk(Cpblk inst, C context) { return Default(inst, context); } protected internal virtual T VisitInitblk(Initblk inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdFlda(LdFlda inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdsFlda(LdsFlda inst, C context) { return Default(inst, context); } protected internal virtual T VisitCastClass(CastClass inst, C context) { return Default(inst, context); } protected internal virtual T VisitIsInst(IsInst inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdObj(LdObj inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdObjIfRef(LdObjIfRef inst, C context) { return Default(inst, context); } protected internal virtual T VisitStObj(StObj inst, C context) { return Default(inst, context); } protected internal virtual T VisitBox(Box inst, C context) { return Default(inst, context); } protected internal virtual T VisitUnbox(Unbox inst, C context) { return Default(inst, context); } protected internal virtual T VisitUnboxAny(UnboxAny inst, C context) { return Default(inst, context); } protected internal virtual T VisitNewObj(NewObj inst, C context) { return Default(inst, context); } protected internal virtual T VisitNewArr(NewArr inst, C context) { return Default(inst, context); } protected internal virtual T VisitDefaultValue(DefaultValue inst, C context) { return Default(inst, context); } protected internal virtual T VisitThrow(Throw inst, C context) { return Default(inst, context); } protected internal virtual T VisitRethrow(Rethrow inst, C context) { return Default(inst, context); } protected internal virtual T VisitSizeOf(SizeOf inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdLen(LdLen inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdElema(LdElema inst, C context) { return Default(inst, context); } protected internal virtual T VisitLdElemaInlineArray(LdElemaInlineArray inst, C context) { return Default(inst, context); } protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst, C context) { return Default(inst, context); } protected internal virtual T VisitStringToInt(StringToInt inst, C context) { return Default(inst, context); } protected internal virtual T VisitExpressionTreeCast(ExpressionTreeCast inst, C context) { return Default(inst, context); } protected internal virtual T VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicBinaryOperatorInstruction(DynamicBinaryOperatorInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicUnaryOperatorInstruction(DynamicUnaryOperatorInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicConvertInstruction(DynamicConvertInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicGetMemberInstruction(DynamicGetMemberInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicGetIndexInstruction(DynamicGetIndexInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicInvokeMemberInstruction(DynamicInvokeMemberInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicInvokeConstructorInstruction(DynamicInvokeConstructorInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicInvokeInstruction(DynamicInvokeInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDynamicIsEventInstruction(DynamicIsEventInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitMatchInstruction(MatchInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitMakeRefAny(MakeRefAny inst, C context) { return Default(inst, context); } protected internal virtual T VisitRefAnyType(RefAnyType inst, C context) { return Default(inst, context); } protected internal virtual T VisitRefAnyValue(RefAnyValue inst, C context) { return Default(inst, context); } protected internal virtual T VisitYieldReturn(YieldReturn inst, C context) { return Default(inst, context); } protected internal virtual T VisitAwait(Await inst, C context) { return Default(inst, context); } protected internal virtual T VisitDeconstructInstruction(DeconstructInstruction inst, C context) { return Default(inst, context); } protected internal virtual T VisitDeconstructResultInstruction(DeconstructResultInstruction inst, C context) { return Default(inst, context); } } partial class InstructionOutputExtensions { static readonly string[] originalOpCodeNames = { "invalid.branch", "invalid.expr", "nop", "ILFunction", "BlockContainer", "Block", "PinnedRegion", "binary", "numeric.compound", "user.compound", "dynamic.compound", "bit.not", "arglist", "br", "leave", "if", "if.notnull", "switch", "switch.section", "try.catch", "try.catch.handler", "try.finally", "try.fault", "lock", "using", "debug.break", "comp", "call", "callvirt", "calli", "ckfinite", "conv", "ldloc", "ldloca", "stloc", "addressof", "3vl.bool.and", "3vl.bool.or", "nullable.unwrap", "nullable.rewrap", "ldstr", "ldstr.utf8", "ldc.i4", "ldc.i8", "ldc.f4", "ldc.f8", "ldc.decimal", "ldnull", "ldftn", "ldvirtftn", "ldvirtdelegate", "ldtypetoken", "ldmembertoken", "localloc", "localloc.span", "cpblk", "initblk", "ldflda", "ldsflda", "castclass", "isinst", "ldobj", "ldobj.if.ref", "stobj", "box", "unbox", "unbox.any", "newobj", "newarr", "default.value", "throw", "rethrow", "sizeof", "ldlen", "ldelema", "ldelema.inlinearray", "get.pinnable.reference", "string.to.int", "expression.tree.cast", "user.logic.operator", "dynamic.logic.operator", "dynamic.binary.operator", "dynamic.unary.operator", "dynamic.convert", "dynamic.getmember", "dynamic.setmember", "dynamic.getindex", "dynamic.setindex", "dynamic.invokemember", "dynamic.invokeconstructor", "dynamic.invoke", "dynamic.isevent", "match", "mkrefany", "refanytype", "refanyval", "yield.return", "await", "deconstruct", "deconstruct.result", "AnyNode", }; } partial class ILInstruction { public bool MatchInvalidBranch() { var inst = this as InvalidBranch; if (inst != null) { return true; } return false; } public bool MatchInvalidExpression() { var inst = this as InvalidExpression; if (inst != null) { return true; } return false; } public bool MatchNop() { var inst = this as Nop; if (inst != null) { return true; } return false; } public bool MatchPinnedRegion([NotNullWhen(true)] out ILVariable? variable, [NotNullWhen(true)] out ILInstruction? init, [NotNullWhen(true)] out ILInstruction? body) { var inst = this as PinnedRegion; if (inst != null) { variable = inst.Variable; init = inst.Init; body = inst.Body; return true; } variable = default(ILVariable); init = default(ILInstruction); body = default(ILInstruction); return false; } public bool MatchArglist() { var inst = this as Arglist; if (inst != null) { return true; } return false; } public bool MatchTryCatchHandler([NotNullWhen(true)] out ILInstruction? filter, [NotNullWhen(true)] out ILInstruction? body, [NotNullWhen(true)] out ILVariable? variable) { var inst = this as TryCatchHandler; if (inst != null) { filter = inst.Filter; body = inst.Body; variable = inst.Variable; return true; } filter = default(ILInstruction); body = default(ILInstruction); variable = default(ILVariable); return false; } public bool MatchLockInstruction([NotNullWhen(true)] out ILInstruction? onExpression, [NotNullWhen(true)] out ILInstruction? body) { var inst = this as LockInstruction; if (inst != null) { onExpression = inst.OnExpression; body = inst.Body; return true; } onExpression = default(ILInstruction); body = default(ILInstruction); return false; } public bool MatchUsingInstruction([NotNullWhen(true)] out ILVariable? variable, [NotNullWhen(true)] out ILInstruction? resourceExpression, [NotNullWhen(true)] out ILInstruction? body) { var inst = this as UsingInstruction; if (inst != null) { variable = inst.Variable; resourceExpression = inst.ResourceExpression; body = inst.Body; return true; } variable = default(ILVariable); resourceExpression = default(ILInstruction); body = default(ILInstruction); return false; } public bool MatchDebugBreak() { var inst = this as DebugBreak; if (inst != null) { return true; } return false; } public bool MatchCkfinite([NotNullWhen(true)] out ILInstruction? argument) { var inst = this as Ckfinite; if (inst != null) { argument = inst.Argument; return true; } argument = default(ILInstruction); return false; } public bool MatchLdLoc([NotNullWhen(true)] out ILVariable? variable) { var inst = this as LdLoc; if (inst != null) { variable = inst.Variable; return true; } variable = default(ILVariable); return false; } public bool MatchLdLoca([NotNullWhen(true)] out ILVariable? variable) { var inst = this as LdLoca; if (inst != null) { variable = inst.Variable; return true; } variable = default(ILVariable); return false; } public bool MatchStLoc([NotNullWhen(true)] out ILVariable? variable, [NotNullWhen(true)] out ILInstruction? value) { var inst = this as StLoc; if (inst != null) { variable = inst.Variable; value = inst.Value; return true; } variable = default(ILVariable); value = default(ILInstruction); return false; } public bool MatchAddressOf([NotNullWhen(true)] out ILInstruction? value, [NotNullWhen(true)] out IType? type) { var inst = this as AddressOf; if (inst != null) { value = inst.Value; type = inst.Type; return true; } value = default(ILInstruction); type = default(IType); return false; } public bool MatchThreeValuedBoolAnd([NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { var inst = this as ThreeValuedBoolAnd; if (inst != null) { left = inst.Left; right = inst.Right; return true; } left = default(ILInstruction); right = default(ILInstruction); return false; } public bool MatchThreeValuedBoolOr([NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { var inst = this as ThreeValuedBoolOr; if (inst != null) { left = inst.Left; right = inst.Right; return true; } left = default(ILInstruction); right = default(ILInstruction); return false; } public bool MatchNullableRewrap([NotNullWhen(true)] out ILInstruction? argument) { var inst = this as NullableRewrap; if (inst != null) { argument = inst.Argument; return true; } argument = default(ILInstruction); return false; } public bool MatchLdStr([NotNullWhen(true)] out string? value) { var inst = this as LdStr; if (inst != null) { value = inst.Value; return true; } value = default(string); return false; } public bool MatchLdStrUtf8([NotNullWhen(true)] out string? value) { var inst = this as LdStrUtf8; if (inst != null) { value = inst.Value; return true; } value = default(string); return false; } public bool MatchLdcI4(out int value) { var inst = this as LdcI4; if (inst != null) { value = inst.Value; return true; } value = default(int); return false; } public bool MatchLdcI8(out long value) { var inst = this as LdcI8; if (inst != null) { value = inst.Value; return true; } value = default(long); return false; } public bool MatchLdcF4(out float value) { var inst = this as LdcF4; if (inst != null) { value = inst.Value; return true; } value = default(float); return false; } public bool MatchLdcF8(out double value) { var inst = this as LdcF8; if (inst != null) { value = inst.Value; return true; } value = default(double); return false; } public bool MatchLdcDecimal(out decimal value) { var inst = this as LdcDecimal; if (inst != null) { value = inst.Value; return true; } value = default(decimal); return false; } public bool MatchLdNull() { var inst = this as LdNull; if (inst != null) { return true; } return false; } public bool MatchLdFtn([NotNullWhen(true)] out IMethod? method) { var inst = this as LdFtn; if (inst != null) { method = inst.Method; return true; } method = default(IMethod); return false; } public bool MatchLdVirtFtn([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IMethod? method) { var inst = this as LdVirtFtn; if (inst != null) { argument = inst.Argument; method = inst.Method; return true; } argument = default(ILInstruction); method = default(IMethod); return false; } public bool MatchLdVirtDelegate([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out IMethod? method) { var inst = this as LdVirtDelegate; if (inst != null) { argument = inst.Argument; type = inst.Type; method = inst.Method; return true; } argument = default(ILInstruction); type = default(IType); method = default(IMethod); return false; } public bool MatchLdTypeToken([NotNullWhen(true)] out IType? type) { var inst = this as LdTypeToken; if (inst != null) { type = inst.Type; return true; } type = default(IType); return false; } public bool MatchLdMemberToken([NotNullWhen(true)] out IMember? member) { var inst = this as LdMemberToken; if (inst != null) { member = inst.Member; return true; } member = default(IMember); return false; } public bool MatchLocAlloc([NotNullWhen(true)] out ILInstruction? argument) { var inst = this as LocAlloc; if (inst != null) { argument = inst.Argument; return true; } argument = default(ILInstruction); return false; } public bool MatchLocAllocSpan([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as LocAllocSpan; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchCpblk([NotNullWhen(true)] out ILInstruction? destAddress, [NotNullWhen(true)] out ILInstruction? sourceAddress, [NotNullWhen(true)] out ILInstruction? size) { var inst = this as Cpblk; if (inst != null) { destAddress = inst.DestAddress; sourceAddress = inst.SourceAddress; size = inst.Size; return true; } destAddress = default(ILInstruction); sourceAddress = default(ILInstruction); size = default(ILInstruction); return false; } public bool MatchInitblk([NotNullWhen(true)] out ILInstruction? address, [NotNullWhen(true)] out ILInstruction? value, [NotNullWhen(true)] out ILInstruction? size) { var inst = this as Initblk; if (inst != null) { address = inst.Address; value = inst.Value; size = inst.Size; return true; } address = default(ILInstruction); value = default(ILInstruction); size = default(ILInstruction); return false; } public bool MatchLdFlda([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IField? @field) { var inst = this as LdFlda; if (inst != null) { target = inst.Target; @field = inst.Field; return true; } target = default(ILInstruction); @field = default(IField); return false; } public bool MatchLdsFlda([NotNullWhen(true)] out IField? @field) { var inst = this as LdsFlda; if (inst != null) { @field = inst.Field; return true; } @field = default(IField); return false; } public bool MatchCastClass([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as CastClass; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchIsInst([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as IsInst; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchLdObj([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IType? type) { var inst = this as LdObj; if (inst != null) { target = inst.Target; type = inst.Type; return true; } target = default(ILInstruction); type = default(IType); return false; } public bool MatchLdObjIfRef([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IType? type) { var inst = this as LdObjIfRef; if (inst != null) { target = inst.Target; type = inst.Type; return true; } target = default(ILInstruction); type = default(IType); return false; } public bool MatchStObj([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out ILInstruction? value, [NotNullWhen(true)] out IType? type) { var inst = this as StObj; if (inst != null) { target = inst.Target; value = inst.Value; type = inst.Type; return true; } target = default(ILInstruction); value = default(ILInstruction); type = default(IType); return false; } public bool MatchBox([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as Box; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchUnbox([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as Unbox; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchUnboxAny([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as UnboxAny; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchNewArr([NotNullWhen(true)] out IType? type) { var inst = this as NewArr; if (inst != null) { type = inst.Type; return true; } type = default(IType); return false; } public bool MatchDefaultValue([NotNullWhen(true)] out IType? type) { var inst = this as DefaultValue; if (inst != null) { type = inst.Type; return true; } type = default(IType); return false; } public bool MatchThrow([NotNullWhen(true)] out ILInstruction? argument) { var inst = this as Throw; if (inst != null) { argument = inst.Argument; return true; } argument = default(ILInstruction); return false; } public bool MatchRethrow() { var inst = this as Rethrow; if (inst != null) { return true; } return false; } public bool MatchSizeOf([NotNullWhen(true)] out IType? type) { var inst = this as SizeOf; if (inst != null) { type = inst.Type; return true; } type = default(IType); return false; } public bool MatchLdElema([NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? array) { var inst = this as LdElema; if (inst != null) { type = inst.Type; array = inst.Array; return true; } type = default(IType); array = default(ILInstruction); return false; } public bool MatchLdElemaInlineArray([NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? array) { var inst = this as LdElemaInlineArray; if (inst != null) { type = inst.Type; array = inst.Array; return true; } type = default(IType); array = default(ILInstruction); return false; } public bool MatchGetPinnableReference([NotNullWhen(true)] out ILInstruction? argument, out IMethod? method) { var inst = this as GetPinnableReference; if (inst != null) { argument = inst.Argument; method = inst.Method; return true; } argument = default(ILInstruction); method = default(IMethod?); return false; } public bool MatchUserDefinedLogicOperator([NotNullWhen(true)] out IMethod? method, [NotNullWhen(true)] out ILInstruction? left, [NotNullWhen(true)] out ILInstruction? right) { var inst = this as UserDefinedLogicOperator; if (inst != null) { method = inst.Method; left = inst.Left; right = inst.Right; return true; } method = default(IMethod); left = default(ILInstruction); right = default(ILInstruction); return false; } public bool MatchMatchInstruction([NotNullWhen(true)] out ILVariable? variable, out IMethod? method, [NotNullWhen(true)] out ILInstruction? testedOperand) { var inst = this as MatchInstruction; if (inst != null) { variable = inst.Variable; method = inst.Method; testedOperand = inst.TestedOperand; return true; } variable = default(ILVariable); method = default(IMethod?); testedOperand = default(ILInstruction); return false; } public bool MatchMakeRefAny([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as MakeRefAny; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchRefAnyType([NotNullWhen(true)] out ILInstruction? argument) { var inst = this as RefAnyType; if (inst != null) { argument = inst.Argument; return true; } argument = default(ILInstruction); return false; } public bool MatchRefAnyValue([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as RefAnyValue; if (inst != null) { argument = inst.Argument; type = inst.Type; return true; } argument = default(ILInstruction); type = default(IType); return false; } public bool MatchYieldReturn([NotNullWhen(true)] out ILInstruction? value) { var inst = this as YieldReturn; if (inst != null) { value = inst.Value; return true; } value = default(ILInstruction); return false; } public bool MatchAwait([NotNullWhen(true)] out ILInstruction? value) { var inst = this as Await; if (inst != null) { value = inst.Value; return true; } value = default(ILInstruction); return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Instructions.tt ================================================ // Copyright (c) 2014-2020 Daniel Grunwald // // 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. #nullable enable <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <# OpCode[] baseClasses = { new OpCode("SimpleInstruction", "Instruction without any arguments", AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")), new OpCode("UnaryInstruction", "Instruction with a single argument", AbstractBaseClass, CustomArguments(("argument", null)), HasFlag("InstructionFlags.None")), new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right", AbstractBaseClass, CustomArguments(("left", null), ("right", null)), HasFlag("InstructionFlags.None")), new OpCode("CallInstruction", "Instruction with a list of arguments.", AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomConstructor, CustomWriteTo, MayThrow, SideEffect), new OpCode("PatternInstruction", "Base class for pattern matching in ILAst.", AbstractBaseClass, ResultType("Unknown")) { Namespace = "ICSharpCode.Decompiler.IL.Patterns" }, new OpCode("CompoundAssignmentInstruction", "Common instruction for compound assignments.", AbstractBaseClass, CustomConstructor, CustomArguments(("target", null), ("value", null))), new OpCode("DynamicInstruction", "Instruction representing a dynamic call site.", AbstractBaseClass, CustomWriteTo, MayThrow, SideEffect) }; OpCode[] opCodes = { new OpCode("invalid.branch", "Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.", CustomClassName("InvalidBranch"), NoArguments, MayThrow, SideEffect, HasFlag("InstructionFlags.EndPointUnreachable")), new OpCode("invalid.expr", "Represents invalid IL. Semantically, this instruction is considered to produce some kind of value.", CustomClassName("InvalidExpression"), NoArguments, MayThrow, SideEffect), new OpCode("nop", "No operation. Takes 0 arguments and returns void.", VoidResult, NoArguments, CustomWriteTo), new OpCode("ILFunction", "A container of IL blocks.", CustomChildren(new [] { new ChildInfo("body"), new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" } }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("DelegateType?.GetStackType() ?? StackType.O") ), new OpCode("BlockContainer", "A container of IL blocks.", ResultType("this.ExpectedResultType"), CustomConstructor, CustomVariableName("container"), MatchCondition("Patterns.ListMatch.DoMatch(this.Blocks, o.Blocks, ref match)")), new OpCode("Block", "A block of IL instructions.", CustomConstructor, CustomVariableName("block"), MatchCondition("this.Kind == o.Kind"), MatchCondition("Patterns.ListMatch.DoMatch(this.Instructions, o.Instructions, ref match)"), MatchCondition("this.FinalInstruction.PerformMatch(o.FinalInstruction, ref match)")), new OpCode("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement).", ResultType("Void"), HasVariableOperand("Store"), CustomChildren(new []{ new ChildInfo("init") { CanInlineInto = true }, new ChildInfo("body") }), CustomInvariant("DebugAssert(Variable.Kind == VariableKind.PinnedRegionLocal);")), new OpCode("binary", "Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.", CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), new OpCode("numeric.compound", "Common instruction for numeric compound assignments.", CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), MatchCondition("this.EvalMode == o.EvalMode"), MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("user.compound", "Common instruction for user-defined compound assignments.", CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, MayThrow, SideEffect, CustomWriteTo, MatchCondition("this.Method.Equals(o.Method)"), MatchCondition("this.EvalMode == o.EvalMode"), MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), MatchCondition("this.EvalMode == o.EvalMode"), MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("br", "Unconditional branch. goto target;", CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, MatchCondition("this.TargetBlock == o.TargetBlock")), new OpCode("leave", "Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.", CustomConstructor, CustomArguments(("value", null)), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags, MatchCondition("this.TargetContainer == o.TargetContainer")), new OpCode("if", "If statement / conditional expression. if (condition) trueExpr else falseExpr", CustomClassName("IfInstruction"), CustomChildren(new []{ new ArgumentInfo("condition"), new ChildInfo("trueInst"), new ChildInfo("falseInst"), }), CustomConstructor, CustomComputeFlags, CustomWriteTo), new OpCode("if.notnull", "Null coalescing operator expression. if.notnull(valueInst, fallbackInst)", CustomClassName("NullCoalescingInstruction"), CustomChildren(new []{ new ChildInfo("valueInst") { CanInlineInto = true }, new ChildInfo("fallbackInst"), }), CustomConstructor, CustomComputeFlags, CustomWriteTo), new OpCode("switch", "Switch statement", CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")), new OpCode("switch.section", "Switch section within a switch statement", CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"), MatchCondition("this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel")), new OpCode("try.catch", "Try-catch statement.", BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"), MatchCondition("Patterns.ListMatch.DoMatch(Handlers, o.Handlers, ref match)")), new OpCode("try.catch.handler", "Catch handler within a try-catch statement.", CustomChildren(new [] { new ChildInfo("filter"), new ChildInfo("body"), }), HasVariableOperand("Store", generateCheckInvariant: false), CustomWriteTo, CustomComputeFlags), new OpCode("try.finally", "Try-finally statement", BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match) && finallyBlock.PerformMatch(o.finallyBlock, ref match)")), new OpCode("try.fault", "Try-fault statement", BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"), MatchCondition("faultBlock.PerformMatch(o.faultBlock, ref match)")), new OpCode("lock", "Lock statement", CustomClassName("LockInstruction"), CustomChildren(new [] { new ArgumentInfo("onExpression") { ExpectedTypes = new[] { "O" }}, new ChildInfo("body") }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"), CustomChildren(new [] { new ArgumentInfo("resourceExpression") { ExpectedTypes = new[] { "O" }}, new ChildInfo("body") }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), new OpCode("debug.break", "Breakpoint instruction", NoArguments, VoidResult, SideEffect), new OpCode("comp", "Comparison. The inputs must be both integers; or both floats; or both object references. " + "Object references can only be compared for equality or inequality. " + "Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which " + "evaluates to 1 (true).", Binary, CustomConstructor, CustomWriteTo, MatchCondition("this.Kind == o.Kind && this.Sign == o.Sign && this.LiftingKind == o.LiftingKind")), new OpCode("call", "Non-virtual method call.", Call), new OpCode("callvirt", "Virtual method call.", CustomClassName("CallVirt"), Call), new OpCode("calli", "Unsafe function pointer call.", CustomClassName("CallIndirect"), CustomConstructor, CustomWriteTo, MatchCondition("EqualSignature(o)"), MatchCondition("Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match)"), MatchCondition("this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match)")), new OpCode("ckfinite", "Checks that the input float is not NaN or infinite.", Unary, MayThrow, VoidResult), new OpCode("conv", "Numeric cast.", Unary, CustomConstructor, MatchCondition("CheckForOverflow == o.CheckForOverflow && Kind == o.Kind && InputSign == o.InputSign && TargetType == o.TargetType && IsLifted == o.IsLifted")), new OpCode("ldloc", "Loads the value of a local variable. (ldarg/ldloc)", CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")), new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)", CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")), new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine + "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())", CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments(("value", null)), ResultType("variable.StackType")), new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("Ref"), HasTypeOperand), new OpCode("3vl.bool.and", "Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior.", CustomClassName("ThreeValuedBoolAnd"), Binary, ResultType("O")), new OpCode("3vl.bool.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.", CustomClassName("ThreeValuedBoolOr"), Binary, ResultType("O")), new OpCode("nullable.unwrap", "The input operand must be one of:" + Environment.NewLine + " 1. a nullable value type" + Environment.NewLine + " 2. a reference type" + Environment.NewLine + " 3. a managed reference to a type parameter." + Environment.NewLine + "If the input is non-null, evaluates to the (unwrapped) input." + Environment.NewLine + "If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction." + Environment.NewLine + "In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction " + "returns the managed reference unmodified (if the value is non-null).", Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)." + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + "the nullable.rewrap instruction evaluates to null.", Unary, CustomComputeFlags), new OpCode("ldstr", "Loads a constant string.", CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")), new OpCode("ldstr.utf8", "Loads a constant byte string (as ReadOnlySpan<byte>).", CustomClassName("LdStrUtf8"), LoadConstant("string"), ResultType("O")), new OpCode("ldc.i4", "Loads a constant 32-bit integer.", LoadConstant("int"), ResultType("I4")), new OpCode("ldc.i8", "Loads a constant 64-bit integer.", LoadConstant("long"), ResultType("I8")), new OpCode("ldc.f4", "Loads a constant 32-bit floating-point number.", LoadConstant("float"), ResultType("F4")), new OpCode("ldc.f8", "Loads a constant 64-bit floating-point number.", LoadConstant("double"), ResultType("F8")), new OpCode("ldc.decimal", "Loads a constant decimal.", LoadConstant("decimal"), ResultType("O")), new OpCode("ldnull", "Loads the null reference.", CustomClassName("LdNull"), NoArguments, ResultType("O")), new OpCode("ldftn", "Load method pointer", CustomClassName("LdFtn"), NoArguments, HasMethodOperand(), ResultType("I")), new OpCode("ldvirtftn", "Load method pointer", CustomClassName("LdVirtFtn"), Unary, HasMethodOperand(), MayThrow, ResultType("I")), new OpCode("ldvirtdelegate", "Virtual delegate construction", CustomClassName("LdVirtDelegate"), Unary, HasTypeOperand, HasMethodOperand(), MayThrow, ResultType("O")), new OpCode("ldtypetoken", "Loads runtime representation of metadata token", CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), new OpCode("ldmembertoken", "Loads runtime representation of metadata token", CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")), new OpCode("localloc", "Allocates space in the stack frame", CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow), new OpCode("localloc.span", "Allocates space in the stack frame and wraps it in a Span", CustomClassName("LocAllocSpan"), Unary, HasTypeOperand, ResultType("O"), MayThrow), new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);", CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })), MayThrow, MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), new OpCode("initblk", "memset(address, value, size)", CustomArguments(("address", new[] { "I", "Ref" }), ("value", new[] { "I4" }), ("size", new[] { "I4" })), MayThrow, MemoryAccess, SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), new OpCode("ldflda", "Load address of instance field", CustomClassName("LdFlda"), CustomArguments(("target", null)), MayThrowIfNotDelayed, HasFieldOperand, ResultType("target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")), new OpCode("ldsflda", "Load static field address", CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand), new OpCode("castclass", "Casts an object to a class.", CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")), new OpCode("isinst", "Test if object is instance of class or interface.", CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")), new OpCode("ldobj", "Indirect load (ref/pointer dereference).", CustomClassName("LdObj"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), new OpCode("ldobj.if.ref", "If argument is a ref to a reference type, loads the object reference, stores it in a temporary, and evaluates to the address of that temporary (address.of(ldobj(arg))). Otherwise, returns the argument ref as-is.This instruction represents the memory-load semantics of callvirt with a generic type as receiver (where the IL always takes a ref, but only methods on value types expect one, for method on reference types there's an implicit ldobj, which this instruction makes explicit in order to preserve the order-of-evaluation).", CustomClassName("LdObjIfRef"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, MayThrow, ResultType("Ref")), new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine + "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign())", CustomClassName("StObj"), CustomArguments(("target", new[] { "Ref", "I" }), ("value", new[] { "type.GetStackType()" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void"), CustomInvariant("CheckTargetSlot();")), new OpCode("box", "Boxes a value.", Unary, HasTypeOperand, ResultType("O")), new OpCode("unbox", "Compute address inside box.", Unary, HasTypeOperand, MayThrow, ResultType("Ref")), new OpCode("unbox.any", "Unbox a value.", Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("type.GetStackType()")), new OpCode("newobj", "Creates an object instance and calls the constructor.", CustomClassName("NewObj"), Call, ResultType("Method.DeclaringType.GetStackType()")), new OpCode("newarr", "Creates an array instance.", CustomClassName("NewArr"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("indices") { IsCollection = true } }, true), MayThrow, ResultType("O")), new OpCode("default.value", "Returns the default value for a type.", NoArguments, HasTypeOperand, ResultType("type.GetStackType()")), new OpCode("throw", "Throws an exception.", Unary, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable"), ResultType("this.resultType")), new OpCode("rethrow", "Rethrows the current exception.", NoArguments, MayThrow, UnconditionalBranch), new OpCode("sizeof", "Gets the size of a type in bytes.", CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")), new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.", CustomClassName("LdLen"), CustomArguments(("array", new[] { "O" })), CustomConstructor, CustomWriteTo, MayThrow), new OpCode("ldelema", "Load address of array element.", CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), BoolFlag("WithSystemIndex"), MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix), new OpCode("ldelema.inlinearray", "Load address of inline array element.", CustomClassName("LdElemaInlineArray"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), MayThrow, ResultType("Ref"), SupportsReadonlyPrefix), new OpCode("get.pinnable.reference", "Retrieves a pinnable reference for the input object." + Environment.NewLine + "The input must be an object reference (O)." + Environment.NewLine + "If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty." + Environment.NewLine + "Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null." + Environment.NewLine, CustomArguments(("argument", new[] { "O" })), ResultType("Ref"), HasMethodOperand(nullable: true)), new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).", CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")), new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.", CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), MatchCondition("this.IsChecked == o.IsChecked")), new OpCode("user.logic.operator", "Use of user-defined && or || operator.", CustomClassName("UserDefinedLogicOperator"), HasMethodOperand(), ResultType("O"), CustomChildren(new []{ new ChildInfo("left") { CanInlineInto = true }, new ChildInfo("right") { CanInlineInto = false } // only executed depending on value of left }), CustomComputeFlags // MayThrow, SideEffect, ControlFlow ), new OpCode("dynamic.logic.operator", "ILAst representation of a short-circuiting binary operator inside a dynamic expression.", CustomClassName("DynamicLogicOperatorInstruction"), Dynamic, CustomComputeFlags, CustomChildren(new []{ new ChildInfo("left") { CanInlineInto = true }, new ChildInfo("right") { CanInlineInto = false } // only executed depending on value of left }), CustomWriteTo ), new OpCode("dynamic.binary.operator", "ILAst representation of a binary operator inside a dynamic expression (maps to Binder.BinaryOperation).", CustomClassName("DynamicBinaryOperatorInstruction"), Dynamic, CustomArguments(("left", null), ("right", null)), CustomWriteTo), new OpCode("dynamic.unary.operator", "ILAst representation of a unary operator inside a dynamic expression (maps to Binder.UnaryOperation).", CustomClassName("DynamicUnaryOperatorInstruction"), Dynamic, CustomArguments(("operand", null)), CustomWriteTo), new OpCode("dynamic.convert", "ILAst representation of a cast inside a dynamic expression (maps to Binder.Convert).", CustomClassName("DynamicConvertInstruction"), Dynamic, HasTypeOperand, CustomArguments(("argument", new[] { "O" })), CustomWriteTo), new OpCode("dynamic.getmember", "ILAst representation of a property get method call inside a dynamic expression (maps to Binder.GetMember).", CustomClassName("DynamicGetMemberInstruction"), Dynamic, CustomArguments(("target", new[] { "O" })), CustomConstructor, CustomWriteTo), new OpCode("dynamic.setmember", "ILAst representation of a property set method call inside a dynamic expression (maps to Binder.SetMember).", CustomClassName("DynamicSetMemberInstruction"), Dynamic, CustomArguments(("target", new[] { "O" }), ("value", null)), CustomWriteTo), new OpCode("dynamic.getindex", "ILAst representation of an indexer get method call inside a dynamic expression (maps to Binder.GetIndex).", CustomClassName("DynamicGetIndexInstruction"), Dynamic, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomWriteTo), new OpCode("dynamic.setindex", "ILAst representation of an indexer set method call inside a dynamic expression (maps to Binder.SetIndex).", CustomClassName("DynamicSetIndexInstruction"), Dynamic, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomWriteTo), new OpCode("dynamic.invokemember", "ILAst representation of a method call inside a dynamic expression (maps to Binder.InvokeMember).", CustomClassName("DynamicInvokeMemberInstruction"), Dynamic, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomWriteTo), new OpCode("dynamic.invokeconstructor", "ILAst representation of a constuctor invocation inside a dynamic expression (maps to Binder.InvokeConstructor).", CustomClassName("DynamicInvokeConstructorInstruction"), Dynamic, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomWriteTo), new OpCode("dynamic.invoke", "ILAst representation of a delegate invocation inside a dynamic expression (maps to Binder.Invoke).", CustomClassName("DynamicInvokeInstruction"), Dynamic, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomWriteTo), new OpCode("dynamic.isevent", "ILAst representation of a call to the Binder.IsEvent method inside a dynamic expression.", CustomClassName("DynamicIsEventInstruction"), Dynamic, CustomArguments(("argument", new[] { "O" })), CustomWriteTo), new OpCode("match", "ILAst representation of C# patterns", CustomClassName("MatchInstruction"), HasVariableOperand("Store"), HasMethodOperand(nullable: true), BoolFlag("IsDeconstructCall"), BoolFlag("IsDeconstructTuple"), BoolFlag("CheckType"), BoolFlag("CheckNotNull"), CustomChildren(new []{ new ChildInfo("testedOperand") { CanInlineInto = true }, new ChildInfo("subPatterns") { IsCollection = true } }), ResultType("I4"), CustomWriteTo, SideEffect, MayThrow, ControlFlow, CustomInvariant("AdditionalInvariants();")), new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")), new OpCode("refanytype", "Push the type token stored in a typed reference.", CustomClassName("RefAnyType"), Unary, ResultType("O")), new OpCode("refanyval", "Push the address stored in a typed reference.", CustomClassName("RefAnyValue"), Unary, HasTypeOperand, MayThrow, ResultType("Ref")), new OpCode("yield.return", "Yield an element from an iterator.", MayBranch, // yield return may end up returning if the consumer disposes the iterator SideEffect, // consumer can have arbitrary side effects while we're yielding CustomArguments(("value", null)), VoidResult), // note: "yield break" is always represented using a "leave" instruction new OpCode("await", "C# await operator.", SideEffect, // other code can run with arbitrary side effects while we're waiting CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")), new OpCode("deconstruct", "Deconstruction statement", CustomClassName("DeconstructInstruction"), CustomConstructor, ResultType("Void"), CustomWriteTo), new OpCode("deconstruct.result", "Represents a deconstructed value", CustomClassName("DeconstructResultInstruction"), CustomConstructor, CustomInvariant("AdditionalInvariants();"), Unary, CustomWriteTo), // patterns new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor), }; #> using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { /// /// Enum representing the type of an . /// public enum OpCode : byte { <# foreach (OpCode opCode in opCodes) { #> /// <#=opCode.Description.Replace("\n", "\n\t\t/// ")#> <#=opCode.Name#>, <# } #> } } <# foreach (OpCode opCode in baseClasses.Concat(opCodes)) { #> namespace <#=opCode.Namespace#> { /// <#=opCode.Description.Replace("\n", "\n\t/// ")#> <#=opCode.ClassModifiers#> partial class <#=opCode.Name#> : <#=string.Join(", ", new[]{opCode.BaseClass}.Concat(opCode.Interfaces))#> { <# if (opCode.GenerateConstructor) { #> <#=opCode.ConstructorModifier#> <#=opCode.Name#>(<#=string.Join(", ", opCode.ConstructorParameters)#>) : base(<#=string.Join(", ", opCode.BaseConstructorArguments)#>) {<#=Body(opCode.ConstructorBody)#>} <# } #> <#=string.Join(Environment.NewLine, opCode.Members.Select(m => "\t\t" + m.Replace("\n", "\n\t\t")))#> <# if (opCode.GenerateComputeFlags && opCode.Flags.Any(f => f != "base.ComputeFlags()")) { #> protected override InstructionFlags ComputeFlags() { return <#=string.Join(" | ", opCode.Flags)#>; } <# } #> <# if (opCode.GenerateComputeFlags && opCode.Flags.Any(f => f != "base.ComputeFlags()")) { #> public override InstructionFlags DirectFlags { get { return <#=opCode.DirectFlags.Count > 0 ? string.Join(" | ", opCode.DirectFlags) : "InstructionFlags.None"#>; } } <# } #> <# if (opCode.GenerateWriteTo) { #> <# if (opCode.CustomWriteToButKeepOriginal) { #> void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options) <# } else { #> public override void WriteTo(ITextOutput output, ILAstWritingOptions options) <# } #> {<#=Body(opCode.WriteToBody)#>} <# } #> <# if (opCode.GenerateAcceptVisitor) { #> public override void AcceptVisitor(ILVisitor visitor) { visitor.Visit<#=opCode.Name#>(this); } public override T AcceptVisitor(ILVisitor visitor) { return visitor.Visit<#=opCode.Name#>(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { return visitor.Visit<#=opCode.Name#>(this, context); } <# } #> <# if (opCode.GeneratePerformMatch) { #> protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as <#=opCode.Name#>; return <#=string.Join(" && ", opCode.PerformMatchConditions)#>; } <# } #> <# if (opCode.Invariants.Count > 0) { #> internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); <# foreach (var invariant in opCode.Invariants) {#> <#=invariant#> <# } #> } <# } #> } } <# } #> namespace ICSharpCode.Decompiler.IL { /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract void Default(ILInstruction inst); <# foreach (OpCode opCode in opCodes.Where(o => o.GenerateAcceptVisitor)) { #> protected internal virtual void Visit<#=opCode.Name#>(<#=opCode.Name#> <#=opCode.VariableName#>) { Default(<#=opCode.VariableName#>); } <# } #> } /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract T Default(ILInstruction inst); <# foreach (OpCode opCode in opCodes.Where(o => o.GenerateAcceptVisitor)) { #> protected internal virtual T Visit<#=opCode.Name#>(<#=opCode.Name#> <#=opCode.VariableName#>) { return Default(<#=opCode.VariableName#>); } <# } #> } /// /// Base class for visitor pattern. /// public abstract class ILVisitor { /// Called by Visit*() methods that were not overridden protected abstract T Default(ILInstruction inst, C context); <# foreach (OpCode opCode in opCodes.Where(o => o.GenerateAcceptVisitor)) { #> protected internal virtual T Visit<#=opCode.Name#>(<#=opCode.Name#> <#=opCode.VariableName#>, C context) { return Default(<#=opCode.VariableName#>, context); } <# } #> } partial class InstructionOutputExtensions { static readonly string[] originalOpCodeNames = { <# foreach (OpCode opCode in opCodes) { #> "<#=opCode.OriginalName#>", <# } #> }; } partial class ILInstruction { <# foreach (OpCode opCode in opCodes) { #> <# if (opCode.GenerateMatch) { #> public bool Match<#=opCode.Name#>(<#=string.Join(", ", opCode.MatchParameters)#>) { var inst = this as <#=opCode.Name#>; if (inst != null) { <# foreach (var parameter in opCode.MatchParameters) {#> <#=parameter.Name#> = inst.<#=parameter.FieldName#>; <# }#> return true; } <# foreach (var parameter in opCode.MatchParameters) {#> <#=parameter.Name#> = default(<#=parameter.TypeName#>); <# }#> return false; } <# } } #> } } <#+ static string Body(IEnumerable statements) { StringBuilder b = new StringBuilder(); foreach (var st in statements) { b.AppendLine(); b.Append("\t\t\t"); b.Append(st.Replace("\n", "\n\t\t\t")); } b.AppendLine(); b.Append("\t\t"); return b.ToString(); } static string MakeName(string originalName) { StringBuilder name = new StringBuilder(); bool nextUpper = true; foreach (char c in originalName) { if (c == '.') nextUpper = true; else if (nextUpper) { name.Append(char.ToUpper(c)); nextUpper = false; } else name.Append(c); } return name.ToString(); } class OpCode { public readonly string OriginalName; public string Name; public string Namespace = "ICSharpCode.Decompiler.IL"; public readonly string Description; public string VariableName = "inst"; public OpCode(string originalName, string description, params Action[] traits) { this.OriginalName = originalName; this.Name = MakeName(originalName); this.Description = description; foreach (var trait in traits) trait(this); if (this.BaseConstructorArguments.FirstOrDefault() != "opCode") this.BaseConstructorArguments.Insert(0, "OpCode." + this.Name); } public string ClassModifiers = "public sealed"; public bool GenerateConstructor = true; public string ConstructorModifier = "public"; public List ConstructorParameters = new List(); public List ConstructorBody = new List(); public bool GenerateMatch = true; public bool GeneratePerformMatch = true; public List MatchParameters = new List(); public List PerformMatchConditions = new List() { "o != null" }; public List Invariants = new List(); public string BaseClass = "ILInstruction"; public List Interfaces = new List(); public List BaseConstructorArguments = new List(); public List Members = new List(); public List Flags = new List(); public List DirectFlags = new List(); public bool GenerateComputeFlags = true; public bool GenerateAcceptVisitor = true; public bool GenerateWriteTo = false; public bool CustomWriteToButKeepOriginal = false; public List WriteOpCodePrefix = new List(); public List WriteOpCodeSuffix = new List(); public List WriteOperand = new List(); public List WriteArguments = new List(); public IEnumerable WriteToBody { get { yield return "WriteILRange(output, options);"; foreach (string line in WriteOpCodePrefix) yield return line; yield return "output.Write(OpCode);"; foreach (string line in WriteOpCodeSuffix.Concat(WriteOperand).Concat(WriteArguments)) yield return line; } } } class MatchParamInfo { public string TypeName; public string Name; public string FieldName; public bool IsReferenceType = true; public override string ToString() { if (IsReferenceType) { return "[NotNullWhen(true)] out " + TypeName + "? " + Name; } else { return "out " + TypeName + " " + Name; } } } static Action CustomClassName(string name) { return opCode => { opCode.Name = name; }; } static Action CustomVariableName(string name) { return opCode => { opCode.VariableName = name; }; } static Action CustomConstructor = opCode => { opCode.GenerateConstructor = false; opCode.GenerateMatch = false; }; static Action CustomWriteTo = opCode => { opCode.GenerateWriteTo = false; }; static Action CustomWriteToButKeepOriginal = opCode => { opCode.CustomWriteToButKeepOriginal = true; }; static Action CustomComputeFlags = opCode => { opCode.GenerateComputeFlags = false; }; // Call trait: the instruction performs a method call static Action MatchCondition(string name) { return opCode => { opCode.PerformMatchConditions.Add(name); }; } static Action HasFlag(string name) { return opCode => { opCode.Flags.Add(name); opCode.DirectFlags.Add(name); }; } static Action AdditionalMember(string declaration) { return opCode => { opCode.Members.Add(declaration); }; } // ResultType trait: the instruction has the specified result type. static Action ResultType(string type) { if (!type.Contains(".")) type = "StackType." + type; return opCode => { opCode.Members.Add("public override StackType ResultType { get { return " + type + "; } }"); }; } // VoidResult trait: the instruction has no result and is not usable as an argument static Action VoidResult = ResultType("Void"); // ResultTypeParam trait: the instruction takes its result type as ctor parameter static Action ResultTypeParam = opCode => { opCode.ConstructorParameters.Add("StackType resultType"); opCode.ConstructorBody.Add("this.resultType = resultType;"); opCode.Members.Add("StackType resultType;"); opCode.Members.Add("public override StackType ResultType { get { return resultType; } }"); }; // MayThrow trait: the instruction may throw exceptions static Action MayThrow = HasFlag("InstructionFlags.MayThrow"); // MayBranch trait: the instruction may cause control flow to branch (e.g. branch, conditionalbranch, return) static Action MayBranch = HasFlag("InstructionFlags.MayBranch"); // UnconditionalBranch trait: the instruction does not produce a result normally; it always branches or throws an exception. Implies VoidResult. // UnconditionalBranch should be paired with either MayBranch or MayThrow (or both). static Action UnconditionalBranch = VoidResult + HasFlag("InstructionFlags.EndPointUnreachable"); // ControlFlow trait: the instruction involves some form of internal control flow // Instructions without this trait must evaluate all arguments left-to-right before having any effect of their own. static Action ControlFlow = HasFlag("InstructionFlags.ControlFlow"); static Action MayThrowIfNotDelayed = HasFlag("(DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow)") + (opCode => { opCode.Members.Add("public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced"); opCode.GenerateWriteTo = true; opCode.WriteOpCodePrefix.Add("if (DelayExceptions)" + Environment.NewLine + "\toutput.Write(\"delayex.\");"); opCode.PerformMatchConditions.Add("DelayExceptions == o.DelayExceptions"); }); static Action BaseClass(string name) { return opCode => { opCode.BaseClass = name; opCode.Flags.Add("base.ComputeFlags()"); opCode.DirectFlags.Add("base.DirectFlags"); }; } // NoArguments trait: the instruction no arguments static Action NoArguments = opCode => { opCode.BaseClass = "SimpleInstruction"; }; // Unary trait: the instruction has a single argument static Action Unary = opCode => { BaseClass("UnaryInstruction")(opCode); opCode.ConstructorParameters.Add("ILInstruction argument"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "argument", FieldName = "Argument" }); opCode.PerformMatchConditions.Add("this.Argument.PerformMatch(o.Argument, ref match)"); opCode.BaseConstructorArguments.Add("argument"); opCode.WriteArguments.Add("output.Write('(');"); opCode.WriteArguments.Add("Argument.WriteTo(output, options);"); opCode.WriteArguments.Add("output.Write(')');"); }; // Binary trait: the instruction has two arguments named 'Left' and 'Right' static Action Binary = opCode => { BaseClass("BinaryInstruction")(opCode); opCode.ConstructorParameters.Add("ILInstruction left"); opCode.ConstructorParameters.Add("ILInstruction right"); opCode.BaseConstructorArguments.Add("left"); opCode.BaseConstructorArguments.Add("right"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "left", FieldName = "Left" }); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "right", FieldName = "Right" }); opCode.PerformMatchConditions.Add("this.Left.PerformMatch(o.Left, ref match)"); opCode.PerformMatchConditions.Add("this.Right.PerformMatch(o.Right, ref match)"); opCode.WriteArguments.Add("output.Write('(');"); opCode.WriteArguments.Add("Left.WriteTo(output, options);"); opCode.WriteArguments.Add("output.Write(\", \");"); opCode.WriteArguments.Add("Right.WriteTo(output, options);"); opCode.WriteArguments.Add("output.Write(')');"); }; static Action CustomArguments(params (string name, string[] expectedTypes)[] arguments) { return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg.name) { ExpectedTypes = arg.expectedTypes }).ToArray(), generateInline: true); } class ChildInfo { public readonly string PropertyName; public readonly string Name; public readonly string SlotName; public bool IsCollection; public string Type = "ILInstruction"; public bool CanInlineInto; public string[] ExpectedTypes; public ChildInfo(string name) { this.Name = name; this.PropertyName = MakeName(name); this.SlotName = this.PropertyName + "Slot"; } public string GetSlotInit() { StringBuilder b = new StringBuilder(); b.Append("new SlotInfo(\"" + PropertyName + "\""); if (CanInlineInto) b.Append(", canInlineInto: true"); b.Append(")"); return b.ToString(); } } class ArgumentInfo : ChildInfo { public ArgumentInfo(string name) : base(name) { this.CanInlineInto = true; } } static Action CustomChildren(ChildInfo[] children, bool generateInline = false) { return opCode => { opCode.GenerateWriteTo = true; opCode.WriteArguments.Add("output.Write('(');"); StringBuilder transformChildren = new StringBuilder(); ChildInfo collection = null; int childCount = children.Length; for (int i = 0; i < children.Length; i++) { string arg = children[i].Name; string argProp = children[i].PropertyName; if (children[i].IsCollection && i + 1 == children.Length) { collection = children[i]; childCount = children.Length - 1; opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)"); opCode.ConstructorParameters.Add("params ILInstruction[] " + arg); opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");"); opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");"); opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)"); if (i == 0) opCode.WriteArguments.Add("bool first = true;"); opCode.WriteArguments.Add("foreach (var " + arg + " in " + argProp + ") {"); if (i > 0) opCode.WriteArguments.Add("\toutput.Write(\", \");"); else opCode.WriteArguments.Add("\tif (!first) output.Write(\", \"); else first = false;"); opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);"); opCode.WriteArguments.Add("}"); opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }"); } else { opCode.Flags.Add(arg + ".Flags"); opCode.ConstructorParameters.Add("ILInstruction " + arg); opCode.ConstructorBody.Add("this." + argProp + " = " + arg + ";"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = arg, FieldName = argProp }); opCode.PerformMatchConditions.Add("this." + arg + ".PerformMatch(o." + arg + ", ref match)"); if (i > 0) opCode.WriteArguments.Add("output.Write(\", \");"); opCode.WriteArguments.Add("this." + arg + ".WriteTo(output, options);"); opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); opCode.Members.Add("ILInstruction " + arg + " = null!;"); opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine + "\tget { return this." + arg + "; }" + Environment.NewLine + "\tset {" + Environment.NewLine + "\t\tValidateChild(value);" + Environment.NewLine + "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine + "\t}" + Environment.NewLine + "}"); } if (children[i].ExpectedTypes?.Length > 0) { string checkString = null; foreach (var expectedType in children[i].ExpectedTypes) { var expectedTypeCode = expectedType; if (!expectedType.Contains(".")) expectedTypeCode = "StackType." + expectedTypeCode; if (checkString != null) checkString += " || "; checkString += arg + ".ResultType == " + expectedTypeCode; } opCode.Invariants.Add("DebugAssert(" + checkString + ");"); } } opCode.WriteArguments.Add("output.Write(')');"); StringBuilder b; b = new StringBuilder(); b.AppendLine("protected sealed override int GetChildCount()"); b.AppendLine("{"); b.Append("\treturn "); if (childCount > 0 || collection == null) b.Append(childCount); if (collection != null) { if (childCount > 0) b.Append(" + "); b.Append(collection.PropertyName + ".Count"); } b.AppendLine(";"); b.Append("}"); opCode.Members.Add(b.ToString()); b = new StringBuilder(); b.AppendLine("protected sealed override ILInstruction GetChild(int index)"); b.AppendLine("{"); b.AppendLine("\tswitch (index)"); b.AppendLine("\t{"); for (int i = 0; i < childCount; i++) { b.AppendLine("\t\tcase " + i + ":"); b.AppendLine("\t\t\treturn this." + children[i].Name + ";"); } b.AppendLine("\t\tdefault:"); if (collection == null) b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); else b.AppendLine("\t\t\treturn this." + collection.PropertyName + "[index - " + childCount + "];"); b.AppendLine("\t}"); b.Append("}"); opCode.Members.Add(b.ToString()); b = new StringBuilder(); b.AppendLine("protected sealed override void SetChild(int index, ILInstruction value)"); b.AppendLine("{"); b.AppendLine("\tswitch (index)"); b.AppendLine("\t{"); for (int i = 0; i < childCount; i++) { b.AppendLine("\t\tcase " + i + ":"); b.AppendLine("\t\t\tthis." + children[i].PropertyName + " = value;"); b.AppendLine("\t\t\tbreak;"); } b.AppendLine("\t\tdefault:"); if (collection == null) b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); else { b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;"); b.AppendLine("\t\t\tbreak;"); } b.AppendLine("\t}"); b.Append("}"); opCode.Members.Add(b.ToString()); b = new StringBuilder(); b.AppendLine("protected sealed override SlotInfo GetChildSlot(int index)"); b.AppendLine("{"); b.AppendLine("\tswitch (index)"); b.AppendLine("\t{"); for (int i = 0; i < childCount; i++) { b.AppendLine("\t\tcase " + i + ":"); b.AppendLine("\t\t\treturn " + children[i].SlotName + ";"); } b.AppendLine("\t\tdefault:"); if (collection == null) b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); else b.AppendLine("\t\t\treturn " + collection.SlotName + ";"); b.AppendLine("\t}"); b.Append("}"); opCode.Members.Add(b.ToString()); b = new StringBuilder(); b.AppendLine("public sealed override ILInstruction Clone()"); b.AppendLine("{"); b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); for (int i = 0; i < children.Length; i++) { if (children[i].IsCollection) { b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");"); b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));"); } else { b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); } } if (opCode.Name == "ILFunction") { b.AppendLine("\tclone.CloneVariables();"); } b.AppendLine("\treturn clone;"); b.Append("}"); opCode.Members.Add(b.ToString()); }; } static Action AbstractBaseClass = opCode => { opCode.GenerateAcceptVisitor = false; opCode.ClassModifiers = "public abstract"; opCode.ConstructorModifier = "protected"; opCode.ConstructorParameters.Add("OpCode opCode"); opCode.BaseConstructorArguments.Add("opCode"); opCode.GenerateMatch = false; opCode.GeneratePerformMatch = false; }; // SideEffect trait: the instruction has a non-local side effect static Action SideEffect = HasFlag("InstructionFlags.SideEffect"); static Action MemoryAccess = SideEffect; // Call trait: the instruction performs a method call static Action Call = opCode => { opCode.BaseClass = "CallInstruction"; opCode.ConstructorParameters.Add("IMethod method"); opCode.BaseConstructorArguments.Add("method"); opCode.GenerateMatch = false; opCode.GeneratePerformMatch = false; }; // HasVariableOperand trait: the instruction refers to a local variable static Action HasVariableOperand(string accessType, bool generateCheckInvariant = true) { Action action = opCode => { opCode.ConstructorParameters.Add("ILVariable variable"); opCode.Members.Add("ILVariable variable;"); opCode.ConstructorBody.Add("this.variable = variable ?? throw new ArgumentNullException(nameof(variable));"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" }); opCode.PerformMatchConditions.Add("variable == o.variable"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("variable.WriteTo(output);"); opCode.Interfaces.Add("I" + accessType + "Instruction"); opCode.Members.Add(@"public ILVariable Variable { get { return variable; } set { DebugAssert(value != null); if (IsConnected) variable.RemoveAccessInstruction(this); variable = value; if (IsConnected) variable.AddAccessInstruction(this); } } public int IndexInAccessInstructionList { get; set; } = -1; int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { get { return ((IAccessInstruction)this).IndexInAccessInstructionList; } set { ((IAccessInstruction)this).IndexInAccessInstructionList = value; } } protected override void Connected() { base.Connected(); variable.AddAccessInstruction(this); } protected override void Disconnected() { variable.RemoveAccessInstruction(this); base.Disconnected(); } ".Replace("Access", accessType)); if (generateCheckInvariant) { opCode.Invariants.Add("DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));"); opCode.Invariants.Add("DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);"); } }; if (accessType == "Load") { action += HasFlag("InstructionFlags.MayReadLocals"); } else if (accessType == "Store") { action += HasFlag("InstructionFlags.MayWriteLocals"); } else { if (accessType != "Address") throw new ArgumentException(); } return action; } static Action HasFieldOperand = opCode => { opCode.ConstructorParameters.Add("IField @field"); opCode.Members.Add("readonly IField @field;"); opCode.ConstructorBody.Add("this.@field = @field;"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IField", Name = "@field", FieldName = "Field" }); opCode.PerformMatchConditions.Add("@field.Equals(o.@field)"); opCode.Members.Add("/// Returns the field operand." + Environment.NewLine + "public IField Field { get { return @field; } }"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("@field.WriteTo(output);"); opCode.Interfaces.Add("IInstructionWithFieldOperand"); }; static Action HasTypeOperand = opCode => { opCode.ConstructorParameters.Add("IType type"); opCode.Members.Add("IType type;"); opCode.ConstructorBody.Add("this.type = type;"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IType", Name = "type", FieldName = "Type" }); opCode.PerformMatchConditions.Add("type.Equals(o.type)"); opCode.Members.Add("/// Returns the type operand." + Environment.NewLine + "public IType Type {" + Environment.NewLine + "\tget { return type; }" + Environment.NewLine + "\tset { type = value; InvalidateFlags(); }" + Environment.NewLine + "}"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("type.WriteTo(output);"); }; static Action HasMethodOperand(bool nullable = false) { return opCode => { string n = nullable ? "?" : ""; opCode.ConstructorParameters.Add($"IMethod{n} method"); opCode.Members.Add($"readonly IMethod{n} method;"); opCode.ConstructorBody.Add("this.method = method;"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = $"IMethod{n}", Name = "method", FieldName = "Method", IsReferenceType = !nullable }); opCode.PerformMatchConditions.Add("object.Equals(method, o.method)"); opCode.Members.Add("/// Returns the method operand." + Environment.NewLine + $"public IMethod{n} Method => method;"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("if (method != null)"); opCode.WriteOperand.Add("{"); opCode.WriteOperand.Add("\toutput.Write(' ');"); opCode.WriteOperand.Add("\tmethod.WriteTo(output);"); opCode.WriteOperand.Add("}"); opCode.Interfaces.Add("IInstructionWithMethodOperand"); }; } static Action HasMemberOperand = opCode => { opCode.ConstructorParameters.Add("IMember member"); opCode.Members.Add("readonly IMember member;"); opCode.ConstructorBody.Add("this.member = member;"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "IMember", Name = "member", FieldName = "Member" }); opCode.PerformMatchConditions.Add("member.Equals(o.member)"); opCode.Members.Add("/// Returns the token operand." + Environment.NewLine + "public IMember Member { get { return member; } }"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("member.WriteTo(output);"); }; // Adds a member of type bool to the instruction. static Action BoolFlag(string flagName) { return opCode => { opCode.PerformMatchConditions.Add($"this.{flagName} == o.{flagName}"); opCode.Members.Add($"public bool {flagName};"); opCode.GenerateWriteTo = true; opCode.WriteOpCodePrefix.Add($"if ({flagName}){Environment.NewLine}\toutput.Write(\"{flagName.ToLowerInvariant()}.\");"); }; } // LoadConstant trait: the instruction loads a compile-time constant. Implies NoArguments. static Action LoadConstant(string operandType) { return opCode => { NoArguments(opCode); opCode.ConstructorParameters.Add(operandType + " value"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = operandType, Name = "value", FieldName = "Value", IsReferenceType = operandType == "string" }); opCode.PerformMatchConditions.Add("this.Value == o.Value"); opCode.Members.Add("public readonly " + operandType + " Value;"); opCode.ConstructorBody.Add("this.Value = value;"); opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("Disassembler.DisassemblerHelpers.WriteOperand(output, Value);"); }; } static Action SupportsVolatilePrefix = opCode => { opCode.Interfaces.Add("ISupportsVolatilePrefix"); opCode.Members.Add("/// Gets/Sets whether the memory access is volatile." + Environment.NewLine + "public bool IsVolatile { get; set; }"); opCode.GenerateWriteTo = true; opCode.WriteOpCodePrefix.Add("if (IsVolatile)" + Environment.NewLine + "\toutput.Write(\"volatile.\");"); opCode.PerformMatchConditions.Add("IsVolatile == o.IsVolatile"); }; static Action SupportsUnalignedPrefix = opCode => { opCode.Interfaces.Add("ISupportsUnalignedPrefix"); opCode.Members.Add("/// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix." + Environment.NewLine + "public byte UnalignedPrefix { get; set; }"); opCode.GenerateWriteTo = true; opCode.WriteOpCodePrefix.Add("if (UnalignedPrefix > 0)" + Environment.NewLine + "\toutput.Write(\"unaligned(\" + UnalignedPrefix + \").\");"); opCode.PerformMatchConditions.Add("UnalignedPrefix == o.UnalignedPrefix"); }; static Action SupportsReadonlyPrefix = opCode => { opCode.Members.Add("/// Gets whether the 'readonly' prefix was applied to this instruction." + Environment.NewLine + "public bool IsReadOnly { get; set; }"); opCode.GenerateWriteTo = true; opCode.WriteOpCodePrefix.Add("if (IsReadOnly)" + Environment.NewLine + "\toutput.Write(\"readonly.\");"); opCode.PerformMatchConditions.Add("IsReadOnly == o.IsReadOnly"); }; static Action CustomInvariant(string code) { return opCode => { opCode.Invariants.Add(code); }; } static Action Pattern = opCode => { BaseClass("PatternInstruction")(opCode); opCode.Namespace = "ICSharpCode.Decompiler.IL.Patterns"; opCode.GenerateAcceptVisitor = false; opCode.GenerateMatch = false; opCode.GeneratePerformMatch = false; }; static Action Dynamic = BaseClass("DynamicInstruction") + MayThrow + SideEffect + CustomConstructor; #> ================================================ FILE: ICSharpCode.Decompiler/IL/Patterns/AnyNode.cs ================================================ #nullable enable using System; namespace ICSharpCode.Decompiler.IL.Patterns { partial class PatternInstruction : ILInstruction { public override void AcceptVisitor(ILVisitor visitor) { throw new NotSupportedException(); } public override T AcceptVisitor(ILVisitor visitor, C context) { throw new NotSupportedException(); } public override T AcceptVisitor(ILVisitor visitor) { throw new NotSupportedException(); } protected override InstructionFlags ComputeFlags() { throw new NotSupportedException(); } public override InstructionFlags DirectFlags { get { throw new NotSupportedException(); } } } partial class AnyNode : PatternInstruction { CaptureGroup? group; public AnyNode(CaptureGroup? group = null) : base(OpCode.AnyNode) { this.group = group; } protected internal override bool PerformMatch(ILInstruction? other, ref Match match) { if (other == null) return false; if (group != null) match.Add(group, other); return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Patterns/ListMatch.cs ================================================ #nullable enable // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.IL.Patterns { /// /// Data holder for a single list matching operation. /// /// /// Notes on backtracking: /// PerformMatch() may save backtracking-savepoints to the ListMatch instance. /// Each backtracking-savepoints is a Stack{int} with instructions of how to restore the saved state. /// When a savepoint is created by a PerformMatch() call, that call may be nested in several other PerformMatch() calls that operate on /// the same list. /// When leaving those calls (whether with a successful match or not), the outer PerformMatch() calls may push additional state onto /// all of the added backtracking-savepoints. /// When the overall list match fails but savepoints exists, the most recently added savepoint is restored by calling PerformMatch() /// with listMatch.restoreStack set to that savepoint. Each PerformMatch() call must pop its state from that stack before /// recursively calling its child patterns. /// public struct ListMatch { /// /// The main list matching logic. /// /// Returns whether the list match was successful. /// If the method returns true, it adds the capture groups (if any) to the match. /// If the method returns false, the match object remains in a partially-updated state and needs to be restored /// before it can be reused. internal static bool DoMatch(IReadOnlyList patterns, IReadOnlyList syntaxList, ref Match match) { ListMatch listMatch = new ListMatch(syntaxList); do { if (PerformMatchSequence(patterns, ref listMatch, ref match)) { // If we have a successful match and it matches the whole list, // we are done. if (listMatch.SyntaxIndex == syntaxList.Count) return true; } // Otherwise, restore a savepoint created by PerformMatch() and resume the matching logic at that savepoint. } while (listMatch.RestoreSavePoint(ref match)); return false; } /// /// PerformMatch() for a sequence of patterns. /// /// List of patterns to match. /// Stores state about the current list match. /// The match object, used to store global state during the match (such as the results of capture groups). /// Returns whether all patterns were matched successfully against a part of the list. /// If the method returns true, it updates listMatch.SyntaxIndex to point to the next node that was not part of the match, /// and adds the capture groups (if any) to the match. /// If the method returns false, the listMatch and match objects remain in a partially-updated state and need to be restored /// before they can be reused. internal static bool PerformMatchSequence(IReadOnlyList patterns, ref ListMatch listMatch, ref Match match) { // The patterns may create savepoints, so we need to save the 'i' variable // as part of those checkpoints. for (int i = listMatch.PopFromSavePoint() ?? 0; i < patterns.Count; i++) { int startMarker = listMatch.GetSavePointStartMarker(); bool success = patterns[i].PerformMatch(ref listMatch, ref match); listMatch.PushToSavePoints(startMarker, i); if (!success) return false; } return true; } /// /// A savepoint that the list matching operation can be restored from. /// struct SavePoint { internal readonly int CheckPoint; internal readonly int SyntaxIndex; internal readonly Stack stack; public SavePoint(int checkpoint, int syntaxIndex) { this.CheckPoint = checkpoint; this.SyntaxIndex = syntaxIndex; this.stack = new Stack(); } } /// /// The syntax list we are matching against. /// internal readonly IReadOnlyList SyntaxList; /// /// The current index in the syntax list. /// internal int SyntaxIndex; ListMatch(IReadOnlyList syntaxList) { this.SyntaxList = syntaxList; this.SyntaxIndex = 0; this.backtrackingStack = null; this.restoreStack = null; } List? backtrackingStack; Stack? restoreStack; void AddSavePoint(SavePoint savepoint) { if (backtrackingStack == null) backtrackingStack = new List(); backtrackingStack.Add(savepoint); } internal void AddSavePoint(ref Match match, int data) { var savepoint = new SavePoint(match.CheckPoint(), this.SyntaxIndex); savepoint.stack.Push(data); AddSavePoint(savepoint); } internal int GetSavePointStartMarker() { return backtrackingStack != null ? backtrackingStack.Count : 0; } internal void PushToSavePoints(int startMarker, int data) { if (backtrackingStack == null) return; for (int i = startMarker; i < backtrackingStack.Count; i++) { backtrackingStack[i].stack.Push(data); } } internal int? PopFromSavePoint() { if (restoreStack == null || restoreStack.Count == 0) return null; return restoreStack.Pop(); } /// /// Restores the listmatch state from a savepoint. /// /// Returns whether a savepoint exists internal bool RestoreSavePoint(ref Match match) { if (backtrackingStack == null || backtrackingStack.Count == 0) return false; var savepoint = backtrackingStack[backtrackingStack.Count - 1]; backtrackingStack.RemoveAt(backtrackingStack.Count - 1); match.RestoreCheckPoint(savepoint.CheckPoint); this.SyntaxIndex = savepoint.SyntaxIndex; restoreStack = savepoint.stack; return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Patterns/Match.cs ================================================ #nullable enable // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; namespace ICSharpCode.Decompiler.IL.Patterns { public class CaptureGroup { } /// /// Data holder for the overall pattern matching operation. /// /// /// This type is a struct in order to prevent unnecessary memory allocations during pattern matching. /// The default value default(Match) represents an unsuccessful match. /// public struct Match { static readonly List> emptyResults = new List>(); List>? results; /// /// Gets whether the match was successful. /// public bool Success { get { return results != null; } internal set { if (value) { if (results == null) results = emptyResults; } else { results = null; } } } /// /// Gets whether the match was successful. /// public static bool operator true(Match m) { return m.Success; } /// /// Gets whether the match failed. /// public static bool operator false(Match m) { return !m.Success; } internal void Add(CaptureGroup g, ILInstruction n) { if (results == null) results = new List>(); results.Add(new KeyValuePair(g, n)); } internal int CheckPoint() { return results != null ? results.Count : 0; } internal void RestoreCheckPoint(int checkPoint) { if (results != null) results.RemoveRange(checkPoint, results.Count - checkPoint); } public IEnumerable Get(CaptureGroup captureGroup) { if (results != null) { foreach (var pair in results) { if (pair.Key == captureGroup) yield return pair.Value; } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { /// /// Analyses the RHS of a 'ptr + int' or 'ptr - int' operation. /// struct PointerArithmeticOffset { /// /// Given an instruction that computes a pointer arithmetic offset in bytes, /// returns an instruction that computes the same offset in number of elements. /// /// Returns null if no such instruction can be found. /// /// Input instruction. /// The target type of the pointer type. /// Whether the pointer arithmetic operation checks for overflow. /// Whether to allow zero extensions in the mul argument. public static ILInstruction Detect(ILInstruction byteOffsetInst, IType pointerElementType, bool checkForOverflow, bool unwrapZeroExtension = false) { if (pointerElementType == null) return null; if (byteOffsetInst is Conv conv && conv.InputType == StackType.I8 && conv.ResultType == StackType.I) { byteOffsetInst = conv.Argument; } int? elementSize = ComputeSizeOf(pointerElementType); if (elementSize == 1) { return byteOffsetInst; } else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) { if (mul.IsLifted) return null; if (mul.CheckForOverflow != checkForOverflow) return null; if (elementSize > 0 && mul.Right.MatchLdcI(elementSize.Value) || mul.Right.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(sizeOf.Type, pointerElementType)) { var countOffsetInst = mul.Left; if (unwrapZeroExtension) { countOffsetInst = countOffsetInst.UnwrapConv(ConversionKind.ZeroExtend); } return countOffsetInst; } } else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerElementType)) { return new LdcI4(1).WithILRange(byteOffsetInst); } else if (byteOffsetInst.MatchLdcI(out long val)) { // If the offset is a constant, it's possible that the compiler // constant-folded the multiplication. if (elementSize > 0 && (val % elementSize == 0) && val > 0) { val /= elementSize.Value; if (val <= int.MaxValue) { return new LdcI4((int)val).WithILRange(byteOffsetInst); } } } return null; } public static int? ComputeSizeOf(IType type) { switch (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.SByte: case KnownTypeCode.Byte: return 1; case KnownTypeCode.Char: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: return 2; case KnownTypeCode.Int32: case KnownTypeCode.UInt32: case KnownTypeCode.Single: return 4; case KnownTypeCode.Int64: case KnownTypeCode.UInt64: case KnownTypeCode.Double: return 8; case KnownTypeCode.Decimal: return 16; } return null; } /// /// Returns true if inst computes the address of a fixed variable; false if it computes the address of a moveable variable. /// (see "Fixed and moveable variables" in the C# specification) /// internal static bool IsFixedVariable(ILInstruction inst) { switch (inst) { case LdLoca ldloca: return ldloca.Variable.CaptureScope == null; // locals are fixed if uncaptured case LdFlda ldflda: return IsFixedVariable(ldflda.Target); default: return inst.ResultType == StackType.I; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/PrimitiveType.cs ================================================ #nullable enable // Copyright (c) 2014 Daniel Grunwald // // 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.Reflection.Metadata; namespace ICSharpCode.Decompiler.IL { public enum PrimitiveType : byte { None, I1 = PrimitiveTypeCode.SByte, I2 = PrimitiveTypeCode.Int16, I4 = PrimitiveTypeCode.Int32, I8 = PrimitiveTypeCode.Int64, R4 = PrimitiveTypeCode.Single, R8 = PrimitiveTypeCode.Double, U1 = PrimitiveTypeCode.Byte, U2 = PrimitiveTypeCode.UInt16, U4 = PrimitiveTypeCode.UInt32, U8 = PrimitiveTypeCode.UInt64, I = PrimitiveTypeCode.IntPtr, U = PrimitiveTypeCode.UIntPtr, /// Managed reference Ref = 16, /// Floating point type of unspecified size: /// usually 80 bits on x86 (when the runtime uses x87 instructions); /// but only 64-bit on x64. /// This only occurs for "conv.r.un" instructions. The C# compiler usually follows those /// with a "conv.r4" or "conv.r8" instruction to indicate the desired float type, so /// we only use this as conversion target type and don't bother tracking it as its own stack type: /// basically everything treats R identical to R8, except for the (conv.r.un + conv.r[48] => conv.r[48].un) /// combining logic which should not combine (conv.r.un + conv.r8 + conv.r4) into a single conv.r4.un. /// R = 254, Unknown = 255 } } ================================================ FILE: ICSharpCode.Decompiler/IL/SemanticHelper.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { static class SemanticHelper { internal static InstructionFlags CombineBranches(InstructionFlags trueFlags, InstructionFlags falseFlags) { // the endpoint of the 'if' is only unreachable if both branches have an unreachable endpoint const InstructionFlags combineWithAnd = InstructionFlags.EndPointUnreachable; return (trueFlags & falseFlags) | ((trueFlags | falseFlags) & ~combineWithAnd); } /// /// Gets whether instruction is pure: /// * must not have side effects /// * must not throw exceptions /// * must not branch /// internal static bool IsPure(InstructionFlags inst) { // ControlFlow is fine: internal control flow is pure as long as it's not an infinite loop, // and infinite loops are impossible without MayBranch. const InstructionFlags pureFlags = InstructionFlags.MayReadLocals | InstructionFlags.ControlFlow; return (inst & ~pureFlags) == 0; } /// /// Gets whether the instruction sequence 'inst1; inst2;' may be ordered to 'inst2; inst1;' /// internal static bool MayReorder(ILInstruction inst1, ILInstruction inst2) { // If both instructions perform an impure action, we cannot reorder them if (!IsPure(inst1.Flags) && !IsPure(inst2.Flags)) return false; // We cannot reorder if inst2 might write what inst1 looks at if (Inst2MightWriteToVariableReadByInst1(inst1, inst2)) return false; // and the same in reverse: if (Inst2MightWriteToVariableReadByInst1(inst2, inst1)) return false; return true; } static bool Inst2MightWriteToVariableReadByInst1(ILInstruction inst1, ILInstruction inst2) { if (!inst1.HasFlag(InstructionFlags.MayReadLocals)) { // quick exit if inst1 doesn't read any variables return false; } var variables = inst1.Descendants.OfType().Select(load => load.Variable).ToHashSet(); if (inst2.HasFlag(InstructionFlags.SideEffect) && variables.Any(v => v.AddressCount > 0)) { // If inst2 might have indirect writes, we cannot reorder with any loads of variables that have their address taken. return true; } foreach (var inst in inst2.Descendants) { if (inst.HasDirectFlag(InstructionFlags.MayWriteLocals)) { ILVariable v = ((IInstructionWithVariableOperand)inst).Variable; if (variables.Contains(v)) { return true; } } } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/SlotInfo.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable namespace ICSharpCode.Decompiler.IL { /// /// Holds information about the role of an instruction within its parent instruction. /// public class SlotInfo { public static SlotInfo None = new SlotInfo(""); /// /// Gets the name of the slot. /// public readonly string Name; /// /// Gets whether it is possible to inline into this slot. /// public readonly bool CanInlineInto; /// /// Gets whether this slot belongs to a collection. /// public readonly bool IsCollection; public SlotInfo(string name, bool canInlineInto = false, bool isCollection = false) { this.IsCollection = isCollection; this.Name = name; this.CanInlineInto = canInlineInto; } public override string ToString() { return Name; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/StackType.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable namespace ICSharpCode.Decompiler.IL { /// /// A type for the purpose of stack analysis. /// public enum StackType : byte { // Note: the numeric of these enum members is relevant for ILReader.MergeStacks: // when two branches meet where a stack slot has different types, the type after // the branch is the one with the higher numeric value. /// /// The stack type is unknown; for example a call returning an unknown type /// because an assembly reference isn't loaded. /// Can also occur with invalid IL. /// Unknown, /// 32-bit integer /// /// Used for C# int, uint, /// C# small integer types byte, sbyte, short, ushort, /// bool and char, /// and any enums with one of the above as underlying type. /// I4, /// native-size integer, or unmanaged pointer /// /// Used for C# IntPtr, UIntPtr and any native pointer types (void* etc.) /// Also used for IL function pointer types. /// I, /// 64-bit integer /// /// Used for C# long, ulong, /// and any enums with one of the above as underlying type. /// I8, /// 32-bit floating point number /// /// Used for C# float. /// F4, /// 64-bit floating point number /// /// Used for C# double. /// F8, /// Another stack type. Includes objects, value types, ... O, /// A managed pointer Ref, /// Represents the lack of a stack slot Void } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs ================================================ // Copyright (c) 2017 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using Humanizer.Inflections; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { public class AssignVariableNames : ILVisitor, IILTransform { static readonly Dictionary typeNameToVariableNameDict = new Dictionary { { "System.Boolean", "flag" }, { "System.Byte", "b" }, { "System.SByte", "b" }, { "System.Int16", "num" }, { "System.Int32", "num" }, { "System.Int64", "num" }, { "System.UInt16", "num" }, { "System.UInt32", "num" }, { "System.UInt64", "num" }, { "System.Single", "num" }, { "System.Double", "num" }, { "System.Decimal", "num" }, { "System.String", "text" }, { "System.Object", "obj" }, { "System.Char", "c" } }; ILTransformContext context; Queue<(ILFunction function, VariableScope parentScope)> workList; const char maxLoopVariableName = 'n'; public class VariableScope { readonly ILTransformContext context; readonly VariableScope parentScope; readonly ILFunction function; readonly Dictionary localFunctions = new(); readonly Dictionary variableMapping = new(ILVariableEqualityComparer.Instance); readonly string[] assignedLocalSignatureIndices; IImmutableSet currentLowerCaseTypeOrMemberNames; Dictionary reservedVariableNames; HashSet loopCounters; int numDisplayClassLocals; public VariableScope(ILFunction function, ILTransformContext context, VariableScope parentScope = null) { this.function = function; this.context = context; this.parentScope = parentScope; numDisplayClassLocals = 0; assignedLocalSignatureIndices = new string[function.LocalVariableSignatureLength]; reservedVariableNames = new Dictionary(); // find all loop counters in the current function loopCounters = new HashSet(); foreach (var inst in TreeTraversal.PreOrder((ILInstruction)function, i => i.Children)) { if (inst is ILFunction && inst != function) break; if (inst is BlockContainer { Kind: ContainerKind.For, Blocks: [.., var incrementBlock] }) { foreach (var i in incrementBlock.Instructions) { if (HighLevelLoopTransform.MatchIncrement(i, out var variable)) loopCounters.Add(variable); } } } // if this is the root scope, we also collect all lower-case type and member names // and fixed parameter names to avoid conflicts when naming local variables. if (parentScope == null) { var currentLowerCaseTypeOrMemberNames = new HashSet(StringComparer.Ordinal); foreach (var name in CollectAllLowerCaseMemberNames(function.Method.DeclaringTypeDefinition)) currentLowerCaseTypeOrMemberNames.Add(name); foreach (IField item in function.Method.DeclaringTypeDefinition.Fields) { if (TransformFieldAndConstructorInitializers.IsGeneratedPrimaryConstructorBackingField(item)) { string name = item.Name.Substring(1, item.Name.Length - 3); currentLowerCaseTypeOrMemberNames.Add(name); AddExistingName(reservedVariableNames, name); } } foreach (var name in CollectAllLowerCaseTypeNames(function.Method.DeclaringTypeDefinition)) { currentLowerCaseTypeOrMemberNames.Add(name); AddExistingName(reservedVariableNames, name); } foreach (var name in CollectAllLowerCaseTypeNames(context.UsingScope)) { currentLowerCaseTypeOrMemberNames.Add(name); AddExistingName(reservedVariableNames, name); } this.currentLowerCaseTypeOrMemberNames = currentLowerCaseTypeOrMemberNames.ToImmutableHashSet(); // handle implicit parameters of set or event accessors if (function.Method != null && IsSetOrEventAccessor(function.Method) && function.Parameters.Count > 0) { for (int i = 0; i < function.Method.Parameters.Count - 1; i++) { AddExistingName(reservedVariableNames, function.Method.Parameters[i].Name); } var lastParameter = function.Method.Parameters.Last(); switch (function.Method.AccessorOwner) { case IProperty prop: if (function.Method.AccessorKind == MethodSemanticsAttributes.Setter) { if (prop.Parameters.Any(p => p.Name == "value")) { function.Warnings.Add("Parameter named \"value\" already present in property signature!"); break; } var variableForLastParameter = function.Variables.FirstOrDefault(v => v.Function == function && v.Kind == VariableKind.Parameter && v.Index == function.Method.Parameters.Count - 1); if (variableForLastParameter == null) { AddExistingName(reservedVariableNames, lastParameter.Name); } else { if (variableForLastParameter.Name != "value") { variableForLastParameter.Name = "value"; } AddExistingName(reservedVariableNames, variableForLastParameter.Name); } } break; case IEvent ev: if (function.Method.AccessorKind != MethodSemanticsAttributes.Raiser) { var variableForLastParameter = function.Variables.FirstOrDefault(v => v.Function == function && v.Kind == VariableKind.Parameter && v.Index == function.Method.Parameters.Count - 1); if (variableForLastParameter == null) { AddExistingName(reservedVariableNames, lastParameter.Name); } else { if (variableForLastParameter.Name != "value") { variableForLastParameter.Name = "value"; } AddExistingName(reservedVariableNames, variableForLastParameter.Name); } } break; default: AddExistingName(reservedVariableNames, lastParameter.Name); break; } } else { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter && v.Index >= 0).ToDictionary(v => v.Index); foreach (var (i, p) in function.Parameters.WithIndex()) { string name = p.Name; if (string.IsNullOrWhiteSpace(name) && p.Type != SpecialType.ArgList) { // needs to be consistent with logic in ILReader.CreateILVariable name = "P_" + i; } if (variables.TryGetValue(i, out var v)) variableMapping[v] = name; AddExistingName(reservedVariableNames, name); } } static bool IsSetOrEventAccessor(IMethod method) { switch (method.AccessorKind) { case MethodSemanticsAttributes.Setter: case MethodSemanticsAttributes.Adder: case MethodSemanticsAttributes.Remover: return true; default: return false; } } } else { this.currentLowerCaseTypeOrMemberNames = parentScope.currentLowerCaseTypeOrMemberNames; var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); foreach (var (i, p) in function.Parameters.WithIndex()) { string name = string.IsNullOrWhiteSpace(p.Name) ? variables[i].Name : p.Name; if (function.Kind is ILFunctionKind.Delegate or ILFunctionKind.ExpressionTree && CSharpDecompiler.IsTransparentIdentifier(name)) { AddExistingName(reservedVariableNames, name); if (variables.TryGetValue(i, out var v)) variableMapping[v] = name; } string nameWithoutNumber = SplitName(name, out int newIndex); if (!parentScope.IsReservedVariableName(nameWithoutNumber, out _)) { AddExistingName(reservedVariableNames, name); if (variables.TryGetValue(i, out var v)) variableMapping[v] = name; } else if (variables.TryGetValue(i, out var v)) { v.HasGeneratedName = true; } } } } public void Add(MethodDefinitionHandle localFunction, string name) { this.localFunctions[localFunction] = name; } public string TryGetExistingName(MethodDefinitionHandle localFunction) { if (localFunctions.TryGetValue(localFunction, out var name)) return name; return parentScope?.TryGetExistingName(localFunction); } public string TryGetExistingName(ILVariable v) { if (variableMapping.TryGetValue(v, out var name)) return name; return parentScope?.TryGetExistingName(v); } public string TryGetExistingName(ILFunction function, int index) { if (this.function == function) { return this.assignedLocalSignatureIndices[index]; } else { return parentScope?.TryGetExistingName(function, index); } } public void AssignNameToLocalSignatureIndex(ILFunction function, int index, string name) { var scope = this; while (scope != null && scope.function != function) scope = scope.parentScope; Debug.Assert(scope != null); scope.assignedLocalSignatureIndices[index] = name; } public bool IsReservedVariableName(string name, out int index) { if (reservedVariableNames.TryGetValue(name, out index)) return true; return parentScope?.IsReservedVariableName(name, out index) ?? false; } public void ReserveVariableName(string name, int index = 1) { reservedVariableNames[name] = index; } public string NextDisplayClassLocal() { return parentScope?.NextDisplayClassLocal() ?? "CS$<>8__locals" + (numDisplayClassLocals++); } public bool IsLoopCounter(ILVariable v) { return loopCounters.Contains(v) || (parentScope?.IsLoopCounter(v) == true); } public string AssignNameIfUnassigned(ILVariable v) { if (variableMapping.TryGetValue(v, out var name)) return name; return AssignName(v); } public string AssignName(ILVariable v) { // variable has no valid name string newName = v.Name; if (v.HasGeneratedName || !IsValidName(newName)) { // don't use the name from the debug symbols if it looks like a generated name // generate a new one based on how the variable is used newName = GenerateNameForVariable(v); } // use the existing name and update index appended to future conflicts string nameWithoutNumber = SplitName(newName, out int newIndex); if (IsReservedVariableName(nameWithoutNumber, out int lastUsedIndex)) { if (v.Type.IsKnownType(KnownTypeCode.Int32) && IsLoopCounter(v)) { // special case for loop counters, // we don't want them to be named i, i2, ..., but i, j, ... newName = GenerateNameForVariable(v); nameWithoutNumber = newName; newIndex = 1; } } if (IsReservedVariableName(nameWithoutNumber, out lastUsedIndex)) { // name without number was already used if (newIndex > lastUsedIndex) { // new index is larger than last, so we can use it } else { // new index is smaller or equal, so we use the next value newIndex = lastUsedIndex + 1; } // resolve conflicts by appending the index to the new name: newName = nameWithoutNumber + newIndex.ToString(); } // update the last used index ReserveVariableName(nameWithoutNumber, newIndex); variableMapping[v] = newName; return newName; } string GenerateNameForVariable(ILVariable variable) { string proposedName = null; if (variable.Type.IsKnownType(KnownTypeCode.Int32)) { // test whether the variable might be a loop counter if (loopCounters.Contains(variable)) { // For loop variables, use i,j,k,l,m,n for (char c = 'i'; c <= maxLoopVariableName; c++) { if (!IsReservedVariableName(c.ToString(), out _)) { proposedName = c.ToString(); break; } } } } // The ComponentResourceManager inside InitializeComponent must be named "resources", // otherwise the WinForms designer won't load the Form. if (CSharp.CSharpDecompiler.IsWindowsFormsInitializeComponentMethod(context.Function.Method) && variable.Type.FullName == "System.ComponentModel.ComponentResourceManager") { proposedName = "resources"; } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForAddress = variable.AddressInstructions.OfType() .Select(arg => arg.Parent is CallInstruction c ? c.GetParameter(arg.ChildIndex)?.Name : null) .Where(arg => !string.IsNullOrWhiteSpace(arg)) .Except(currentLowerCaseTypeOrMemberNames).ToList(); if (proposedNameForAddress.Count > 0) { proposedName = proposedNameForAddress[0]; } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForStores = new HashSet(); foreach (var store in variable.StoreInstructions) { if (store is StLoc stloc) { var name = GetNameFromInstruction(stloc.Value); if (!currentLowerCaseTypeOrMemberNames.Contains(name)) proposedNameForStores.Add(name); } else if (store is MatchInstruction match && match.SlotInfo == MatchInstruction.SubPatternsSlot) { var name = GetNameFromInstruction(match.TestedOperand); if (!currentLowerCaseTypeOrMemberNames.Contains(name)) proposedNameForStores.Add(name); } else if (store is PinnedRegion pinnedRegion) { var name = GetNameFromInstruction(pinnedRegion.Init); if (!currentLowerCaseTypeOrMemberNames.Contains(name)) proposedNameForStores.Add(name); } } if (proposedNameForStores.Count == 1) { proposedName = proposedNameForStores.Single(); } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForLoads = variable.LoadInstructions .Select(arg => GetNameForArgument(arg.Parent, arg.ChildIndex)) .Except(currentLowerCaseTypeOrMemberNames).ToList(); if (proposedNameForLoads.Count == 1) { proposedName = proposedNameForLoads[0]; } } if (string.IsNullOrEmpty(proposedName) && variable.Kind == VariableKind.StackSlot) { var proposedNameForStoresFromNewObj = variable.StoreInstructions.OfType() .Select(expr => GetNameByType(GuessType(variable.Type, expr.Value, context))) .Except(currentLowerCaseTypeOrMemberNames).ToList(); if (proposedNameForStoresFromNewObj.Count == 1) { proposedName = proposedNameForStoresFromNewObj[0]; } } if (string.IsNullOrEmpty(proposedName)) { proposedName = GetNameByType(variable.Type); } // for generated names remove number-suffixes return SplitName(proposedName, out _); } } public void Run(ILFunction function, ILTransformContext context) { this.context = context; this.workList ??= new Queue<(ILFunction function, VariableScope parentScope)>(); this.workList.Clear(); this.workList.Enqueue((function, null)); while (this.workList.Count > 0) { var (currentFunction, parentContext) = this.workList.Dequeue(); var nestedContext = new VariableScope(currentFunction, this.context, parentContext); foreach (var localFunction in currentFunction.LocalFunctions) { AssignNameToLocalFunction(localFunction, nestedContext); workList.Enqueue((localFunction, nestedContext)); } currentFunction.Body.AcceptVisitor(this, nestedContext); if (currentFunction.Kind != ILFunctionKind.TopLevelFunction) { foreach (var p in currentFunction.Variables.Where(v => v.Kind == VariableKind.Parameter && v.Index >= 0)) { p.Name = nestedContext.AssignNameIfUnassigned(p); } } } } private static void AssignNameToLocalFunction(ILFunction function, VariableScope parentContext) { if (!LocalFunctionDecompiler.ParseLocalFunctionName(function.Name, out _, out var newName) || !IsValidName(newName)) newName = null; string nameWithoutNumber; int number; if (!string.IsNullOrEmpty(newName)) { nameWithoutNumber = SplitName(newName, out number); } else { nameWithoutNumber = "f"; number = 1; } int count; if (!parentContext.IsReservedVariableName(nameWithoutNumber, out int currentIndex)) { count = 1; } else { if (currentIndex < number) count = number; else count = Math.Max(number, currentIndex) + 1; } parentContext.ReserveVariableName(nameWithoutNumber, count); if (count > 1) { newName = nameWithoutNumber + count.ToString(); } else { newName = nameWithoutNumber; } function.Name = newName; function.ReducedMethod.Name = newName; parentContext.Add((MethodDefinitionHandle)function.ReducedMethod.MetadataToken, newName); } Unit VisitChildren(ILInstruction inst, VariableScope context) { foreach (var child in inst.Children) { child.AcceptVisitor(this, context); } return default; } protected override Unit Default(ILInstruction inst, VariableScope context) { if (inst is IInstructionWithVariableOperand { Variable: var v }) { // if there is already a valid name for the variable slot, just use it string name = context.TryGetExistingName(v); if (!string.IsNullOrEmpty(name)) { v.Name = name; return VisitChildren(inst, context); } switch (v.Kind) { case VariableKind.Parameter when !v.HasGeneratedName && v.Function.Kind == ILFunctionKind.TopLevelFunction: // Parameter names of top-level functions are handled in ILReader.CreateILVariable // and CSharpDecompiler.FixParameterNames break; case VariableKind.InitializerTarget: // keep generated names case VariableKind.NamedArgument: context.ReserveVariableName(v.Name); break; case VariableKind.UsingLocal when v.AddressCount == 0 && v.LoadCount == 0: // using variables that are not read, will not be declared in source source break; case VariableKind.DisplayClassLocal: v.Name = context.NextDisplayClassLocal(); break; case VariableKind.Local when v.Index != null: name = context.TryGetExistingName(v.Function, v.Index.Value); if (name != null) { // make sure all local ILVariables that refer to the same slot in the locals signature // are assigned the same name. v.Name = name; } else { v.Name = context.AssignName(v); context.AssignNameToLocalSignatureIndex(v.Function, v.Index.Value, v.Name); } break; default: v.Name = context.AssignName(v); break; } } return VisitChildren(inst, context); } protected internal override Unit VisitILFunction(ILFunction function, VariableScope context) { workList.Enqueue((function, context)); return default; } protected internal override Unit VisitCall(Call inst, VariableScope context) { if (inst.Method is LocalFunctionMethod m) { string name = context.TryGetExistingName((MethodDefinitionHandle)m.MetadataToken); if (!string.IsNullOrEmpty(name)) m.Name = name; } return base.VisitCall(inst, context); } protected internal override Unit VisitLdFtn(LdFtn inst, VariableScope context) { if (inst.Method is LocalFunctionMethod m) { string name = context.TryGetExistingName((MethodDefinitionHandle)m.MetadataToken); if (!string.IsNullOrEmpty(name)) m.Name = name; } return base.VisitLdFtn(inst, context); } static IEnumerable CollectAllLowerCaseMemberNames(ITypeDefinition type) { foreach (var item in type.GetMembers(m => IsLowerCase(m.Name))) yield return item.Name; } static IEnumerable CollectAllLowerCaseTypeNames(ITypeDefinition type) { var ns = type.ParentModule.Compilation.GetNamespaceByFullName(type.Namespace); foreach (var item in ns.Types) { if (IsLowerCase(item.Name)) yield return item.Name; } var current = type; while (current != null) { foreach (var nested in current.NestedTypes) { if (IsLowerCase(nested.Name)) yield return nested.Name; } current = current.DeclaringTypeDefinition; } } static IEnumerable CollectAllLowerCaseTypeNames(UsingScope usingScope) { return usingScope?.Usings.SelectMany(n => n.Types).Select(t => t.Name).Where(IsLowerCase) ?? []; } static bool IsLowerCase(string name) { return name.Length > 0 && char.ToLower(name[0]) == name[0]; } /// /// Must be in sync with . /// internal static bool IsSupportedInstruction(object arg) { switch (arg) { case GetPinnableReference _: case LdObj _: case LdFlda _: case LdsFlda _: case CallInstruction _: return true; default: return false; } } internal static bool IsValidName(string varName) { if (string.IsNullOrWhiteSpace(varName)) return false; if (!(char.IsLetter(varName[0]) || varName[0] == '_')) return false; for (int i = 1; i < varName.Length; i++) { if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_')) return false; } return true; } static string GetNameFromInstruction(ILInstruction inst) { switch (inst) { case GetPinnableReference getPinnableReference: return GetNameFromInstruction(getPinnableReference.Argument); case LdObj ldobj: return GetNameFromInstruction(ldobj.Target); case LdFlda ldflda: if (ldflda.Field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return GetNameFromInstruction(ldflda.Target); return CleanUpVariableName(ldflda.Field.Name); case LdsFlda ldsflda: return CleanUpVariableName(ldsflda.Field.Name); case CallInstruction call: if (call is NewObj) break; IMethod m = call.Method; if (ExcludeMethodFromCandidates(m)) break; if (m.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && m.Parameters.Count == 0) { // use name from properties, but not from indexers return CleanUpVariableName(m.Name.Substring(4)); } else if (m.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && m.Name.Length >= 4 && char.IsUpper(m.Name[3])) { // use name from Get-methods return CleanUpVariableName(m.Name.Substring(3)); } break; case DynamicInvokeMemberInstruction dynInvokeMember: if (dynInvokeMember.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && dynInvokeMember.Name.Length >= 4 && char.IsUpper(dynInvokeMember.Name[3])) { // use name from Get-methods return CleanUpVariableName(dynInvokeMember.Name.Substring(3)); } break; } return null; } static string GetNameForArgument(ILInstruction parent, int i) { switch (parent) { case StObj stobj: IField field; if (stobj.Target is LdFlda ldflda) field = ldflda.Field; else if (stobj.Target is LdsFlda ldsflda) field = ldsflda.Field; else break; return CleanUpVariableName(field.Name); case CallInstruction call: IMethod m = call.Method; if (ExcludeMethodFromCandidates(m)) return null; if (m.Parameters.Count == 1 && i == call.Arguments.Count - 1) { // argument might be value of a setter if (m.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) { return CleanUpVariableName(m.Name.Substring(4)); } else if (m.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && m.Name.Length >= 4 && char.IsUpper(m.Name[3])) { return CleanUpVariableName(m.Name.Substring(3)); } } var p = call.GetParameter(i); if (p != null && !string.IsNullOrEmpty(p.Name)) return CleanUpVariableName(p.Name); break; case Leave _: return "result"; } return null; } static bool ExcludeMethodFromCandidates(IMethod m) { if (m.SymbolKind == SymbolKind.Operator) return true; if (m.Name == "ToString") return true; if (m.Name == "Concat" && m.DeclaringType.IsKnownType(KnownTypeCode.String)) return true; if (m.Name == "GetPinnableReference") return true; return false; } static string GetNameByType(IType type) { type = NullableType.GetUnderlyingType(type); while (type is ModifiedType || type is PinnedType) { type = NullableType.GetUnderlyingType(((TypeWithElementType)type).ElementType); } string name; if (type.IsAnonymousType()) { name = "anon"; } else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) { name = "ex"; } else if (type.Name.EndsWith("EventArgs", StringComparison.Ordinal)) { name = "e"; } else if (type.IsCSharpNativeIntegerType()) { name = "num"; } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { name = type.Kind switch { TypeKind.Array => "array", TypeKind.Pointer => "ptr", TypeKind.TypeParameter => "val", TypeKind.Unknown => "val", TypeKind.Dynamic => "val", TypeKind.ByReference => "reference", TypeKind.Tuple => "tuple", TypeKind.NInt => "num", TypeKind.NUInt => "num", _ => type.Name }; // remove the 'I' for interfaces if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) name = name.Substring(1); name = CleanUpVariableName(name) ?? "obj"; } return name; } static void AddExistingName(Dictionary reservedVariableNames, string name) { if (string.IsNullOrEmpty(name)) return; string nameWithoutDigits = SplitName(name, out int number); if (reservedVariableNames.TryGetValue(nameWithoutDigits, out int existingNumber)) { reservedVariableNames[nameWithoutDigits] = Math.Max(number, existingNumber); } else { reservedVariableNames.Add(nameWithoutDigits, number); } } static string SplitName(string name, out int number) { // First, identify whether the name already ends with a number: int pos = name.Length; while (pos > 0 && name[pos - 1] >= '0' && name[pos - 1] <= '9') pos--; if (pos < name.Length) { if (int.TryParse(name.Substring(pos), out number)) { return name.Substring(0, pos); } } number = 1; return name; } static string CleanUpVariableName(string name) { // remove the backtick (generics) int pos = name.IndexOf('`'); if (pos >= 0) name = name.Substring(0, pos); // remove field prefix: if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal)) name = name.Substring(2); else if (name.Length > 1 && name[0] == '_' && (char.IsLetter(name[1]) || name[1] == '_')) name = name.Substring(1); if (TextWriterTokenWriter.ContainsNonPrintableIdentifierChar(name)) { return null; } if (name.Length == 0) return "obj"; string lowerCaseName = char.ToLower(name[0]) + name.Substring(1); if (CSharp.OutputVisitor.CSharpOutputVisitor.IsKeyword(lowerCaseName)) return null; return lowerCaseName; } static IType GuessType(IType variableType, ILInstruction inst, ILTransformContext context) { if (!variableType.IsKnownType(KnownTypeCode.Object)) return variableType; IType inferredType = inst.InferType(context.TypeSystem); if (inferredType.Kind != TypeKind.Unknown) return inferredType; else return variableType; } static Dictionary CollectReservedVariableNames(ILFunction function, ILVariable existingVariable, bool mustResolveConflicts, UsingScope usingScope) { var reservedVariableNames = new Dictionary(); var rootFunction = function.Ancestors.OfType().Single(f => f.Parent == null); foreach (var f in rootFunction.Descendants.OfType()) { foreach (var p in rootFunction.Parameters) { AddExistingName(reservedVariableNames, p.Name); } foreach (var v in f.Variables.Where(v => v.Kind != VariableKind.Parameter)) { if (v != existingVariable) AddExistingName(reservedVariableNames, v.Name); } } if (mustResolveConflicts) { var memberNames = CollectAllLowerCaseMemberNames(function.Method.DeclaringTypeDefinition) .Concat(CollectAllLowerCaseTypeNames(function.Method.DeclaringTypeDefinition)) .Concat(CollectAllLowerCaseTypeNames(usingScope)); foreach (var name in memberNames) AddExistingName(reservedVariableNames, name); } return reservedVariableNames; } internal static string GenerateForeachVariableName(ILFunction function, ILInstruction valueContext, UsingScope usingScope, ILVariable existingVariable = null, bool mustResolveConflicts = false) { if (function == null) throw new ArgumentNullException(nameof(function)); if (existingVariable != null && !existingVariable.HasGeneratedName) { return existingVariable.Name; } var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts, usingScope); string baseName = GetNameFromInstruction(valueContext); if (string.IsNullOrEmpty(baseName)) { if (valueContext is LdLoc ldloc && ldloc.Variable.Kind == VariableKind.Parameter) { baseName = ldloc.Variable.Name; } } string proposedName = "item"; if (!string.IsNullOrEmpty(baseName)) { if (!IsPlural(baseName, ref proposedName)) { if (baseName.Length > 4 && baseName.EndsWith("List", StringComparison.Ordinal)) { proposedName = baseName.Substring(0, baseName.Length - 4); } else if (baseName.Equals("list", StringComparison.OrdinalIgnoreCase)) { proposedName = "item"; } else if (baseName.EndsWith("children", StringComparison.OrdinalIgnoreCase)) { proposedName = baseName.Remove(baseName.Length - 3); } } } // remove any numbers from the proposed name proposedName = SplitName(proposedName, out int number); if (!reservedVariableNames.ContainsKey(proposedName)) { reservedVariableNames.Add(proposedName, 0); } int count = ++reservedVariableNames[proposedName]; Debug.Assert(!string.IsNullOrWhiteSpace(proposedName)); if (count > 1) { return proposedName + count.ToString(); } else { return proposedName; } } internal static string GenerateVariableName(ILFunction function, IType type, UsingScope usingScope, ILInstruction valueContext = null, ILVariable existingVariable = null, bool mustResolveConflicts = false) { if (function == null) throw new ArgumentNullException(nameof(function)); var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts, usingScope); string baseName = valueContext != null ? GetNameFromInstruction(valueContext) ?? GetNameByType(type) : GetNameByType(type); string proposedName = "obj"; if (!string.IsNullOrEmpty(baseName)) { if (!IsPlural(baseName, ref proposedName)) { if (baseName.Length > 4 && baseName.EndsWith("List", StringComparison.Ordinal)) { proposedName = baseName.Substring(0, baseName.Length - 4); } else if (baseName.Equals("list", StringComparison.OrdinalIgnoreCase)) { proposedName = "item"; } else if (baseName.EndsWith("children", StringComparison.OrdinalIgnoreCase)) { proposedName = baseName.Remove(baseName.Length - 3); } else { proposedName = baseName; } } } // remove any numbers from the proposed name proposedName = SplitName(proposedName, out int number); if (!reservedVariableNames.ContainsKey(proposedName)) { reservedVariableNames.Add(proposedName, 0); } int count = ++reservedVariableNames[proposedName]; Debug.Assert(!string.IsNullOrWhiteSpace(proposedName)); if (count > 1) { return proposedName + count.ToString(); } else { return proposedName; } } private static bool IsPlural(string baseName, ref string proposedName) { var newName = Vocabularies.Default.Singularize(baseName, inputIsKnownToBePlural: false); if (string.IsNullOrWhiteSpace(newName) || newName == baseName) return false; proposedName = newName; return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Per-block IL transform. /// public interface IBlockTransform { /// /// Runs the transform on the specified block. /// /// Note: the transform may only modify the specified block and its descendants, /// as well as any sibling blocks that are dominated by the specified block. /// void Run(Block block, BlockTransformContext context); } /// /// Parameter class holding various arguments for . /// public class BlockTransformContext : ILTransformContext { /// /// The block to process. /// /// /// Should be identical to the block parameter to IBlockTransform.Run. /// public Block Block { get; set; } /// /// The control flow node corresponding to the block being processed. /// /// /// Identical to ControlFlowGraph.GetNode(Block). /// Note: the control flow graph is not up-to-date, but was created at the start of the /// block transforms (before loop detection). /// public ControlFlowNode ControlFlowNode { get; set; } /// /// Gets the control flow graph. /// /// Note: the control flow graph is not up-to-date, but was created at the start of the /// block transforms (before loop detection). /// public ControlFlowGraph ControlFlowGraph { get; set; } /// /// Initially equal to Block.Instructions.Count indicating that nothing has been transformed yet. /// Set by when another already transformed block is merged into /// the current block. Subsequent s must update this value, for example, /// by resetting it to Block.Instructions.Count. will use this value to /// skip already transformed instructions. /// public int IndexOfFirstAlreadyTransformedInstruction { get; set; } public BlockTransformContext(ILTransformContext context) : base(context) { } } /// /// IL transform that runs a list of per-block transforms. /// public class BlockILTransform : IILTransform { public IList PreOrderTransforms { get; } = new List(); public IList PostOrderTransforms { get; } = new List(); bool running; public override string ToString() { return $"{nameof(BlockILTransform)} ({string.Join(", ", PreOrderTransforms.Concat(PostOrderTransforms).Select(t => t.GetType().Name))})"; } public void Run(ILFunction function, ILTransformContext context) { if (running) throw new InvalidOperationException("Reentrancy detected. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant."); try { running = true; var blockContext = new BlockTransformContext(context); Debug.Assert(blockContext.Function == function); foreach (var container in function.Descendants.OfType().ToList()) { context.CancellationToken.ThrowIfCancellationRequested(); blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); } } finally { running = false; } } /// /// Walks the dominator tree rooted at entryNode, calling the transforms on each block. /// void VisitBlock(ControlFlowNode entryNode, BlockTransformContext context) { IEnumerable Preorder(ControlFlowNode cfgNode) { // preorder processing: Block block = (Block)cfgNode.UserData; context.StepStartGroup(block.Label, block); context.ControlFlowNode = cfgNode; context.Block = block; context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; block.RunTransforms(PreOrderTransforms, context); // process the children return cfgNode.DominatorTreeChildren; } foreach (var cfgNode in TreeTraversal.PostOrder(entryNode, Preorder)) { // in post-order: Block block = (Block)cfgNode.UserData; context.ControlFlowNode = cfgNode; context.Block = block; context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; block.RunTransforms(PostOrderTransforms, context); context.StepEndGroup(); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs ================================================ // Copyright (c) 2011-2016 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.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class CachedDelegateInitialization : IBlockTransform { BlockTransformContext context; public void Run(Block block, BlockTransformContext context) { this.context = context; if (!context.Settings.AnonymousMethods) return; for (int i = context.IndexOfFirstAlreadyTransformedInstruction - 1; i >= 0; i--) { if (block.Instructions[i] is IfInstruction inst) { if (CachedDelegateInitializationWithField(inst)) { block.Instructions.RemoveAt(i); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (CachedDelegateInitializationWithLocal(inst)) { ILInlining.InlineOneIfPossible(block, i, InliningOptions.Aggressive, context); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (CachedDelegateInitializationRoslynInStaticWithLocal(inst) || CachedDelegateInitializationRoslynWithLocal(inst)) { block.Instructions.RemoveAt(i); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (CachedDelegateInitializationVB(inst)) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (CachedDelegateInitializationVBWithReturn(inst)) { block.Instructions.RemoveAt(i); context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (CachedDelegateInitializationVBWithClosure(inst)) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } } } } /// /// if (comp(ldsfld CachedAnonMethodDelegate == ldnull)) { /// stsfld CachedAnonMethodDelegate(DelegateConstruction) /// } /// ... one usage of CachedAnonMethodDelegate ... /// => /// ... one usage of DelegateConstruction ... /// bool CachedDelegateInitializationWithField(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop()) return false; var storeInst = trueInst.Instructions[0]; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdsFld(out IField field) || !right.MatchLdNull()) return false; if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return false; if (!DelegateConstruction.MatchDelegateConstruction(value.UnwrapConv(ConversionKind.Invalid) as NewObj, out _, out _, out _, true)) return false; var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) return false; var usages = nextInstruction.Descendants.Where(i => i.MatchLdsFld(field)).ToArray(); if (usages.Length != 1) return false; context.Step("CachedDelegateInitializationWithField", inst); usages[0].ReplaceWith(value); return true; } /// /// if (comp(ldloc v == ldnull)) { /// stloc v(DelegateConstruction) /// } /// => /// stloc v(DelegateConstruction) /// bool CachedDelegateInitializationWithLocal(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop()) return false; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable v) || !right.MatchLdNull()) return false; var storeInst = trueInst.Instructions.Last(); if (!storeInst.MatchStLoc(v, out ILInstruction value)) return false; if (!DelegateConstruction.MatchDelegateConstruction(value as NewObj, out _, out _, out _, true)) return false; // do not transform if there are other stores/loads of this variable if (v.StoreCount != 2 || v.StoreInstructions.Count != 2 || v.LoadCount != 2 || v.AddressCount != 0) return false; // do not transform if the first assignment is not assigning null: var otherStore = v.StoreInstructions.OfType().SingleOrDefault(store => store != storeInst); if (otherStore == null || !otherStore.Value.MatchLdNull() || !(otherStore.Parent is Block)) return false; // do not transform if there is no usage directly afterwards var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) return false; var usages = nextInstruction.Descendants.Where(i => i.MatchLdLoc(v)).ToArray(); if (usages.Length != 1) return false; context.Step("CachedDelegateInitializationWithLocal", inst); ((Block)otherStore.Parent).Instructions.Remove(otherStore); inst.ReplaceWith(storeInst); return true; } /// /// stloc s(ldobj(ldsflda(CachedAnonMethodDelegate)) /// if (comp(ldloc s == null)) { /// stloc s(stobj(ldsflda(CachedAnonMethodDelegate), DelegateConstruction)) /// } /// => /// stloc s(DelegateConstruction) /// bool CachedDelegateInitializationRoslynInStaticWithLocal(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop()) return false; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable s) || !right.MatchLdNull()) return false; var storeInst = trueInst.Instructions.Last() as StLoc; var storeBeforeIf = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex - 1) as StLoc; if (storeBeforeIf == null || storeInst == null || storeBeforeIf.Variable != s || storeInst.Variable != s) return false; if (!(storeInst.Value is StObj stobj) || !(storeBeforeIf.Value is LdObj ldobj)) return false; if (!(stobj.Value is NewObj)) return false; if (!stobj.Target.MatchLdsFlda(out var field1) || !ldobj.Target.MatchLdsFlda(out var field2) || !field1.Equals(field2)) return false; if (!DelegateConstruction.MatchDelegateConstruction((NewObj)stobj.Value, out _, out _, out _, true)) return false; context.Step("CachedDelegateInitializationRoslynInStaticWithLocal", inst); storeBeforeIf.Value = stobj.Value; return true; } /// /// stloc s(ldobj(ldflda(CachedAnonMethodDelegate)) /// if (comp(ldloc s == null)) { /// stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction)) /// } /// => /// stloc s(DelegateConstruction) /// bool CachedDelegateInitializationRoslynWithLocal(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop()) return false; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable s) || !right.MatchLdNull()) return false; var storeInst = trueInst.Instructions.Last() as StLoc; var storeBeforeIf = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex - 1) as StLoc; if (storeBeforeIf == null || storeInst == null || storeBeforeIf.Variable != s || storeInst.Variable != s) return false; if (!(storeInst.Value is StObj stobj) || !(storeBeforeIf.Value is LdObj ldobj)) return false; if (!(stobj.Value is NewObj)) return false; if (!stobj.Target.MatchLdFlda(out var _, out var field1) || !ldobj.Target.MatchLdFlda(out var __, out var field2) || !field1.Equals(field2)) return false; if (!DelegateConstruction.MatchDelegateConstruction((NewObj)stobj.Value, out _, out _, out _, true)) return false; context.Step("CachedDelegateInitializationRoslynWithLocal", inst); storeBeforeIf.Value = stobj.Value; return true; } /// /// if (comp.i4(comp.o(ldobj delegateType(ldsflda CachedAnonMethodDelegate) != ldnull) == ldc.i4 0)) Block { /// stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction)) /// } else Block { /// stloc s(ldobj System.Action(ldsflda $I4-1)) /// } /// => /// stloc s(DelegateConstruction) /// bool CachedDelegateInitializationVB(IfInstruction inst) { if (!(inst.TrueInst is Block trueInst && inst.FalseInst is Block falseInst)) return false; if (trueInst.Instructions.Count != 1 || falseInst.Instructions.Count != 1) return false; if (!(trueInst.Instructions[0].MatchStLoc(out var s, out var trueInitValue) && falseInst.Instructions[0].MatchStLoc(s, out var falseInitValue))) { return false; } if (s.Kind != VariableKind.StackSlot || s.StoreCount != 2) return false; if (!(trueInitValue is StObj stobj) || !(falseInitValue is LdObj ldobj)) return false; if (!(stobj.Value is NewObj delegateConstruction)) return false; if (!stobj.Target.MatchLdsFlda(out var field1) || !ldobj.Target.MatchLdsFlda(out var field2) || !field1.Equals(field2)) { return false; } if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !right.MatchLdNull()) return false; if (!ldobj.Match(left).Success) return false; if (!DelegateConstruction.MatchDelegateConstruction(delegateConstruction, out _, out _, out _, true)) return false; context.Step("CachedDelegateInitializationVB", inst); inst.ReplaceWith(new StLoc(s, delegateConstruction)); return true; } /// /// if (comp.o(ldsfld CachedAnonMethodDelegate != ldnull)) { /// leave IL_0005 (ldsfld CachedAnonMethodDelegate) /// } /// leave IL_0005 (stsfld CachedAnonMethodDelegate(DelegateConstruction)) /// => /// leave IL_0005 (DelegateConstruction) /// bool CachedDelegateInitializationVBWithReturn(IfInstruction inst) { if (!inst.Condition.MatchCompNotEqualsNull(out var arg) || !arg.MatchLdsFld(out var field)) return false; if (!inst.FalseInst.MatchNop()) return false; if (!inst.TrueInst.MatchReturn(out arg) || !arg.MatchLdsFld(field)) return false; var leaveAfterIf = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1) as Leave; if (leaveAfterIf is null || !leaveAfterIf.IsLeavingFunction) return false; if (!leaveAfterIf.Value.MatchStsFld(out var field2, out var delegateConstruction) || !field.Equals(field2)) return false; if (!DelegateConstruction.MatchDelegateConstruction(delegateConstruction, out _, out _, out _, true)) return false; context.Step("CachedDelegateInitializationVBWithReturn", inst); leaveAfterIf.Value = delegateConstruction; return true; } /// /// if (comp.o(ldobj delegateType(ldflda CachedAnonMethodDelegate(ldloc closure)) != ldnull)) Block { /// stloc s(ldobj delegateType(ldflda CachedAnonMethodDelegate(ldloc closure))) /// } else Block { /// stloc s(stobj delegateType(ldflda CachedAnonMethodDelegate(ldloc closure), DelegateConstruction)) /// } /// => /// stloc s(DelegateConstruction) /// bool CachedDelegateInitializationVBWithClosure(IfInstruction inst) { if (!(inst.TrueInst is Block trueInst && inst.FalseInst is Block falseInst)) return false; if (trueInst.Instructions.Count != 1 || falseInst.Instructions.Count != 1) return false; if (!(trueInst.Instructions[0].MatchStLoc(out var s, out var trueInitValue) && falseInst.Instructions[0].MatchStLoc(s, out var falseInitValue))) { return false; } if (s.Kind != VariableKind.StackSlot || s.StoreCount != 2) return false; if (!(falseInitValue is StObj stobj) || !(trueInitValue is LdObj ldobj)) return false; if (!(stobj.Value is NewObj delegateConstruction)) return false; if (!stobj.Target.MatchLdFlda(out var target1, out var field1) || !ldobj.Target.MatchLdFlda(out var target2, out var field2) || !field1.Equals(field2) || !target1.Match(target2).Success) { return false; } if (!inst.Condition.MatchCompNotEqualsNull(out ILInstruction left)) return false; if (!ldobj.Match(left).Success) return false; if (!DelegateConstruction.MatchDelegateConstruction(delegateConstruction, out _, out _, out _, true)) return false; context.Step("CachedDelegateInitializationVBWithClosure", inst); inst.ReplaceWith(new StLoc(s, delegateConstruction)); return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs ================================================ // 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 // 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. namespace ICSharpCode.Decompiler.IL.Transforms { class CombineExitsTransform : IILTransform { public void Run(ILFunction function, ILTransformContext context) { if (!(function.Body is BlockContainer container && container.Blocks.Count == 1)) return; var combinedExit = CombineExits(container.EntryPoint); if (combinedExit == null) return; ExpressionTransforms.RunOnSingleStatement(combinedExit, context); } static Leave CombineExits(Block block) { if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst && block.Instructions.LastOrDefault() is Leave leaveElse)) return null; if (!ifInst.FalseInst.MatchNop()) return null; // try to unwrap true branch to single instruction: var trueInstruction = Block.Unwrap(ifInst.TrueInst); // if the true branch is a block with multiple instructions: // try to apply the combine exits transform to the nested block // and then continue on that transformed block. // Example: // if (cond) { // if (cond2) { // leave (value) // } // leave (value2) // } // leave (value3) // => // leave (if (cond) value else if (cond2) value2 else value3) if (trueInstruction is Block nestedBlock && nestedBlock.Instructions.Count == 2) trueInstruction = CombineExits(nestedBlock); if (!(trueInstruction is Leave leave)) return null; if (!(leave.IsLeavingFunction && leaveElse.IsLeavingFunction)) return null; if (leave.Value.MatchNop() || leaveElse.Value.MatchNop()) return null; // if (cond) { // leave (value) // } // leave (elseValue) // => // leave (if (cond) value else elseValue) IfInstruction value = new IfInstruction(ifInst.Condition, leave.Value, leaveElse.Value); value.AddILRange(ifInst); Leave combinedLeave = new Leave(leave.TargetContainer, value); combinedLeave.AddILRange(leaveElse); combinedLeave.AddILRange(leave); ifInst.ReplaceWith(combinedLeave); block.Instructions.RemoveAt(combinedLeave.ChildIndex + 1); return combinedLeave; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs ================================================ // Copyright (c) 2011-2015 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Runs a very simple form of copy propagation. /// Copy propagation is used in two cases: /// 1) assignments from arguments to local variables /// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg), /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// public class CopyPropagation : IILTransform { public static void Propagate(StLoc store, ILTransformContext context) { Debug.Assert(store.Variable.IsSingleDefinition); Block block = (Block)store.Parent; int i = store.ChildIndex; DoPropagate(store.Variable, store.Value, block, ref i, context); } public void Run(ILFunction function, ILTransformContext context) { var splitVariables = new HashSet(ILVariableEqualityComparer.Instance); foreach (var g in function.Variables.GroupBy(v => v, ILVariableEqualityComparer.Instance)) { if (g.Count() > 1) { splitVariables.Add(g.Key); } } foreach (var block in function.Descendants.OfType()) { if (block.Kind != BlockKind.ControlFlow) continue; RunOnBlock(block, context, splitVariables); } } static void RunOnBlock(Block block, ILTransformContext context, HashSet splitVariables = null) { for (int i = 0; i < block.Instructions.Count; i++) { if (block.Instructions[i].MatchStLoc(out ILVariable v, out ILInstruction copiedExpr)) { if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot) { // dead store to stack if (SemanticHelper.IsPure(copiedExpr.Flags)) { // no-op -> delete context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]); block.Instructions.RemoveAt(i); // This can open up new inlining opportunities: int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context); i -= c + 1; } else { // evaluate the value for its side-effects context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]); copiedExpr.AddILRange(block.Instructions[i]); block.Instructions[i] = copiedExpr; } } else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr, splitVariables, context.Settings)) { DoPropagate(v, copiedExpr, block, ref i, context); } } } } static bool CanPerformCopyPropagation(ILVariable target, ILInstruction value, HashSet splitVariables, DecompilerSettings settings) { Debug.Assert(target.StackType == value.ResultType); if (target.Type.IsSmallIntegerType()) return false; if (splitVariables != null && splitVariables.Contains(target)) { return false; // non-local code move might change semantics when there's split variables } switch (value.OpCode) { case OpCode.LdLoca: case OpCode.LdsFlda: // All address-loading instructions always return the same value for a given operand/argument combination, // so they can be safely copied. // ... except for LdElema and LdFlda, because those might throw an exception, and we don't want to // change the place where the exception is thrown. return true; case OpCode.LdElema: case OpCode.LdFlda: return !settings.UseRefLocalsForAccurateOrderOfEvaluation; case OpCode.LdLoc: var v = ((LdLoc)value).Variable; if (splitVariables != null && splitVariables.Contains(v)) { return false; // non-local code move might change semantics when there's split variables } switch (v.Kind) { case VariableKind.Parameter: // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) // note: the initialization by the caller is the first store -> StoreCount must be 1 return v.IsSingleDefinition; default: // Variables can be copied if both are single-definition. // To avoid removing too many variables, we do this only if the target // is either a stackslot or a ref local. Debug.Assert(target.IsSingleDefinition); return v.IsSingleDefinition && (target.Kind == VariableKind.StackSlot || target.StackType == StackType.Ref); } default: // All instructions without special behavior that target a stack-variable can be copied. return value.Flags == InstructionFlags.None && value.Children.Count == 0 && target.Kind == VariableKind.StackSlot; } } static void DoPropagate(ILVariable v, ILInstruction copiedExpr, Block block, ref int i, ILTransformContext context) { context.Step($"Copy propagate {v.Name}", copiedExpr); // un-inline the arguments of the ldArg instruction ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count]; for (int j = 0; j < uninlinedArgs.Length; j++) { var arg = copiedExpr.Children[j]; var type = context.TypeSystem.FindType(arg.ResultType); uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType) { Name = "C_" + arg.StartILOffset, HasGeneratedName = true, }; block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg)); } v.Function.Variables.AddRange(uninlinedArgs); // perform copy propagation: foreach (var expr in v.LoadInstructions.ToArray()) { var clone = copiedExpr.Clone(); for (int j = 0; j < uninlinedArgs.Length; j++) { clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j])); } // We are copying an expression from far away, reusing the ILRange would result in incorrect sequence points. clone.SetILRange(new Interval()); expr.ReplaceWith(clone); } block.Instructions.RemoveAt(i); int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context); i -= c + 1; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs ================================================ // Copyright (c) 2020 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.Collections.Immutable; using System.Linq; using System.Resources; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// /// class DeconstructionTransform : IStatementTransform { StatementTransformContext context; readonly Dictionary deconstructionResultsLookup = new Dictionary(); ILVariable[] deconstructionResults; ILVariable tupleVariable; TupleType tupleType; /* stloc tuple(call MakeIntIntTuple(ldloc this)) ---- stloc myInt(call op_Implicit(ldfld Item2(ldloca tuple))) stloc a(ldfld Item1(ldloca tuple)) stloc b(ldloc myInt) ==> deconstruct { init: deconstruct: match.deconstruct(temp = ldloca tuple) { match(result0 = deconstruct.result 0(temp)), match(result1 = deconstruct.result 1(temp)) } conversions: { stloc conv2(call op_Implicit(ldloc result1)) } assignments: { stloc a(ldloc result0) stloc b(ldloc conv2) } } * */ void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.Deconstruction) return; try { this.context = context; Reset(); if (TransformDeconstruction(block, pos)) return; if (InlineDeconstructionInitializer(block, pos)) return; } finally { this.context = null; Reset(); } } private void Reset() { this.deconstructionResultsLookup.Clear(); this.tupleVariable = null; this.tupleType = null; this.deconstructionResults = null; } struct ConversionInfo { public IType inputType; public Conv conv; } /// /// Get index of deconstruction result or tuple element /// Returns -1 on failure. /// int FindIndex(ILInstruction inst, out Action delayedActions) { delayedActions = null; if (inst.MatchLdLoc(out var v)) { if (!deconstructionResultsLookup.TryGetValue(v, out int index)) return -1; return index; } if (inst.MatchLdFld(out _, out _)) { if (!TupleTransform.MatchTupleFieldAccess((LdFlda)((LdObj)inst).Target, out var tupleType, out var target, out int index)) return -1; // Item fields are one-based, we use zero-based indexing. index--; // normalize tuple type tupleType = TupleType.FromUnderlyingType(context.TypeSystem, tupleType); if (!target.MatchLdLoca(out v)) return -1; if (this.tupleVariable == null) { this.tupleVariable = v; this.tupleType = (TupleType)tupleType; this.deconstructionResults = new ILVariable[this.tupleType.Cardinality]; } if (this.tupleType.Cardinality < 2) return -1; if (v != tupleVariable || !this.tupleType.Equals(tupleType)) return -1; if (this.deconstructionResults[index] == null) { var freshVar = new ILVariable(VariableKind.StackSlot, this.tupleType.ElementTypes[index]) { Name = "E_" + index }; delayedActions += _ => context.Function.Variables.Add(freshVar); this.deconstructionResults[index] = freshVar; } delayedActions += _ => { inst.ReplaceWith(new LdLoc(this.deconstructionResults[index])); }; return index; } return -1; } /// /// stloc v(value) /// expr(..., deconstruct { ... }, ...) /// => /// expr(..., deconstruct { init: stloc v(value) ... }, ...) /// bool InlineDeconstructionInitializer(Block block, int pos) { if (!block.Instructions[pos].MatchStLoc(out var v, out var value)) return false; if (!(v.IsSingleDefinition && v.LoadCount == 1)) return false; if (pos + 1 >= block.Instructions.Count) return false; var result = ILInlining.FindLoadInNext(block.Instructions[pos + 1], v, value, InliningOptions.FindDeconstruction); if (result.Type != ILInlining.FindResultType.Deconstruction) return false; var deconstruction = (DeconstructInstruction)result.LoadInst; LdLoc loadInst = v.LoadInstructions[0]; if (!loadInst.IsDescendantOf(deconstruction.Assignments)) return false; if (loadInst.SlotInfo == StObj.TargetSlot) { if (value.OpCode == OpCode.LdFlda || value.OpCode == OpCode.LdElema) return false; } if (deconstruction.Init.Count > 0) { var a = deconstruction.Init[0].Variable.LoadInstructions.Single(); var b = v.LoadInstructions.Single(); if (!b.IsBefore(a)) return false; } context.Step("InlineDeconstructionInitializer", block.Instructions[pos]); deconstruction.Init.Insert(0, (StLoc)block.Instructions[pos]); block.Instructions.RemoveAt(pos); v.Kind = VariableKind.DeconstructionInitTemporary; return true; } bool TransformDeconstruction(Block block, int pos) { int startPos = pos; Action delayedActions = null; if (MatchDeconstruction(block.Instructions[pos], out IMethod deconstructMethod, out ILInstruction rootTestedOperand)) { pos++; } if (!MatchConversions(block, ref pos, out var conversions, out var conversionStLocs, ref delayedActions)) return false; if (!MatchAssignments(block, ref pos, conversions, conversionStLocs, ref delayedActions)) return false; // first tuple element may not be discarded, // otherwise we would run this transform on a suffix of the actual pattern. if (deconstructionResults[0] == null) return false; context.Step("Deconstruction", block.Instructions[startPos]); DeconstructInstruction replacement = new DeconstructInstruction(); IType deconstructedType; if (deconstructMethod == null) { deconstructedType = this.tupleType; rootTestedOperand = new LdLoc(this.tupleVariable); } else { if (deconstructMethod.IsStatic) { deconstructedType = deconstructMethod.Parameters[0].Type; } else { deconstructedType = deconstructMethod.DeclaringType; } } var rootTempVariable = context.Function.RegisterVariable(VariableKind.PatternLocal, deconstructedType); replacement.Pattern = new MatchInstruction(rootTempVariable, deconstructMethod, rootTestedOperand) { IsDeconstructCall = deconstructMethod != null, IsDeconstructTuple = this.tupleType != null }; int index = 0; foreach (ILVariable v in deconstructionResults) { var result = v; if (result == null) { var freshVar = new ILVariable(VariableKind.PatternLocal, this.tupleType.ElementTypes[index]) { Name = "E_" + index }; context.Function.Variables.Add(freshVar); result = freshVar; } else { result.Kind = VariableKind.PatternLocal; } replacement.Pattern.SubPatterns.Add( new MatchInstruction( result, new DeconstructResultInstruction(index, result.StackType, new LdLoc(rootTempVariable)) ) ); index++; } replacement.Conversions = new Block(BlockKind.DeconstructionConversions); foreach (var convInst in conversionStLocs) { replacement.Conversions.Instructions.Add(convInst); } replacement.Assignments = new Block(BlockKind.DeconstructionAssignments); delayedActions?.Invoke(replacement); block.Instructions[startPos] = replacement; block.Instructions.RemoveRange(startPos + 1, pos - startPos - 1); return true; } bool MatchDeconstruction(ILInstruction inst, out IMethod deconstructMethod, out ILInstruction testedOperand) { testedOperand = null; deconstructMethod = null; deconstructionResults = null; if (!(inst is CallInstruction call)) return false; if (!MatchInstruction.IsDeconstructMethod(call.Method)) return false; if (call.Method.IsStatic || call.Method.DeclaringType.IsReferenceType == false) { if (!(call is Call)) return false; } else { if (!(call is CallVirt)) return false; } if (call.Arguments.Count < 3) return false; deconstructionResults = new ILVariable[call.Arguments.Count - 1]; for (int i = 0; i < deconstructionResults.Length; i++) { if (!call.Arguments[i + 1].MatchLdLoca(out var v)) return false; // TODO v.LoadCount may be 2 if the deconstruction is assigned to a tuple variable // or 0? because of discards if (!(v.StoreCount == 0 && v.AddressCount == 1 && v.LoadCount <= 1)) return false; deconstructionResultsLookup.Add(v, i); deconstructionResults[i] = v; } testedOperand = call.Arguments[0]; deconstructMethod = call.Method; return true; } bool MatchConversions(Block block, ref int pos, out Dictionary conversions, out List conversionStLocs, ref Action delayedActions) { conversions = new Dictionary(); conversionStLocs = new List(); int previousIndex = -1; while (MatchConversion( block.Instructions.ElementAtOrDefault(pos), out var inputInstruction, out var outputVariable, out var info)) { int index = FindIndex(inputInstruction, out var tupleAccessAdjustment); if (index <= previousIndex) return false; if (!(outputVariable.IsSingleDefinition && outputVariable.LoadCount == 1)) return false; delayedActions += tupleAccessAdjustment; deconstructionResultsLookup.Add(outputVariable, index); conversions.Add(outputVariable, info); conversionStLocs.Add((StLoc)block.Instructions[pos]); pos++; previousIndex = index; } return true; } bool MatchConversion(ILInstruction inst, out ILInstruction inputInstruction, out ILVariable outputVariable, out ConversionInfo info) { info = default; inputInstruction = null; if (!inst.MatchStLoc(out outputVariable, out var value)) return false; if (!(value is Conv conv)) return false; info = new ConversionInfo { inputType = conv.Argument.InferType(context.TypeSystem), conv = conv }; inputInstruction = conv.Argument; return true; } bool MatchAssignments(Block block, ref int pos, Dictionary conversions, List conversionStLocs, ref Action delayedActions) { int previousIndex = -1; int conversionStLocIndex = 0; int startPos = pos; while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), out var targetType, out var valueInst, out var addAssignment)) { int index = FindIndex(valueInst, out var tupleAccessAdjustment); if (index <= previousIndex) return false; AddMissingAssignmentsForConversions(index, ref delayedActions); if (!(valueInst.MatchLdLoc(out var resultVariable) && conversions.TryGetValue(resultVariable, out var conversionInfo))) { conversionInfo = new ConversionInfo { inputType = valueInst.InferType(context.TypeSystem) }; } if (block.Instructions[pos].MatchStLoc(out var assignmentTarget, out _) && assignmentTarget.Kind == VariableKind.StackSlot && assignmentTarget.IsSingleDefinition && conversionInfo.conv == null) { delayedActions += _ => { assignmentTarget.Type = conversionInfo.inputType; }; } else { if (!IsCompatibleImplicitConversion(targetType, conversionInfo)) return false; } delayedActions += addAssignment; delayedActions += tupleAccessAdjustment; pos++; previousIndex = index; } AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions); if (deconstructionResults != null) { int i = previousIndex + 1; while (i < deconstructionResults.Length) { var v = deconstructionResults[i]; // this should only happen in release mode, where usually the last deconstruction element // is not stored to a temporary, if it is used directly (and only once!) // after the deconstruction. if (v?.LoadCount == 1) { delayedActions += (DeconstructInstruction deconstructInst) => { var freshVar = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type); deconstructInst.Assignments.Instructions.Add(new StLoc(freshVar, new LdLoc(v))); v.LoadInstructions[0].Variable = freshVar; }; } i++; } } return startPos != pos; void AddMissingAssignmentsForConversions(int index, ref Action delayedActions) { while (conversionStLocIndex < conversionStLocs.Count) { var stLoc = conversionStLocs[conversionStLocIndex]; int conversionResultIndex = deconstructionResultsLookup[stLoc.Variable]; if (conversionResultIndex >= index) break; if (conversionResultIndex > previousIndex) { delayedActions += (DeconstructInstruction deconstructInst) => { var freshVar = context.Function.RegisterVariable(VariableKind.StackSlot, stLoc.Variable.Type); deconstructInst.Assignments.Instructions.Add(new StLoc(stLoc.Variable, new LdLoc(freshVar))); stLoc.Variable = freshVar; }; } previousIndex = conversionResultIndex; conversionStLocIndex++; } } } bool MatchAssignment(ILInstruction inst, out IType targetType, out ILInstruction valueInst, out Action addAssignment) { targetType = null; valueInst = null; addAssignment = null; if (inst == null) return false; if (inst.MatchStLoc(out var v, out var value) && value is Block block && block.MatchInlineAssignBlock(out var call, out valueInst)) { if (!DeconstructInstruction.IsAssignment(call, context.TypeSystem, out targetType, out _)) return false; if (!(v.IsSingleDefinition && v.LoadCount == 0)) return false; var valueInstCopy = valueInst; addAssignment = (DeconstructInstruction deconstructInst) => { call.Arguments[call.Arguments.Count - 1] = valueInstCopy; deconstructInst.Assignments.Instructions.Add(call); }; return true; } else if (DeconstructInstruction.IsAssignment(inst, context.TypeSystem, out targetType, out valueInst)) { // OK - use the assignment as is addAssignment = (DeconstructInstruction deconstructInst) => { deconstructInst.Assignments.Instructions.Add(inst); }; return true; } else { return false; } } bool IsCompatibleImplicitConversion(IType targetType, ConversionInfo conversionInfo) { var c = CSharpConversions.Get(context.TypeSystem) .ImplicitConversion(conversionInfo.inputType, targetType); if (!c.IsValid) return false; var inputType = conversionInfo.inputType; var conv = conversionInfo.conv; if (c.IsIdentityConversion || c.IsReferenceConversion) { return conv == null || conv.Kind == ConversionKind.Nop; } if (c.IsNumericConversion && conv != null) { switch (conv.Kind) { case ConversionKind.IntToFloat: return inputType.GetSign() == conv.InputSign; case ConversionKind.FloatPrecisionChange: return true; case ConversionKind.SignExtend: return inputType.GetSign() == Sign.Signed; case ConversionKind.ZeroExtend: return inputType.GetSign() == Sign.Unsigned; default: return false; } } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs ================================================ // Copyright (c) 2011-2016 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.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transforms anonymous methods and lambdas by creating nested ILFunctions. /// public class DelegateConstruction : IILTransform { ILTransformContext context; ITypeResolveContext decompilationContext; readonly Stack activeMethods = new Stack(); void IILTransform.Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AnonymousMethods) return; var prevContext = this.context; var prevDecompilationContext = this.decompilationContext; try { activeMethods.Push((MethodDefinitionHandle)function.Method.MetadataToken); this.context = context; this.decompilationContext = new SimpleTypeResolveContext(function.Method); var cancellationToken = context.CancellationToken; foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); if (!MatchDelegateConstruction(inst, out var targetMethod, out var target, out var delegateType, allowTransformed: false)) continue; context.StepStartGroup($"TransformDelegateConstruction {inst.StartILOffset}", inst); ILFunction f = TransformDelegateConstruction(inst, targetMethod, target, delegateType); if (f != null && target is IInstructionWithVariableOperand instWithVar) { var v = instWithVar.Variable; if (v.Kind == VariableKind.Local) { v.Kind = VariableKind.DisplayClassLocal; } if (v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc store && store.Value is NewObj) { v.CaptureScope = BlockContainer.FindClosestContainer(store); } } context.StepEndGroup(); } } finally { this.context = prevContext; this.decompilationContext = prevDecompilationContext; activeMethods.Pop(); } } internal static bool MatchDelegateConstruction(ILInstruction inst, out IMethod targetMethod, out ILInstruction target, out IType delegateType, bool allowTransformed = false) { targetMethod = null; target = null; delegateType = null; switch (inst) { case NewObj call: if (call.Arguments.Count != 2) return false; target = call.Arguments[0]; var opCode = call.Arguments[1].OpCode; delegateType = call.Method.DeclaringType; if (!(opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction))) return false; targetMethod = ((IInstructionWithMethodOperand)call.Arguments[1]).Method; break; case LdVirtDelegate ldVirtDelegate: target = ldVirtDelegate.Argument; targetMethod = ldVirtDelegate.Method; delegateType = ldVirtDelegate.Type; break; default: return false; } return delegateType.Kind == TypeKind.Delegate || delegateType.Kind == TypeKind.Unknown; } static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) { if (method == null) return false; if (!(method.HasGeneratedName() || method.Name.Contains("$") || method.IsCompilerGenerated() || TransformDisplayClassUsage.IsPotentialClosure( decompiledTypeDefinition, method.DeclaringTypeDefinition) || ContainsAnonymousType(method))) { return false; } return true; } static bool ContainsAnonymousType(IMethod method) { if (method.ReturnType.ContainsAnonymousType()) return true; foreach (var p in method.Parameters) { if (p.Type.ContainsAnonymousType()) return true; } return false; } static GenericContext? GenericContextFromTypeArguments(TypeParameterSubstitution subst) { var classTypeParameters = new List(); var methodTypeParameters = new List(); if (subst.ClassTypeArguments != null) { foreach (var t in subst.ClassTypeArguments) { if (t is ITypeParameter tp) classTypeParameters.Add(tp); else return null; } } if (subst.MethodTypeArguments != null) { foreach (var t in subst.MethodTypeArguments) { if (t is ITypeParameter tp) methodTypeParameters.Add(tp); else return null; } } return new GenericContext(classTypeParameters, methodTypeParameters); } ILFunction TransformDelegateConstruction( ILInstruction value, IMethod targetMethod, ILInstruction target, IType delegateType) { if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) return null; if (targetMethod.MetadataToken.IsNil) return null; if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context)) return null; if (!ValidateDelegateTarget(target)) return null; var handle = (MethodDefinitionHandle)targetMethod.MetadataToken; if (activeMethods.Contains(handle)) { this.context.Function.Warnings.Add(" Found self-referencing delegate construction. Abort transformation to avoid stack overflow."); return null; } var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) return null; var genericContext = GenericContextFromTypeArguments(targetMethod.Substitution); if (genericContext == null) return null; var ilReader = context.CreateILReader(); var body = context.PEFile.GetMethodBody(methodDefinition.RelativeVirtualAddress); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken); function.DelegateType = delegateType; // Embed the lambda into the parent function's ILAst, so that "Show steps" can show // how the lambda body is being transformed. value.ReplaceWith(function); function.CheckInvariant(ILPhase.Normal); var contextPrefix = targetMethod.Name; foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { v.Name = contextPrefix + v.Name; } var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext); nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function); function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(VariableKindExtensions.IsThis))); // handle nested lambdas nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function); ((IILTransform)this).Run(function, nestedContext); nestedContext.StepEndGroup(); function.AddILRange(target); function.AddILRange(value); if (value is Call call) function.AddILRange(call.Arguments[1]); return function; } private static bool ValidateDelegateTarget(ILInstruction inst) { switch (inst) { case LdNull _: return true; case LdLoc ldloc: return ldloc.Variable.IsSingleDefinition; case LdObj ldobj: // TODO : should make sure that the display-class 'this' is unused, // if the delegate target is ldobj(ldsflda field). if (ldobj.Target is LdsFlda) return true; // TODO : ldfld chains must be validated more thoroughly, i.e., we should make sure // that the value of the field is never changed. ILInstruction target = ldobj; while (target is LdObj || target is LdFlda) { if (target is LdObj o) { target = o.Target; continue; } if (target is LdFlda f) { target = f.Target; continue; } } return target is LdLoc; default: return false; } } private IEnumerable GetTransforms() { yield return new CombineExitsTransform(); } /// /// Replaces loads of 'this' with the target expression. /// Async delegates use: ldobj(ldloca this). /// internal class ReplaceDelegateTargetVisitor : ILVisitor { readonly ILVariable thisVariable; readonly ILInstruction target; public ReplaceDelegateTargetVisitor(ILInstruction target, ILVariable thisVariable) { this.target = target; this.thisVariable = thisVariable; } protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) { child.AcceptVisitor(this); } } protected internal override void VisitILFunction(ILFunction function) { if (function == thisVariable?.Function) { ILVariable v = null; switch (target) { case LdLoc l: v = l.Variable; break; case LdObj lo: ILInstruction inner = lo.Target; while (inner is LdFlda ldf) { inner = ldf.Target; } if (inner is LdLoc l2) v = l2.Variable; break; } if (v != null) function.CapturedVariables.Add(v); } base.VisitILFunction(function); } protected internal override void VisitLdLoc(LdLoc inst) { if (inst.Variable == thisVariable) { inst.ReplaceWith(target.Clone()); return; } base.VisitLdLoc(inst); } protected internal override void VisitLdObj(LdObj inst) { if (inst.Target.MatchLdLoca(thisVariable)) { inst.ReplaceWith(target.Clone()); return; } base.VisitLdObj(inst); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs ================================================ // Copyright (c) 2017 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.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class DetectCatchWhenConditionBlocks : IILTransform { public void Run(ILFunction function, ILTransformContext context) { foreach (var catchBlock in function.Descendants.OfType()) { if (catchBlock.Filter is BlockContainer container && MatchCatchWhenEntryPoint(catchBlock.Variable, container, container.EntryPoint, out var exceptionType, out var exceptionSlot, out var whenConditionBlock) && exceptionType.GetStackType() == catchBlock.Variable.StackType) { // set exceptionType catchBlock.Variable.Type = exceptionType; // Block entryPoint (incoming: 1) { // stloc temp(isinst exceptionType(ldloc exceptionVar)) // if (comp(ldloc temp != ldnull)) br whenConditionBlock // br falseBlock // } // => // Block entryPoint (incoming: 1) { // stloc temp(ldloc exceptionSlot) // br whenConditionBlock // } var instructions = container.EntryPoint.Instructions; if (instructions.Count == 3) { // stloc temp(isinst exceptionType(ldloc exceptionVar)) // if (comp(ldloc temp != ldnull)) br whenConditionBlock // br falseBlock context.Step($"Detected catch-when for {catchBlock.Variable.Name} (extra store)", instructions[0]); ((StLoc)instructions[0]).Value = exceptionSlot; instructions[1].ReplaceWith(new Branch(whenConditionBlock)); instructions.RemoveAt(2); container.SortBlocks(deleteUnreachableBlocks: true); } else if (instructions.Count == 2) { // if (comp(isinst exceptionType(ldloc exceptionVar) != ldnull)) br whenConditionBlock // br falseBlock context.Step($"Detected catch-when for {catchBlock.Variable.Name}", instructions[0]); instructions[0].ReplaceWith(new Branch(whenConditionBlock)); instructions.RemoveAt(1); container.SortBlocks(deleteUnreachableBlocks: true); } PropagateExceptionVariable(context, catchBlock); } } } /// /// catch E_189 : 0200007C System.Exception when (BlockContainer { /// Block IL_0079 (incoming: 1) { /// stloc S_30(ldloc E_189) /// br IL_0085 /// } /// /// Block IL_0085 (incoming: 1) { /// stloc I_1(ldloc S_30) /// where S_30 and I_1 are single definition /// => /// copy-propagate E_189 to replace all uses of S_30 and I_1 /// static void PropagateExceptionVariable(ILTransformContext context, TryCatchHandler handler) { var exceptionVariable = handler.Variable; if (!exceptionVariable.IsSingleDefinition) return; context.StepStartGroup(nameof(PropagateExceptionVariable)); int i = 0; while (i < exceptionVariable.LoadInstructions.Count) { var load = exceptionVariable.LoadInstructions[i]; if (!load.IsDescendantOf(handler)) { i++; continue; } // We are only interested in store "statements" copying the exception variable // without modifying it. var statement = Block.GetContainingStatement(load); if (!(statement is StLoc stloc)) { i++; continue; } // simple copy case: // stloc b(ldloc a) if (stloc.Value == load) { PropagateExceptionInstance(stloc); } // if the type of the cast-class instruction matches the exceptionType, // this cast can be removed without losing any side-effects. // Note: this would also hold true iff exceptionType were an exception derived // from cc.Type, however, we are more restrictive to match the pattern exactly. // stloc b(castclass exceptionType(ldloc a)) else if (stloc.Value is CastClass cc && cc.Type.Equals(exceptionVariable.Type) && cc.Argument == load) { stloc.Value = load; PropagateExceptionInstance(stloc); } else { i++; } void PropagateExceptionInstance(StLoc store) { foreach (var load in store.Variable.LoadInstructions.ToArray()) { if (!load.IsDescendantOf(handler)) continue; load.ReplaceWith(new LdLoc(exceptionVariable).WithILRange(load)); } if (store.Variable.LoadCount == 0 && store.Parent is Block block) { block.Instructions.RemoveAt(store.ChildIndex); } else { i++; } } } context.StepEndGroup(keepIfEmpty: false); } /// /// Block entryPoint (incoming: 1) { /// stloc temp(isinst exceptionType(ldloc exceptionVar)) /// if (comp(ldloc temp != ldnull)) br whenConditionBlock /// br falseBlock /// } /// bool MatchCatchWhenEntryPoint(ILVariable exceptionVar, BlockContainer container, Block entryPoint, out IType exceptionType, out ILInstruction exceptionSlot, out Block whenConditionBlock) { exceptionType = null; exceptionSlot = null; whenConditionBlock = null; if (entryPoint == null || entryPoint.IncomingEdgeCount != 1) return false; if (entryPoint.Instructions.Count == 3) { // stloc temp(isinst exceptionType(ldloc exceptionVar)) // if (comp(ldloc temp != ldnull)) br whenConditionBlock // br falseBlock if (!entryPoint.Instructions[0].MatchStLoc(out var temp, out var isinst) || temp.Kind != VariableKind.StackSlot || !isinst.MatchIsInst(out exceptionSlot, out exceptionType)) return false; if (!exceptionSlot.MatchLdLoc(exceptionVar)) return false; if (!entryPoint.Instructions[1].MatchIfInstruction(out var condition, out var branch)) return false; if (!condition.MatchCompNotEquals(out var left, out var right)) return false; if (!entryPoint.Instructions[2].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock)) return false; if ((left.MatchLdNull() && right.MatchLdLoc(temp)) || (right.MatchLdNull() && left.MatchLdLoc(temp))) { return branch.MatchBranch(out whenConditionBlock); } } else if (entryPoint.Instructions.Count == 2) { // if (comp(isinst exceptionType(ldloc exceptionVar) != ldnull)) br whenConditionBlock // br falseBlock if (!entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branch)) return false; if (!condition.MatchCompNotEquals(out var left, out var right)) return false; if (!entryPoint.Instructions[1].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock)) return false; if (!left.MatchIsInst(out exceptionSlot, out exceptionType)) return false; if (!exceptionSlot.MatchLdLoc(exceptionVar)) return false; if (right.MatchLdNull()) { return branch.MatchBranch(out whenConditionBlock); } } return false; } /// /// Block falseBlock (incoming: 1) { /// stloc returnVar(ldc.i4 0) /// br exitBlock /// } /// bool MatchFalseBlock(BlockContainer container, Block falseBlock, out ILVariable returnVar, out Block exitBlock) { returnVar = null; exitBlock = null; if (falseBlock.IncomingEdgeCount != 1 || falseBlock.Instructions.Count != 2) return false; return falseBlock.Instructions[0].MatchStLoc(out returnVar, out var zero) && zero.MatchLdcI4(0) && falseBlock.Instructions[1].MatchBranch(out exitBlock) && MatchExitBlock(container, exitBlock, returnVar); } /// /// Block exitBlock(incoming: 2) { /// leave container(ldloc returnVar) /// } /// bool MatchExitBlock(BlockContainer container, Block exitBlock, ILVariable returnVar) { if (exitBlock.IncomingEdgeCount != 2 || exitBlock.Instructions.Count != 1) return false; return exitBlock.Instructions[0].MatchLeave(container, out var value) && value.MatchLdLoc(returnVar); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs ================================================ // 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 { /// /// Transforms the "callsite initialization pattern" into DynamicInstructions. /// 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 callsites = new Dictionary(); HashSet modifiedContainers = new HashSet(); foreach (var block in function.Descendants.OfType()) { 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(); foreach (var invokeCall in function.Descendants.OfType()) { 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 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 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: var arguments = targetInvokeCall.Arguments.Skip(2).ToArray(); // Extract type information from targetInvokeCall: // Must either be an inlined type or // a reference to a variable that is initialized with a type. if (!TransformExpressionTrees.MatchGetTypeFromHandle(arguments[0], out var type)) { if (!(arguments[0].MatchLdLoc(out var temp) && temp.IsSingleDefinition && temp.StoreInstructions.FirstOrDefault() is StLoc initStore)) return null; if (!TransformExpressionTrees.MatchGetTypeFromHandle(initStore.Value, out type)) return null; } deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); return new DynamicInvokeConstructorInstruction( binderFlags: callsite.Flags, type: type ?? SpecialType.UnknownType, context: callsite.Context, argumentInfo: callsite.ArgumentInfos, arguments: arguments ); 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 ArgumentOutOfRangeException($"Value {callsite.Kind} is not supported!"); } } 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(context.Function, callSiteInitBlock, 3, variableOrTemporary, new[] { 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 ArgumentOutOfRangeException(); } 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(context.Function, callSiteInfo.InitBlock, instructionOffset, variable, new[] { numberOfArguments }, out var arguments, out _)) return false; int i = 0; callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; IMethod invokeMethod = callSiteInfo.DelegateType.GetDelegateInvokeMethod(); if (invokeMethod == null) return false; var compileTimeTypes = invokeMethod.Parameters.SelectReadOnlyArray(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; if (!createCall.Arguments[1].MatchLdStr(out string 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 } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs ================================================ // 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 // 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.Linq; namespace ICSharpCode.Decompiler.IL.Transforms { public class DynamicIsEventAssignmentTransform : IStatementTransform { /// stloc V_1(dynamic.isevent (target)) /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) /// } /// [stloc copyOfValue(value)] /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } /// => /// if (logic.not(dynamic.isevent (target))) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(dynamic.getmember B(target), value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } public void Run(Block block, int pos, StatementTransformContext context) { if (!(pos + 3 < block.Instructions.Count && block.Instructions[pos].MatchStLoc(out var flagVar, out var inst) && inst is DynamicIsEventInstruction isEvent)) return; if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2)) return; if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore)) return; if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst)) return; int offset = 2; if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) && pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 && valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction)) { offset++; } foreach (var descendant in block.Instructions[pos + offset].Descendants) { if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse)) continue; context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]); // Collapse duplicate condition getMemberVarUse.ReplaceWith(getMemberInst); isEventConditionUse.ReplaceWith(isEvent); block.Instructions.RemoveRange(pos, 2); // Reuse ExpressionTransforms ExpressionTransforms.TransformDynamicSetMemberInstruction(setMemberInst, context); context.RequestRerun(); break; } } /// /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } /// static bool MatchIsEventAssignmentIfInstruction(ILInstruction ifInst, DynamicIsEventInstruction isEvent, ILVariable flagVar, ILVariable getMemberVar, out DynamicSetMemberInstruction setMemberInst, out ILInstruction getMemberVarUse, out ILInstruction isEventConditionUse) { setMemberInst = null; getMemberVarUse = null; isEventConditionUse = null; if (!ifInst.MatchIfInstruction(out var condition, out var trueInst, out var falseInst)) return false; if (MatchFlagEqualsZero(condition, flagVar)) { if (!condition.MatchCompEquals(out var left, out _)) return false; isEventConditionUse = left; } else if (condition.MatchLdLoc(flagVar)) { var tmp = trueInst; trueInst = falseInst; falseInst = tmp; isEventConditionUse = condition; } else return false; setMemberInst = Block.Unwrap(trueInst) as DynamicSetMemberInstruction; if (setMemberInst == null) return false; if (!isEvent.Argument.Match(setMemberInst.Target).Success) return false; if (!(Block.Unwrap(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) return false; if (!isEvent.Argument.Match(invokeMemberInst.Arguments[0]).Success) return false; if (!(setMemberInst.Value is DynamicBinaryOperatorInstruction binOp && binOp.Left.MatchLdLoc(getMemberVar))) return false; getMemberVarUse = binOp.Left; return true; } /// /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) /// } /// static bool MatchLhsCacheIfInstruction(ILInstruction ifInst, ILVariable flagVar, out StLoc cacheStore) { cacheStore = null; if (!ifInst.MatchIfInstruction(out var condition, out var trueInst)) return false; if (!MatchFlagEqualsZero(condition, flagVar)) return false; cacheStore = Block.Unwrap(trueInst) as StLoc; return cacheStore != null; } static bool MatchFlagEqualsZero(ILInstruction condition, ILVariable flagVar) { return condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(flagVar) && right.MatchLdcI4(0); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class EarlyExpressionTransforms : ILVisitor, IILTransform { ILTransformContext context; public void Run(ILFunction function, ILTransformContext context) { this.context = context; Default(function); } protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) { child.AcceptVisitor(this); } } protected internal override void VisitComp(Comp inst) { base.VisitComp(inst); FixComparisonKindLdNull(inst, context); } internal static void FixComparisonKindLdNull(Comp inst, ILTransformContext context) { if (inst.IsLifted) { return; } if (inst.Right.MatchLdNull()) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp(left > ldnull) => comp(left != ldnull)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst); inst.Kind = ComparisonKind.Equality; } } else if (inst.Left.MatchLdNull()) { if (inst.Kind == ComparisonKind.LessThan) { context.Step("comp(ldnull < right) => comp(ldnull != right)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.GreaterThanOrEqual) { context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst); inst.Kind = ComparisonKind.Equality; } } if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out var arg, out var type) && type.Kind == TypeKind.TypeParameter) { if (inst.Kind == ComparisonKind.Equality) { context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst); inst.Left = arg; } if (inst.Kind == ComparisonKind.Inequality) { context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst); inst.Left = arg; } } } protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); StObjToStLoc(inst, context); } // This transform is required because ILInlining only works with stloc/ldloc internal static bool StObjToStLoc(StObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst); ILInstruction replacement = new StLoc(v, inst.Value).WithILRange(inst); if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown && inst.SlotInfo != Block.InstructionSlot) { replacement = new Conv(replacement, inst.Type.ToPrimitiveType(), checkForOverflow: false, Sign.None); } inst.ReplaceWith(replacement); return true; } return false; } protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); AddressOfLdLocToLdLoca(inst, context); LdObjToLdLoc(inst, context); } internal static bool LdObjToLdLoc(LdObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { context.Step($"ldobj(ldloca {v.Name}) => ldloc {v.Name}", inst); ILInstruction replacement = new LdLoc(v).WithILRange(inst); if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown) { replacement = new Conv(replacement, inst.Type.ToPrimitiveType(), checkForOverflow: false, Sign.None); } inst.ReplaceWith(replacement); return true; } return false; } internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context) { // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions // => // ldobj(...(ldloca V)) var temp = inst.Target; var range = temp.ILRanges; while (temp.MatchLdFlda(out var ldfldaTarget, out _)) { temp = ldfldaTarget; range = range.Concat(temp.ILRanges); } if (temp.MatchAddressOf(out var addressOfTarget, out _) && addressOfTarget.MatchLdLoc(out var v)) { context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst); var replacement = new LdLoca(v).WithILRange(addressOfTarget); foreach (var r in range) { replacement = replacement.WithILRange(r); } temp.ReplaceWith(replacement); } } protected internal override void VisitNewObj(NewObj inst) { if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant)) { context.Step("TransformDecimalCtorToConstant", inst); inst.ReplaceWith(decimalConstant); return; } base.VisitNewObj(inst); } bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result) { IType t = inst.Method.DeclaringType; result = null; if (!t.IsKnownType(KnownTypeCode.Decimal)) return false; var args = inst.Arguments; if (args.Count == 1) { long val; if (args[0].MatchLdcI(out val)) { var paramType = inst.Method.Parameters[0].Type.GetDefinition()?.KnownTypeCode; result = paramType switch { KnownTypeCode.Int32 => new LdcDecimal(new decimal(unchecked((int)val))), KnownTypeCode.UInt32 => new LdcDecimal(new decimal(unchecked((uint)val))), KnownTypeCode.Int64 => new LdcDecimal(new decimal(val)), KnownTypeCode.UInt64 => new LdcDecimal(new decimal(unchecked((ulong)val))), _ => null }; return result is not null; } } else if (args.Count == 5) { int lo, mid, hi, isNegative, scale; if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) && args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) && args[4].MatchLdcI4(out scale) && unchecked((byte)scale) <= 28) { result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, unchecked((byte)scale))); return true; } } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs ================================================ // Copyright (c) 2014-2017 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Collection of transforms that detect simple expression patterns /// (e.g. 'cgt.un(..., ld.null)') and replace them with different instructions. /// /// /// Should run after inlining so that the expression patterns can be detected. /// public class ExpressionTransforms : ILVisitor, IStatementTransform { internal StatementTransformContext context; public static void RunOnSingleStatement(ILInstruction statement, ILTransformContext context) { if (statement == null) throw new ArgumentNullException(nameof(statement)); if (!(statement.Parent is Block parent)) throw new ArgumentException("ILInstruction must be a statement, i.e., direct child of a block."); new ExpressionTransforms().Run(parent, statement.ChildIndex, new StatementTransformContext(new BlockTransformContext(context))); } public void Run(Block block, int pos, StatementTransformContext context) { this.context = context; context.StepStartGroup($"ExpressionTransforms ({block.Label}:{pos})", block.Instructions[pos]); block.Instructions[pos].AcceptVisitor(this); context.StepEndGroup(keepIfEmpty: true); } protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) { child.AcceptVisitor(this); } } protected internal override void VisitBlockContainer(BlockContainer container) { if (container.Kind == ContainerKind.Switch) { // Special case for switch: Only visit the switch condition block. var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0]; switchInst.Value.AcceptVisitor(this); HandleSwitchExpression(container, switchInst); } // No need to call base.VisitBlockContainer, see comment in VisitBlock. } protected internal override void VisitBlock(Block block) { if (block.Kind == BlockKind.ControlFlow) { // Don't visit child control flow blocks; // since this is a block transform // we know those were already handled previously. return; } base.VisitBlock(block); } protected internal override void VisitComp(Comp inst) { // "logic.not(arg)" is sugar for "comp(arg != ldc.i4 0)" if (inst.MatchLogicNot(out var arg)) { VisitLogicNot(inst, arg); return; } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp)) { // if (comp(x != 0)) ==> if (x) // comp(comp(...) != 0) => comp(...) context.Step("Remove redundant comp(... != 0)", inst); inst.Left.AddILRange(inst); inst.ReplaceWith(inst.Left); inst.Left.AcceptVisitor(this); return; } if (context.Settings.LiftNullables) { new NullableLiftingTransform(context).Run(inst); } base.VisitComp(inst); if (inst.IsLifted) { return; } EarlyExpressionTransforms.FixComparisonKindLdNull(inst, context); var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend); if (rightWithoutConv.MatchLdcI4(0) && inst.Sign == Sign.Unsigned && (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); inst.Kind = ComparisonKind.Inequality; VisitComp(inst); return; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst); inst.Kind = ComparisonKind.Equality; VisitComp(inst); return; } } else if (rightWithoutConv.MatchLdcI4(0) && inst.Kind.IsEqualityOrInequality()) { if (inst.Left.MatchLdLen(StackType.I, out ILInstruction array)) { // comp.unsigned(ldlen array == conv i4->i(ldc.i4 0)) // => comp(ldlen.i4 array == ldc.i4 0) // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen. context.Step("comp(ldlen.i4 array == ldc.i4 0)", inst); inst.InputType = StackType.I4; inst.Left.ReplaceWith(new LdLen(StackType.I4, array).WithILRange(inst.Left)); inst.Right = rightWithoutConv; } else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.O) { // C++/CLI sometimes uses this weird comparison with null: context.Step("comp(conv o->i (ldloc obj) == conv i4->i (ldc.i4 0))", inst); // -> comp(ldloc obj == ldnull) inst.InputType = StackType.O; inst.Left = conv.Argument; inst.Right = new LdNull().WithILRange(inst.Right); inst.Right.AddILRange(rightWithoutConv); } } } protected internal override void VisitConv(Conv inst) { inst.Argument.AcceptVisitor(this); if (inst.Argument.MatchLdLen(StackType.I, out ILInstruction array) && inst.TargetType.IsIntegerType() && (!inst.CheckForOverflow || context.Settings.AssumeArrayLengthFitsIntoInt32)) { context.Step("conv.i4(ldlen array) => ldlen.i4(array)", inst); inst.AddILRange(inst.Argument); inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array).WithILRange(inst)); return; } if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv && conv.Kind == ConversionKind.IntToFloat && conv.TargetType == PrimitiveType.R) { // IL conv.r.un does not indicate whether to convert the target type to R4 or R8, // so the C# compiler usually follows it with an explicit conv.r4 or conv.r8. // To avoid emitting '(float)(double)val', we combine these two conversions: context.Step("conv.rN(conv.r.un(...)) => conv.rN.un(...)", inst); inst.ReplaceWith(new Conv(conv.Argument, conv.InputType, conv.InputSign, inst.TargetType, inst.CheckForOverflow, inst.IsLifted | conv.IsLifted)); return; } } protected internal override void VisitBox(Box inst) { inst.Argument.AcceptVisitor(this); if (inst.Type.IsReferenceType == true && inst.Argument.ResultType == inst.ResultType) { // For reference types, box is a no-op. context.Step("box ref-type(arg) => arg", inst); inst.Argument.AddILRange(inst); inst.ReplaceWith(inst.Argument); } } protected internal override void VisitLdElema(LdElema inst) { base.VisitLdElema(inst); CleanUpArrayIndices(inst.Indices); if (IndexRangeTransform.HandleLdElema(inst, context)) return; } protected internal override void VisitNewArr(NewArr inst) { base.VisitNewArr(inst); CleanUpArrayIndices(inst.Indices); } void CleanUpArrayIndices(InstructionCollection indices) { foreach (ILInstruction index in indices) { if (index is Conv conv && conv.ResultType == StackType.I && (conv.Kind == ConversionKind.Truncate && conv.CheckForOverflow || conv.Kind == ConversionKind.ZeroExtend || conv.Kind == ConversionKind.SignExtend) ) { context.Step("Remove conv.i from array index", index); index.ReplaceWith(conv.Argument); } } } void VisitLogicNot(Comp inst, ILInstruction arg) { ILInstruction lhs, rhs; if (arg is Comp comp) { if ((!comp.InputType.IsFloatType() && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { context.Step("push negation into comparison", inst); comp.Kind = comp.Kind.Negate(); comp.AddILRange(inst); inst.ReplaceWith(comp); } comp.AcceptVisitor(this); } else if (arg.MatchLogicAnd(out lhs, out rhs)) { // logic.not(if (lhs) rhs else ldc.i4 0) // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs) context.Step("push negation into logic.and", inst); IfInstruction ifInst = (IfInstruction)arg; var ldc0 = ifInst.FalseInst; Debug.Assert(ldc0.MatchLdcI4(0)); ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst); ifInst.TrueInst = new LdcI4(1).WithILRange(ldc0); ifInst.FalseInst = Comp.LogicNot(rhs).WithILRange(inst); inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else if (arg.MatchLogicOr(out lhs, out rhs)) { // logic.not(if (lhs) ldc.i4 1 else rhs) // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0) context.Step("push negation into logic.or", inst); IfInstruction ifInst = (IfInstruction)arg; var ldc1 = ifInst.TrueInst; Debug.Assert(ldc1.MatchLdcI4(1)); ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst); ifInst.TrueInst = Comp.LogicNot(rhs).WithILRange(inst); ifInst.FalseInst = new LdcI4(0).WithILRange(ldc1); inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else { arg.AcceptVisitor(this); } } protected internal override void VisitCall(Call inst) { if (NullableLiftingTransform.MatchGetValueOrDefault(inst, out var nullableValue, out var fallback) && SemanticHelper.IsPure(fallback.Flags)) { context.Step("call Nullable{T}.GetValueOrDefault(a, b) -> a ?? b", inst); var ldObj = new LdObj(nullableValue, inst.Method.DeclaringType); var replacement = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, ldObj, fallback) { UnderlyingResultType = fallback.ResultType }; inst.ReplaceWith(replacement.WithILRange(inst)); replacement.AcceptVisitor(this); return; } if (TransformArrayInitializers.TransformRuntimeHelpersCreateSpanInitialization(inst, context, out var replacement2)) { context.Step("TransformRuntimeHelpersCreateSpanInitialization: single-dim", inst); inst.ReplaceWith(replacement2); replacement2.AcceptVisitor(this); return; } base.VisitCall(inst); if (context.Settings.InlineArrays && InlineArrayTransform.RunOnExpression(inst, context)) { return; } TransformAssignment.HandleCompoundAssign(inst, context); } protected internal override void VisitCallVirt(CallVirt inst) { base.VisitCallVirt(inst); TransformAssignment.HandleCompoundAssign(inst, context); } protected internal override void VisitNewObj(NewObj inst) { if (TransformSpanTCtorContainingStackAlloc(inst, out ILInstruction locallocSpan)) { context.Step("new Span(stackalloc) -> stackalloc Span", inst); inst.ReplaceWith(locallocSpan); ILInstruction stmt = Block.GetContainingStatement(locallocSpan); // Special case to eliminate extra store if (stmt.GetNextSibling() is StLoc storeStmt && storeStmt.Value is LdLoc) ILInlining.InlineIfPossible((Block)stmt.Parent, stmt.ChildIndex, context); return; } if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out var replacement)) { context.Step("TransformSpanTArrayInitialization: single-dim", inst); inst.ReplaceWith(replacement); return; } if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) { context.Step("new Delegate(target, ldvirtftn Method) -> ldvirtdelegate Delegate Method(target)", inst); inst.ReplaceWith(ldVirtDelegate); return; } base.VisitNewObj(inst); } /// /// newobj Delegate..ctor(target, ldvirtftn TargetMethod(target)) /// => /// ldvirtdelegate System.Delegate TargetMethod(target) /// bool TransformDelegateCtorLdVirtFtnToLdVirtDelegate(NewObj inst, out LdVirtDelegate ldVirtDelegate) { ldVirtDelegate = null; if (inst.Method.DeclaringType.Kind != TypeKind.Delegate) return false; if (inst.Arguments.Count != 2) return false; if (!(inst.Arguments[1] is LdVirtFtn ldVirtFtn)) return false; if (!SemanticHelper.IsPure(inst.Arguments[0].Flags)) return false; if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) return false; ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method) .WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument); return true; } /// /// newobj Span..ctor(localloc(conv i4->u <zero extend>(ldc.i4 sizeInBytes)), numberOfElementsExpr) /// => /// localloc.span T(numberOfElementsExpr) /// /// -or- /// /// newobj Span..ctor(Block IL_0000 (StackAllocInitializer) { /// stloc I_0(localloc(conv i4->u<zero extend>(ldc.i4 sizeInBytes))) /// ... /// final: ldloc I_0 /// }, numberOfElementsExpr) /// => /// Block IL_0000 (StackAllocInitializer) { /// stloc I_0(localloc.span T(numberOfElementsExpr)) /// ... /// final: ldloc I_0 /// } /// bool TransformSpanTCtorContainingStackAlloc(NewObj newObj, out ILInstruction locallocSpan) { locallocSpan = null; IType type = newObj.Method.DeclaringType; if (!type.IsKnownType(KnownTypeCode.SpanOfT) && !type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) return false; if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1) return false; IType elementType = type.TypeArguments[0]; if (newObj.Arguments[0].MatchLocAlloc(out var sizeInBytes) && MatchesElementCount(sizeInBytes, elementType, newObj.Arguments[1])) { locallocSpan = new LocAllocSpan(newObj.Arguments[1], type); return true; } if (newObj.Arguments[0] is Block initializer && initializer.Kind == BlockKind.StackAllocInitializer) { if (!initializer.Instructions[0].MatchStLoc(out var initializerVariable, out var value)) return false; if (!(value.MatchLocAlloc(out sizeInBytes) && MatchesElementCount(sizeInBytes, elementType, newObj.Arguments[1]))) return false; var newVariable = initializerVariable.Function.RegisterVariable(VariableKind.InitializerTarget, type); foreach (var load in initializerVariable.LoadInstructions.ToArray()) { ILInstruction newInst = new LdLoc(newVariable); newInst.AddILRange(load); if (load.Parent != initializer) newInst = new Conv(newInst, PrimitiveType.I, false, Sign.None); load.ReplaceWith(newInst); } foreach (var store in initializerVariable.StoreInstructions.ToArray()) { store.Variable = newVariable; } value.ReplaceWith(new LocAllocSpan(newObj.Arguments[1], type)); locallocSpan = initializer; return true; } return false; } bool MatchesElementCount(ILInstruction sizeInBytesInstr, IType elementType, ILInstruction elementCountInstr2) { var pointerType = new PointerType(elementType); var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType.ElementType, checkForOverflow: true, unwrapZeroExtension: true); if (elementCountInstr == null || !elementCountInstr.UnwrapConv(ConversionKind.ZeroExtend).Match(elementCountInstr2).Success) return false; return true; } bool TransformDecimalFieldToConstant(LdObj inst, out LdcDecimal result) { if (inst.MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { decimal? value = null; if (field.Name == "One") { value = decimal.One; } else if (field.Name == "MinusOne") { value = decimal.MinusOne; } else if (field.Name == "Zero") { value = decimal.Zero; } if (value != null) { result = new LdcDecimal(value.Value).WithILRange(inst).WithILRange(inst.Target); return true; } } result = null; return false; } protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context); if (EarlyExpressionTransforms.LdObjToLdLoc(inst, context)) return; if (TransformDecimalFieldToConstant(inst, out LdcDecimal decimalConstant)) { context.Step("TransformDecimalFieldToConstant", inst); inst.ReplaceWith(decimalConstant); return; } } protected internal override void VisitLdObjIfRef(LdObjIfRef inst) { base.VisitLdObjIfRef(inst); if (inst.Target is AddressOf) { context.Step("ldobj.if.ref(addressof(...)) -> addressof(...)", inst); // there already is a temporary, so the ldobj.if.ref is a no-op in both cases inst.ReplaceWith(inst.Target); return; } if (inst.Target.MatchLdLoc(out var s) && s.IsSingleDefinition && s.LoadCount == 1 && s.StoreInstructions.SingleOrDefault() is StLoc { Value: LdLoca { Variable: { AddressCount: 1, StoreCount: 1 } } }) { context.Step("Single use of ldobj.if.ref(ldloc v) -> ldloc v", inst); // there already is a temporary, so the ldobj.if.ref is a no-op in both cases inst.ReplaceWith(inst.Target); return; } } protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) { context.RequestRerun(); return; } TransformAssignment.HandleCompoundAssign(inst, context); } protected internal override void VisitStLoc(StLoc inst) { base.VisitStLoc(inst); TransformAssignment.HandleCompoundAssign(inst, context); } protected internal override void VisitIfInstruction(IfInstruction inst) { inst.TrueInst.AcceptVisitor(this); inst.FalseInst.AcceptVisitor(this); inst = HandleConditionalOperator(inst); // Bring LogicAnd/LogicOr into their canonical forms: // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0 // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS // Be careful: when both LHS and RHS are the constant 1, we must not // swap the arguments as it would lead to an infinite transform loop. if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) || inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1)) { context.Step("canonicalize logic and/or", inst); var t = inst.TrueInst; inst.TrueInst = inst.FalseInst; inst.FalseInst = t; inst.Condition = Comp.LogicNot(inst.Condition); } // Process condition after our potential modifications. inst.Condition.AcceptVisitor(this); if (new NullableLiftingTransform(context).Run(inst)) { context.Step("NullableLiftingTransform", inst); return; } if (TransformDynamicAddAssignOrRemoveAssign(inst)) return; if (inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) { ILInstruction transformed = UserDefinedLogicTransform.Transform(condition, trueInst, falseInst); if (transformed == null) { transformed = UserDefinedLogicTransform.TransformDynamic(condition, trueInst, falseInst); } if (transformed != null) { context.Step("User-defined short-circuiting logic operator (roslyn pattern)", condition); transformed.AddILRange(inst); inst.ReplaceWith(transformed); return; } } if (MatchInstruction.IsPatternMatch(inst.Condition, out _, context.Settings) && inst.TrueInst.MatchLdcI4(1) && inst.FalseInst.MatchLdcI4(0)) { context.Step("match(x) ? true : false -> match(x)", inst); inst.Condition.AddILRange(inst); inst.ReplaceWith(inst.Condition); return; } } IfInstruction HandleConditionalOperator(IfInstruction inst) { // if (cond) stloc A(V1) else stloc A(V2) --> stloc A(if (cond) V1 else V2) Block trueInst = inst.TrueInst as Block; if (trueInst == null || trueInst.Instructions.Count != 1) return inst; Block falseInst = inst.FalseInst as Block; if (falseInst == null || falseInst.Instructions.Count != 1) return inst; ILVariable v; ILInstruction value1, value2; if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) { context.Step("conditional operator", inst); var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); newIf.AddILRange(inst); inst.ReplaceWith(new StLoc(v, newIf)); context.RequestRerun(); // trigger potential inlining of the newly created StLoc return newIf; } return inst; } private void HandleSwitchExpression(BlockContainer container, SwitchInstruction switchInst) { if (!context.Settings.SwitchExpressions) return; Debug.Assert(container.Kind == ContainerKind.Switch); Debug.Assert(container.ResultType == StackType.Void); var defaultSection = switchInst.GetDefaultSection(); StackType resultType = StackType.Void; BlockContainer leaveTarget = null; ILVariable resultVariable = null; foreach (var section in switchInst.Sections) { if (section != defaultSection) { // every section except for the default must have exactly 1 label if (section.Labels.Count() != (section.HasNullLabel ? 0u : 1u)) return; } if (!section.Body.MatchBranch(out var sectionBlock)) return; if (sectionBlock.IncomingEdgeCount != 1) return; if (sectionBlock.Parent != container) return; if (sectionBlock.Instructions.Count == 1) { if (sectionBlock.Instructions[0] is Throw) { // OK } else if (sectionBlock.Instructions[0] is Leave leave) { if (!leave.IsLeavingFunction) return; leaveTarget ??= leave.TargetContainer; Debug.Assert(leaveTarget == leave.TargetContainer); resultType = leave.Value.ResultType; } else { return; } } else if (sectionBlock.Instructions.Count == 2) { if (!sectionBlock.Instructions[0].MatchStLoc(out var v, out _)) return; if (!sectionBlock.Instructions[1].MatchLeave(container)) return; resultVariable ??= v; if (resultVariable != v) return; resultType = resultVariable.StackType; } else { return; } } // Exactly one of resultVariable/leaveTarget must be null if ((resultVariable == null) == (leaveTarget == null)) return; if (switchInst.Value is StringToInt str2int) { // validate that each integer is used for exactly one value var integersUsed = new HashSet(); foreach ((string key, int val) in str2int.Map) { if (!integersUsed.Add(val)) return; } } context.Step("Switch Expression", switchInst); switchInst.SetResultType(resultType); foreach (var section in switchInst.Sections) { var block = ((Branch)section.Body).TargetBlock; if (block.Instructions.Count == 1) { if (block.Instructions[0] is Throw t) { t.resultType = resultType; section.Body = t; } else if (block.Instructions[0] is Leave leave) { section.Body = leave.Value; } else { throw new InvalidOperationException(); } } else { section.Body = ((StLoc)block.Instructions[0]).Value; } } if (resultVariable != null) { container.ReplaceWith(new StLoc(resultVariable, switchInst)); } else { container.ReplaceWith(new Leave(leaveTarget, switchInst)); } context.RequestRerun(); // new StLoc might trigger inlining } /// /// op is either add or remove/subtract: /// if (dynamic.isevent (target)) { /// dynamic.invokemember.invokespecial.discard op_Name(target, value) /// } else { /// dynamic.compound.op (dynamic.getmember Name(target), value) /// } /// => /// dynamic.compound.op (dynamic.getmember Name(target), value) /// bool TransformDynamicAddAssignOrRemoveAssign(IfInstruction inst) { if (!inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) return false; if (!(condition is DynamicIsEventInstruction isEvent)) return false; trueInst = Block.Unwrap(trueInst); falseInst = Block.Unwrap(falseInst); if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign)) return false; if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) return false; if (!SemanticHelper.IsPure(isEvent.Argument.Flags)) return false; if (!isEvent.Argument.Match(getMember.Target).Success) return false; if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) return false; if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) return false; switch (dynamicCompoundAssign.Operation) { case ExpressionType.AddAssign: if (invokeMember.Name != "add_" + getMember.Name) return false; break; case ExpressionType.SubtractAssign: if (invokeMember.Name != "remove_" + getMember.Name) return false; break; default: return false; } if (!dynamicCompoundAssign.Value.Match(invokeMember.Arguments[1]).Success) return false; if (!invokeMember.Arguments[0].Match(getMember.Target).Success) return false; context.Step("+= / -= dynamic.isevent pattern -> dynamic.compound.op", inst); inst.ReplaceWith(dynamicCompoundAssign); return true; } /// /// dynamic.setmember.compound Name(target, dynamic.binary.operator op(dynamic.getmember Name(target), value)) /// => /// dynamic.compound.op (dynamic.getmember Name(target), value) /// protected internal override void VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst) { base.VisitDynamicSetMemberInstruction(inst); TransformDynamicSetMemberInstruction(inst, context); } internal static void TransformDynamicSetMemberInstruction(DynamicSetMemberInstruction inst, StatementTransformContext context) { if (!inst.BinderFlags.HasFlag(CSharpBinderFlags.ValueFromCompoundAssignment)) return; if (!(inst.Value is DynamicBinaryOperatorInstruction binaryOp)) return; if (!(binaryOp.Left is DynamicGetMemberInstruction dynamicGetMember)) return; if (!dynamicGetMember.Target.Match(inst.Target).Success) return; if (!SemanticHelper.IsPure(dynamicGetMember.Target.Flags)) return; if (inst.Name != dynamicGetMember.Name || !DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation)) return; context.Step("dynamic.setmember.compound -> dynamic.compound.op", inst); inst.ReplaceWith(new DynamicCompoundAssign(binaryOp.Operation, binaryOp.BinderFlags, binaryOp.Left, binaryOp.LeftArgumentInfo, binaryOp.Right, binaryOp.RightArgumentInfo)); } /// /// dynamic.setindex.compound(target, index, dynamic.binary.operator op(dynamic.getindex(target, index), value)) /// => /// dynamic.compound.op (dynamic.getindex(target, index), value) /// protected internal override void VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst) { base.VisitDynamicSetIndexInstruction(inst); if (!inst.BinderFlags.HasFlag(CSharpBinderFlags.ValueFromCompoundAssignment)) return; if (!(inst.Arguments.LastOrDefault() is DynamicBinaryOperatorInstruction binaryOp)) return; if (!(binaryOp.Left is DynamicGetIndexInstruction dynamicGetIndex)) return; if (inst.Arguments.Count != dynamicGetIndex.Arguments.Count + 1) return; // Ensure that same arguments are passed to dynamicGetIndex and inst: for (int j = 0; j < dynamicGetIndex.Arguments.Count; j++) { if (!SemanticHelper.IsPure(dynamicGetIndex.Arguments[j].Flags)) return; if (!dynamicGetIndex.Arguments[j].Match(inst.Arguments[j]).Success) return; } if (!DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation)) return; context.Step("dynamic.setindex.compound -> dynamic.compound.op", inst); inst.ReplaceWith(new DynamicCompoundAssign(binaryOp.Operation, binaryOp.BinderFlags, binaryOp.Left, binaryOp.LeftArgumentInfo, binaryOp.Right, binaryOp.RightArgumentInfo)); } protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { base.VisitBinaryNumericInstruction(inst); switch (inst.Operator) { case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftRight: if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) && MatchExpectedShiftSize(rhs)) { // a << (b & 31) => a << b context.Step("Combine bit.and into shift", inst); inst.Right = lhs; } break; case BinaryNumericOperator.BitAnd: if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) && inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)) { if (new NullableLiftingTransform(context).Run(inst)) { // e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" } } break; } bool MatchExpectedShiftSize(ILInstruction rhs) { switch (inst.ResultType) { case StackType.I4: return rhs.MatchLdcI4(31); case StackType.I8: return rhs.MatchLdcI4(63); case StackType.I: // sizeof(IntPtr) * 8 - 1 return rhs.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub, out var mult, out var one) && mult.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out var size, out var eight) && size.MatchSizeOf(out var sizeofType) && sizeofType.GetStackType() == StackType.I && eight.MatchLdcI4(8) && one.MatchLdcI4(1); default: return false; } } } protected internal override void VisitTryCatchHandler(TryCatchHandler inst) { base.VisitTryCatchHandler(inst); if (inst.Filter is BlockContainer filterContainer && filterContainer.Blocks.Count == 1) { TransformCatchWhen(inst, filterContainer.EntryPoint); } if (inst.Body is BlockContainer catchContainer) TransformCatchVariable(inst, catchContainer.EntryPoint, isCatchBlock: true); } /// /// catch ex : TException when (...) BlockContainer { /// Block entryPoint (incoming: 1) { /// stloc v(ldloc ex) /// ... /// } /// } /// => /// catch v : TException when (...) BlockContainer { /// Block entryPoint (incoming: 1) { /// ... /// } /// } /// void TransformCatchVariable(TryCatchHandler handler, Block entryPoint, bool isCatchBlock) { if (!handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) return; // handler.Variable already has non-trivial uses if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) { // Not the pattern with a second exceptionVar. // However, it is still possible that we need to remove a pointless UnboxAny: if (handler.Variable.LoadInstructions.Single().Parent is UnboxAny inlinedUnboxAny) { if (inlinedUnboxAny.Type.Equals(handler.Variable.Type)) { context.Step("TransformCatchVariable - remove inlined UnboxAny", inlinedUnboxAny); inlinedUnboxAny.ReplaceWith(inlinedUnboxAny.Argument); foreach (var range in inlinedUnboxAny.ILRanges) handler.AddExceptionSpecifierILRange(range); } } return; } if (exceptionVar.Kind != VariableKind.Local && exceptionVar.Kind != VariableKind.StackSlot) return; if (exceptionSlotLoad is UnboxAny unboxAny) { // When catching a type parameter, csc emits an unbox.any instruction if (!unboxAny.Type.Equals(handler.Variable.Type)) return; exceptionSlotLoad = unboxAny.Argument; } if (!exceptionSlotLoad.MatchLdLoc(handler.Variable)) return; // Check that exceptionVar is only used within the catch block: var allUses = exceptionVar.LoadInstructions .Concat(exceptionVar.StoreInstructions.Cast()) .Concat(exceptionVar.AddressInstructions); foreach (var inst in allUses) { if (!inst.IsDescendantOf(handler)) return; } context.Step("TransformCatchVariable", entryPoint.Instructions[0]); exceptionVar.Kind = VariableKind.ExceptionLocal; exceptionVar.Name = handler.Variable.Name; exceptionVar.Type = handler.Variable.Type; exceptionVar.HasGeneratedName = handler.Variable.HasGeneratedName; handler.Variable = exceptionVar; if (isCatchBlock) { foreach (var offset in entryPoint.Instructions[0].Descendants.SelectMany(o => o.ILRanges)) handler.AddExceptionSpecifierILRange(offset); } entryPoint.Instructions.RemoveAt(0); } /// /// Inline condition from catch-when condition BlockContainer, if possible. /// void TransformCatchWhen(TryCatchHandler handler, Block entryPoint) { TransformCatchVariable(handler, entryPoint, isCatchBlock: false); if (entryPoint.Instructions.Count == 1 && entryPoint.Instructions[0].MatchLeave(out _, out var condition)) { context.Step("TransformCatchWhen", entryPoint.Instructions[0]); handler.Filter = condition; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs ================================================ // Copyright (c) 2019 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class FixRemainingIncrements : IILTransform { void IILTransform.Run(ILFunction function, ILTransformContext context) { var callsToFix = new List(); foreach (var call in function.Descendants.OfType()) { if (!UserDefinedCompoundAssign.IsIncrementOrDecrement(call.Method, context.Settings)) continue; if (call.Arguments.Count != 1) continue; if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { // For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)". // We can handle these calls in ReplaceMethodCallsWithOperators. continue; } callsToFix.Add(call); } foreach (var call in callsToFix) { // A user-defined increment/decrement that was not handled by TransformAssignment. // This can happen because the variable-being-incremented was optimized out by Roslyn, // e.g. // public void Issue1552Pre(UserType a, UserType b) // { // UserType num = a + b; // Console.WriteLine(++num); // } // can end up being compiled to: // Console.WriteLine(UserType.op_Increment(a + b)); if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot) { var store = (StLoc)call.Parent; var block = (Block)store.Parent; context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call); // stloc V(call op_Increment(...)) // -> // stloc V(...) // compound.assign op_Increment(V) call.ReplaceWith(call.Arguments[0]); block.Instructions.Insert(store.ChildIndex + 1, new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); } else { context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call); var newVariable = call.Arguments[0].Extract(context); if (newVariable == null) { Debug.Fail("Failed to extract argument of remaining increment/decrement"); continue; } newVariable.Type = call.GetParameter(0).Type; Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable)); call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs ================================================ // Copyright (c) 2017 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 ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// If possible, transforms plain ILAst loops into while (condition), do-while and for-loops. /// For the invariants of the transforms . /// public class HighLevelLoopTransform : IILTransform { ILTransformContext context; public void Run(ILFunction function, ILTransformContext context) { this.context = context; foreach (BlockContainer loop in function.Descendants.OfType()) { if (loop.Kind != ContainerKind.Loop) continue; if (MatchWhileLoop(loop, out var condition, out var loopBody)) { if (context.Settings.ForStatement) MatchForLoop(loop, condition, loopBody); continue; } if (context.Settings.DoWhileStatement && MatchDoWhileLoop(loop)) continue; } } bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody) { // ConditionDetection favours leave inside if and branch at end of block // while-loop: // if (!loop-condition) leave loop-container // ... condition = null; loopBody = loop.EntryPoint; if (!(loopBody.Instructions[0] is IfInstruction ifInstruction)) return false; if (!ifInstruction.FalseInst.MatchNop()) return false; if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition)) return false; condition = ifInstruction; if (!ifInstruction.TrueInst.MatchLeave(loop)) { // sometimes the loop-body is nested within the if // if (loop-condition) { loop-body } // leave loop-container if (loopBody.Instructions.Count != 2 || !loop.EntryPoint.Instructions.Last().MatchLeave(loop)) return false; if (!ifInstruction.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable)) ((Block)ifInstruction.TrueInst).Instructions.Add(new Leave(loop)); ConditionDetection.InvertIf(loopBody, ifInstruction, context); } context.Step("Transform to while (condition) loop: " + loop.EntryPoint.Label, loop); loop.Kind = ContainerKind.While; //invert comparison ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition); ifInstruction.FalseInst = ifInstruction.TrueInst; //move the rest of the body into a new block loopBody = new Block(); loopBody.AddRef(); ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count, loopBody); loop.Blocks.Insert(1, loopBody); loopBody.ReleaseRef(); if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) loopBody.Instructions.Add(new Leave(loop)); ifInstruction.TrueInst = new Branch(loopBody); ExpressionTransforms.RunOnSingleStatement(ifInstruction, context); // Analyze conditions and decide whether to move some of them out of the condition block: /*var conditions = new List(); SplitConditions(condition.Condition, conditions); // Break apart conditions that could be a MoveNext call followed by a Current accessor call: if (MightBeHeaderOfForEach(loop, conditions)) { ifInstruction.Condition = conditions[0]; foreach (var cond in conditions.Skip(1).Reverse()) { IfInstruction inst; loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop))); ExpressionTransforms.RunOnSingleStatment(inst, context); } }*/ return true; } bool MightBeHeaderOfForEach(BlockContainer loop, List conditions) { if (conditions.Count <= 1) return false; if (!(conditions[0] is CallInstruction moveNextCall && moveNextCall.Method.Name == "MoveNext" && conditions[1].Descendants.Any(IsGetCurrentCall))) return false; return loop.Parent?.Parent?.Parent is UsingInstruction; bool IsGetCurrentCall(ILInstruction inst) { return inst is CallInstruction getterCall && getterCall.Method.IsAccessor && getterCall.Method.Name == "get_Current"; } } void SplitConditions(ILInstruction expression, List conditions) { if (expression.MatchLogicAnd(out var l, out var r)) { SplitConditions(l, conditions); SplitConditions(r, conditions); } else { conditions.Add(expression); } } /// /// Matches a do-while loop and performs the following transformations: /// - combine all compatible conditions into one IfInstruction. /// - extract conditions into a condition block, or move the existing condition block to the end. /// bool MatchDoWhileLoop(BlockContainer loop) { (List conditions, ILInstruction exit, bool swap, bool split, bool unwrap) = AnalyzeDoWhileConditions(loop); // not a do-while loop, exit. if (conditions == null || conditions.Count == 0) return false; context.Step("Transform to do-while loop: " + loop.EntryPoint.Label, loop); Block conditionBlock; // first we remove all extracted instructions from the original block. var originalBlock = (Block)exit.Parent; if (unwrap) { // we found a condition block nested in a condition that is followed by a return statement: // we flip the condition and swap the blocks Debug.Assert(originalBlock.Parent is IfInstruction); var returnCondition = (IfInstruction)originalBlock.Parent; var topLevelBlock = (Block)returnCondition.Parent; Debug.Assert(topLevelBlock.Parent == loop); var leaveFunction = topLevelBlock.Instructions[returnCondition.ChildIndex + 1]; Debug.Assert(leaveFunction.MatchReturn(out _)); returnCondition.Condition = Comp.LogicNot(returnCondition.Condition); returnCondition.TrueInst = leaveFunction; // simplify the condition: ExpressionTransforms.RunOnSingleStatement(returnCondition, context); topLevelBlock.Instructions.RemoveAt(returnCondition.ChildIndex + 1); topLevelBlock.Instructions.AddRange(originalBlock.Instructions); originalBlock = topLevelBlock; split = true; } originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1); // we need to split the block: if (split) { // add a new block at the end and add a branch to the new block. conditionBlock = new Block(); loop.Blocks.Add(conditionBlock); originalBlock.Instructions.Add(new Branch(conditionBlock)); } else { // move the condition block to the end. conditionBlock = originalBlock; loop.Blocks.MoveElementToEnd(originalBlock); } // combine all conditions and the exit instruction into one IfInstruction: IfInstruction condition = null; conditionBlock.AddILRange(exit); foreach (var inst in conditions) { conditionBlock.AddILRange(inst); if (condition == null) { condition = inst; if (swap) { // branches must be swapped and condition negated: condition.Condition = Comp.LogicNot(condition.Condition); condition.FalseInst = condition.TrueInst; condition.TrueInst = exit; } else { condition.FalseInst = exit; } } else { if (swap) { condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition); } else { condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition); } } } // insert the combined conditions into the condition block: conditionBlock.Instructions.Add(condition); // simplify the condition: ExpressionTransforms.RunOnSingleStatement(condition, context); // transform complete loop.Kind = ContainerKind.DoWhile; return true; } static (List conditions, ILInstruction exit, bool swap, bool split, bool unwrap) AnalyzeDoWhileConditions(BlockContainer loop) { // we iterate over all blocks from the bottom, because the entry-point // should only be considered as condition block, if there are no other blocks. foreach (var block in loop.Blocks.Reverse()) { // first we match the end of the block: if (MatchDoWhileConditionBlock(loop, block, out bool swap, out bool unwrapCondtionBlock, out Block conditionBlock)) { // now collect all instructions that are usable as loop conditions var conditions = CollectConditions(loop, conditionBlock, swap); // split only if the block is either the entry-point or contains other instructions as well. var split = conditionBlock == loop.EntryPoint || conditionBlock.Instructions.Count > conditions.Count + 1; // + 1 is the final leave/branch. return (conditions, conditionBlock.Instructions.Last(), swap, split, unwrapCondtionBlock); } } return (null, null, false, false, false); } /// /// Returns a list of all IfInstructions that can be used as loop conditon, i.e., /// that have no false-instruction and have leave loop (if swapped) or branch entry-point as true-instruction. /// static List CollectConditions(BlockContainer loop, Block block, bool swap) { var list = new List(); int i = block.Instructions.Count - 2; while (i >= 0 && block.Instructions[i] is IfInstruction ifInst) { if (!ifInst.FalseInst.MatchNop()) break; if (UsesVariableCapturedInLoop(loop, ifInst.Condition)) break; if (swap) { if (!ifInst.TrueInst.MatchLeave(loop)) break; list.Add(ifInst); } else { if (!ifInst.TrueInst.MatchBranch(loop.EntryPoint)) break; list.Add(ifInst); } i--; } return list; } static bool UsesVariableCapturedInLoop(BlockContainer loop, ILInstruction condition) { foreach (var inst in condition.Descendants.OfType()) { if (inst.Variable.CaptureScope == loop) return true; } return false; } static bool MatchDoWhileConditionBlock(BlockContainer loop, Block block, out bool swapBranches, out bool unwrapCondtionBlock, out Block conditionBlock) { // match the end of the block: // if (condition) branch entry-point else nop // leave loop // -or- // if (condition) leave loop else nop // branch entry-point swapBranches = false; unwrapCondtionBlock = false; conditionBlock = block; // empty block? if (block.Instructions.Count < 2) return false; var last = block.Instructions.Last(); var ifInstruction = block.Instructions.SecondToLastOrDefault() as IfInstruction; // no IfInstruction or already transformed? if (ifInstruction == null || !ifInstruction.FalseInst.MatchNop()) return false; // the block ends in a return statement preceeded by an IfInstruction // take a look at the nested block and check if that might be a condition block if (last.MatchReturn(out _) && ifInstruction.TrueInst is Block nestedConditionBlock) { if (nestedConditionBlock.Instructions.Count < 2) return false; last = nestedConditionBlock.Instructions.Last(); ifInstruction = nestedConditionBlock.Instructions.SecondToLastOrDefault() as IfInstruction; if (ifInstruction == null || !ifInstruction.FalseInst.MatchNop()) return false; unwrapCondtionBlock = true; conditionBlock = nestedConditionBlock; } // if the last instruction is a branch // we assume the branch instructions need to be swapped. if (last.MatchBranch(loop.EntryPoint)) swapBranches = true; else if (last.MatchLeave(loop)) swapBranches = false; else return false; // match the IfInstruction if (swapBranches) { if (!ifInstruction.TrueInst.MatchLeave(loop)) return false; } else { if (!ifInstruction.TrueInst.MatchBranch(loop.EntryPoint)) return false; } return true; } // early match before block containers have been constructed internal static bool MatchDoWhileConditionBlock(Block block, out Block target1, out Block target2) { target1 = target2 = null; if (block.Instructions.Count < 2) return false; var last = block.Instructions.Last(); if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInstruction) || !ifInstruction.FalseInst.MatchNop()) return false; return (ifInstruction.TrueInst.MatchBranch(out target1) || ifInstruction.TrueInst.MatchReturn(out var _)) && (last.MatchBranch(out target2) || last.MatchReturn(out var _)); } internal static Block GetIncrementBlock(BlockContainer loop, Block whileLoopBody) => loop.Blocks.SingleOrDefault(b => b != whileLoopBody && b.Instructions.Last().MatchBranch(loop.EntryPoint) && b.Instructions.SkipLast(1).All(IsSimpleStatement)); internal static bool MatchIncrementBlock(Block block, out Block loopHead) => block.Instructions.Last().MatchBranch(out loopHead) && block.Instructions.SkipLast(1).All(IsSimpleStatement); bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody) { // for loops have exactly two incoming edges at the entry point. if (loop.EntryPoint.IncomingEdgeCount != 2) return false; // try to find an increment block: // consists of simple statements only. var incrementBlock = GetIncrementBlock(loop, whileLoopBody); if (incrementBlock != null) { // we found a possible increment block, just make sure, that there are at least three blocks: // - condition block // - loop body // - increment block if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3) return false; context.Step("Transform to for loop: " + loop.EntryPoint.Label, loop); // move the block to the end of the loop: loop.Blocks.MoveElementToEnd(incrementBlock); loop.Kind = ContainerKind.For; } else { // we need to move the increment statements into its own block: // last must be a branch entry-point var last = whileLoopBody.Instructions.LastOrDefault(); var secondToLast = whileLoopBody.Instructions.SecondToLastOrDefault(); if (last == null || secondToLast == null) return false; if (!last.MatchBranch(loop.EntryPoint)) return false; // we only deal with 'numeric' increments if (!MatchIncrement(secondToLast, out var incrementVariable)) return false; // the increment variable must be local/stack variable if (incrementVariable.Kind == VariableKind.Parameter) return false; // split conditions: var conditions = new List(); SplitConditions(whileCondition.Condition, conditions); IfInstruction forCondition = null; int numberOfConditions = 0; foreach (var condition in conditions) { // the increment variable must be used in the condition if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable))) break; // condition should not contain an assignment if (condition.Descendants.Any(IsAssignment)) break; if (forCondition == null) { forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst); } else { forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition); } numberOfConditions++; } if (numberOfConditions == 0) return false; context.Step("Transform to for loop: " + loop.EntryPoint.Label, loop); // split condition block: whileCondition.ReplaceWith(forCondition); ExpressionTransforms.RunOnSingleStatement(forCondition, context); for (int i = conditions.Count - 1; i >= numberOfConditions; i--) { IfInstruction inst; whileLoopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop))); ExpressionTransforms.RunOnSingleStatement(inst, context); } // create a new increment block and add it at the end: int secondToLastIndex = secondToLast.ChildIndex; var newIncremenBlock = new Block(); loop.Blocks.Add(newIncremenBlock); // move the increment instruction: newIncremenBlock.Instructions.Add(secondToLast); newIncremenBlock.Instructions.Add(last); newIncremenBlock.AddILRange(secondToLast); whileLoopBody.Instructions.RemoveRange(secondToLastIndex, 2); whileLoopBody.Instructions.Add(new Branch(newIncremenBlock)); // complete transform. loop.Kind = ContainerKind.For; } return true; } bool IsAssignment(ILInstruction inst) { if (inst is StLoc) return true; if (inst is CompoundAssignmentInstruction) return true; return false; } /// /// Returns true if the instruction is stloc v(add(ldloc v, arg)) /// or compound.assign(ldloca v, arg) /// public static bool MatchIncrement(ILInstruction inst, out ILVariable variable) { if (inst.MatchStLoc(out variable, out var value)) { if (value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { return left.MatchLdLoc(variable); } } else if (inst is CompoundAssignmentInstruction cai) { return cai.TargetKind == CompoundTargetKind.Address && cai.Target.MatchLdLoca(out variable); } return false; } /// /// Gets whether the statement is 'simple' (usable as for loop iterator): /// Currently we only accept calls and assignments. /// static bool IsSimpleStatement(ILInstruction inst) { switch (inst.OpCode) { case OpCode.Call: case OpCode.CallVirt: case OpCode.NewObj: case OpCode.StLoc: case OpCode.StObj: case OpCode.NumericCompoundAssign: case OpCode.UserDefinedCompoundAssign: return true; default: return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs ================================================ // Copyright (c) 2015 Daniel Grunwald // // 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. #nullable enable using System; using System.Diagnostics; using System.Threading; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Per-function IL transform. /// public interface IILTransform { void Run(ILFunction function, ILTransformContext context); } /// /// Parameter class holding various arguments for . /// public class ILTransformContext { public ILFunction Function { get; } public IDecompilerTypeSystem TypeSystem { get; } public IDebugInfoProvider? DebugInfo { get; } public DecompilerSettings Settings { get; } public CancellationToken CancellationToken { get; set; } public Stepper Stepper { get; set; } public Metadata.MetadataFile PEFile => TypeSystem.MainModule.MetadataFile; internal DecompileRun? DecompileRun { get; set; } internal UsingScope? UsingScope => DecompileRun?.UsingScope; CSharpResolver? csharpResolver; internal CSharpResolver CSharpResolver { get { var resolver = LazyInit.VolatileRead(ref csharpResolver); if (resolver != null) return resolver; return LazyInit.GetOrSet(ref csharpResolver, new CSharpResolver(new CSharpTypeResolveContext(TypeSystem.MainModule, UsingScope))); } } public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, IDebugInfoProvider? debugInfo, DecompilerSettings? settings = null) { this.Function = function ?? throw new ArgumentNullException(nameof(function)); this.TypeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.Settings = settings ?? new DecompilerSettings(); this.DebugInfo = debugInfo; Stepper = new Stepper(); } public ILTransformContext(ILTransformContext context, ILFunction? function = null) { this.Function = function ?? context.Function; this.TypeSystem = context.TypeSystem; this.DebugInfo = context.DebugInfo; this.Settings = context.Settings; this.DecompileRun = context.DecompileRun; this.CancellationToken = context.CancellationToken; this.Stepper = context.Stepper; } /// /// Creates a new ILReader instance for decompiling another method in the same assembly. /// internal ILReader CreateILReader() { return new ILReader(TypeSystem.MainModule) { UseDebugSymbols = Settings.UseDebugSymbols, UseRefLocalsForAccurateOrderOfEvaluation = Settings.UseRefLocalsForAccurateOrderOfEvaluation, DebugInfo = DebugInfo }; } /// /// Call this method immediately before performing a transform step. /// Unlike context.Stepper.Step(), calls to this method are only compiled in debug builds. /// [Conditional("STEP")] [DebuggerStepThrough] internal void Step(string description, ILInstruction? near) { Stepper.Step(description, near); } [Conditional("STEP")] [DebuggerStepThrough] internal void StepStartGroup(string description, ILInstruction? near = null) { Stepper.StartGroup(description, near); } [Conditional("STEP")] internal void StepEndGroup(bool keepIfEmpty = false) { Stepper.EndGroup(keepIfEmpty); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs ================================================ // Copyright (c) 2019 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Context object for the ILInstruction.Extract() operation. /// class ExtractionContext { /// /// Nearest function, used for registering the new locals that are created by extraction. /// readonly ILFunction Function; readonly ILTransformContext context; /// /// Combined flags of all instructions being moved. /// internal InstructionFlags FlagsBeingMoved; /// /// List of actions to be executed when performing the extraction. /// /// Each function in this list has the side-effect of replacing the instruction-to-be-moved /// with a load of a fresh temporary variable; and returns the the store to the temporary variable, /// which will be inserted at block-level. /// readonly List> MoveActions = new List>(); ExtractionContext(ILFunction function, ILTransformContext context) { Debug.Assert(function != null); this.Function = function; this.context = context; } internal void RegisterMove(ILInstruction predecessor) { FlagsBeingMoved |= predecessor.Flags; MoveActions.Add(delegate { var type = context.TypeSystem.FindType(predecessor.ResultType); var v = Function.RegisterVariable(VariableKind.StackSlot, type); predecessor.ReplaceWith(new LdLoc(v)); return new StLoc(v, predecessor); }); } internal void RegisterMoveIfNecessary(ILInstruction predecessor) { if (!CanReorderWithInstructionsBeingMoved(predecessor)) { RegisterMove(predecessor); } } /// /// Currently, predecessor is evaluated before the instructions being moved. /// If this function returns true, predecessor can stay as-is, despite the move changing the evaluation order. /// If this function returns false, predecessor will need to also move, to ensure the evaluation order stays unchanged. /// public bool CanReorderWithInstructionsBeingMoved(ILInstruction predecessor) { // We could track the instructions being moved and be smarter about unnecessary moves, // but given the limited scenarios where extraction is used so far, // this seems unnecessary. return predecessor.Flags == InstructionFlags.None; } /// /// Extracts the specified instruction: /// The instruction is replaced with a load of a new temporary variable; /// and the instruction is moved to a store to said variable at block-level. /// /// May return null if extraction is not possible. /// public static ILVariable? Extract(ILInstruction instToExtract, ILTransformContext context) { var function = instToExtract.Ancestors.OfType().First(); ExtractionContext ctx = new ExtractionContext(function, context); ctx.FlagsBeingMoved = instToExtract.Flags; ILInstruction? inst = instToExtract; while (inst != null) { if (inst.Parent is IfInstruction ifInst && inst.SlotInfo != IfInstruction.ConditionSlot) { // this context doesn't support extraction, but maybe we can create a block here? if (ifInst.ResultType == StackType.Void) { Block newBlock = new Block(); inst.ReplaceWith(newBlock); newBlock.Instructions.Add(inst); } } if (inst.Parent is Block { Kind: BlockKind.ControlFlow } block) { // We've reached a target block, and extraction is possible all the way. // Check if the parent BlockContainer allows extraction: if (block.Parent is BlockContainer container) { switch (container.Kind) { case ContainerKind.Normal: case ContainerKind.Loop: // extraction is always possible break; case ContainerKind.Switch: // extraction is possible, unless in the entry-point (i.e., the switch head) if (block == container.EntryPoint && inst.ChildIndex == 0) { // try to extract to the container's parent block, if it's a valid location inst = container; continue; } break; case ContainerKind.While: // extraction is possible, unless in the entry-point (i.e., the condition block) if (block == container.EntryPoint) { return null; } break; case ContainerKind.DoWhile: // extraction is possible, unless in the last block (i.e., the condition block) if (block == container.Blocks.Last()) { return null; } break; case ContainerKind.For: // extraction is possible, unless in the first or last block // (i.e., the condition block or increment block) if (block == container.EntryPoint || block == container.Blocks.Last()) { return null; } break; } } int insertIndex = inst.ChildIndex; var type = context.TypeSystem.FindType(instToExtract.ResultType); // Move instToExtract itself: var v = function.RegisterVariable(VariableKind.StackSlot, type); instToExtract.ReplaceWith(new LdLoc(v)); block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract)); // Apply the other move actions: foreach (var moveAction in ctx.MoveActions) { block.Instructions.Insert(insertIndex, moveAction()); } return v; } if (inst.Parent != null && !inst.Parent.PrepareExtract(inst.ChildIndex, ctx)) return null; inst = inst.Parent; } return null; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs ================================================ // Copyright (c) 2011-2017 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using System.Reflection; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.IL.Transforms { [Flags] public enum InliningOptions { None = 0, Aggressive = 1, IntroduceNamedArguments = 2, FindDeconstruction = 4, AllowChangingOrderOfEvaluationForExceptions = 8, AllowInliningOfLdloca = 0x10 } /// /// Performs inlining transformations. /// public class ILInlining : IILTransform, IBlockTransform, IStatementTransform { internal InliningOptions options; public void Run(ILFunction function, ILTransformContext context) { foreach (var block in function.Descendants.OfType()) { InlineAllInBlock(function, block, this.options, context); } function.Variables.RemoveDead(); } public void Run(Block block, BlockTransformContext context) { InlineAllInBlock(context.Function, block, this.options, context); } public void Run(Block block, int pos, StatementTransformContext context) { var options = this.options | OptionsForBlock(block, pos, context); while (InlineOneIfPossible(block, pos, options, context: context)) { // repeat inlining until nothing changes. } } internal static InliningOptions OptionsForBlock(Block block, int pos, ILTransformContext context) { InliningOptions options = InliningOptions.None; if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block)) { options |= InliningOptions.Aggressive; } else { var function = block.Ancestors.OfType().FirstOrDefault(); var inst = block.Instructions[pos]; if (IsInConstructorInitializer(function, inst) || PreferExpressionsOverStatements(function)) options |= InliningOptions.Aggressive; } if (!context.Settings.UseRefLocalsForAccurateOrderOfEvaluation) { options |= InliningOptions.AllowChangingOrderOfEvaluationForExceptions; } return options; } static bool PreferExpressionsOverStatements(ILFunction function) { switch (function.Kind) { case ILFunctionKind.Delegate: return function.Parameters.Any(p => CSharp.CSharpDecompiler.IsTransparentIdentifier(p.Name)); case ILFunctionKind.ExpressionTree: return true; default: return false; } } public static bool InlineAllInBlock(ILFunction function, Block block, InliningOptions options, ILTransformContext context) { bool modified = false; var instructions = block.Instructions; for (int i = instructions.Count - 1; i >= 0; i--) { if (instructions[i] is StLoc inst) { if (InlineOneIfPossible(block, i, options, context)) { modified = true; continue; } } } return modified; } internal static bool IsInConstructorInitializer(ILFunction function, ILInstruction inst) { int ctorCallStart = function.ChainedConstructorCallILOffset; if (inst.EndILOffset > ctorCallStart) return false; var topLevelInst = inst.Ancestors.LastOrDefault(instr => instr.Parent is Block); if (topLevelInst == null) return false; return topLevelInst.EndILOffset <= ctorCallStart; } internal static bool IsCatchWhenBlock(Block block) { var container = BlockContainer.FindClosestContainer(block); return container?.Parent is TryCatchHandler handler && handler.Filter == container; } /// /// Inlines instructions before pos into block.Instructions[pos]. /// /// The number of instructions that were inlined. public static int InlineInto(Block block, int pos, InliningOptions options, ILTransformContext context) { if (pos >= block.Instructions.Count) return 0; int count = 0; while (--pos >= 0) { if (InlineOneIfPossible(block, pos, options, context)) count++; else break; } return count; } /// /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// public static bool InlineIfPossible(Block block, int pos, ILTransformContext context) { return InlineOneIfPossible(block, pos, InliningOptions.Aggressive, context); } /// /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible. /// public static bool InlineOneIfPossible(Block block, int pos, InliningOptions options, ILTransformContext context) { context.CancellationToken.ThrowIfCancellationRequested(); if (block.Instructions[pos] is not StLoc stloc) return false; if (!VariableCanBeUsedForInlining(stloc.Variable)) return false; // TODO: inlining of small integer types might be semantically incorrect, // but we can't avoid it this easily without breaking lots of tests. //if (v.Type.IsSmallIntegerType()) // return false; // stloc might perform implicit truncation return InlineOne(stloc, options, context); } public static bool VariableCanBeUsedForInlining(ILVariable v) { if (v.Kind == VariableKind.PinnedLocal) return false; // ensure the variable is accessed only a single time if (v.StoreCount != 1) return false; if (v.LoadCount + v.AddressCount != 1) return false; return true; } /// /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// public static bool InlineOne(StLoc stloc, InliningOptions options, ILTransformContext context) { ILVariable v = stloc.Variable; Block block = (Block)stloc.Parent; int pos = stloc.ChildIndex; if (DoInline(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos + 1), options, context)) { // Assign the ranges of the stloc instruction: stloc.Value.AddILRange(stloc); // Remove the stloc instruction: Debug.Assert(block.Instructions[pos] == stloc); block.Instructions.RemoveAt(pos); return true; } else if (v.LoadCount == 0 && v.AddressCount == 0) { // The variable is never loaded if (SemanticHelper.IsPure(stloc.Value.Flags)) { // Remove completely if the instruction has no effects // (except for reading locals) context.Step("Remove dead store without side effects", stloc); block.Instructions.RemoveAt(pos); return true; } else if (v.Kind == VariableKind.StackSlot) { context.Step("Remove dead store, but keep expression", stloc); // Assign the ranges of the stloc instruction: stloc.Value.AddILRange(stloc); // Remove the stloc, but keep the inner expression stloc.ReplaceWith(stloc.Value); return true; } } return false; } /// /// Inlines 'expr' into 'next', if possible. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context) { var r = FindLoadInNext(next, v, inlinedExpression, options); if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument) { var loadInst = r.LoadInst; if (loadInst.OpCode == OpCode.LdLoca) { if (!IsGeneratedTemporaryForAddressOf((LdLoca)loadInst, v, inlinedExpression, options)) return false; } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); bool aggressive = (options & InliningOptions.Aggressive) != 0; if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, r, inlinedExpression, v)) { return false; } } if (r.Type == FindResultType.NamedArgument) { NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context); // Now that the argument is evaluated early, we can inline as usual } context.Step($"Inline variable '{v.Name}'", inlinedExpression); // Assign the ranges of the ldloc instruction: inlinedExpression.AddILRange(loadInst); if (loadInst.OpCode == OpCode.LdLoca) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' // to preserve the semantics of the compiler-generated temporary Debug.Assert(((LdLoca)loadInst).Variable == v); loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type)); } else { loadInst.ReplaceWith(inlinedExpression); } return true; } return false; } /// /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// /// The load instruction (a descendant within 'next') /// The variable being inlined. static bool IsGeneratedTemporaryForAddressOf(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options) { Debug.Assert(loadInst.Variable == v); if (!options.HasFlag(InliningOptions.AllowInliningOfLdloca)) { return false; // inlining of ldloca is not allowed in the early inlining stage } // Inlining a value type variable is allowed only if the resulting code will maintain the semantics // that the method is operating on a copy. // Thus, we have to ensure we're operating on an r-value. // Additionally, we cannot inline in cases where the C# compiler prohibits the direct use // of the rvalue (e.g. M(ref (MyStruct)obj); is invalid). if (IsUsedAsThisPointerInCall(loadInst, out var method, out var constrainedTo)) { if (options.HasFlag(InliningOptions.Aggressive)) { // Inlining might be required in ctor initializers (see #2714). // expressionBuilder.VisitAddressOf will handle creating the copy for us. return true; } switch (ClassifyExpression(inlinedExpression)) { case ExpressionClassification.RValue: // For struct method calls on rvalues, the C# compiler always generates temporaries. return true; case ExpressionClassification.MutableLValue: // For struct method calls on mutable lvalues, the C# compiler never generates temporaries. return false; case ExpressionClassification.ReadonlyLValue: // For struct method calls on readonly lvalues, the C# compiler // only generates a temporary if it isn't a "readonly struct" return MethodRequiresCopyForReadonlyLValue(method, constrainedTo); default: throw new InvalidOperationException("invalid expression classification"); } } else if (loadInst.Parent is LdElemaInlineArray) { return true; } else if (IsPassedToReadOnlySpanCtor(loadInst)) { // Always inlining is possible here, because it's an 'in' or 'ref readonly' parameter // and the C# compiler allows calling it with an rvalue, even though that might produce // a warning. Note that we don't need to check the expression classification, because // expressionBuilder.VisitAddressOf will handle creating the copy for us. // This is necessary, because there are compiler-generated uses of this ctor when // passing a single-element array to a params ROS parameter and our following transforms // assume the value is already inlined. return true; } else if (IsPassedToInlineArrayAsSpan(loadInst)) { // Inlining is not allowed: // .InlineArrayAsReadOnlySpan(GetInlineArray()) is invalid C# code return false; } else if (IsPassedToInParameter(loadInst)) { if (options.HasFlag(InliningOptions.Aggressive)) { // Inlining might be required in ctor initializers (see #2714). // expressionBuilder.VisitAddressOf will handle creating the copy for us. return true; } switch (ClassifyExpression(inlinedExpression)) { case ExpressionClassification.RValue: // For rvalues passed to in parameters, the C# compiler generates a temporary. return true; case ExpressionClassification.MutableLValue: case ExpressionClassification.ReadonlyLValue: // For lvalues passed to in parameters, the C# compiler never generates temporaries. return false; default: throw new InvalidOperationException("invalid expression classification"); } } else if (IsUsedAsThisPointerInFieldRead(loadInst)) { // mcs generated temporaries for field reads on rvalues (#1555) return ClassifyExpression(inlinedExpression) == ExpressionClassification.RValue; } else { return false; } } private static bool IsPassedToInlineArrayAsSpan(LdLoca loadInst) { if (loadInst.Parent is not Call call) return false; var method = call.Method; var declaringType = method.DeclaringType; return declaringType.ReflectionName == "" && method.Name is "InlineArrayAsReadOnlySpan" or "InlineArrayAsSpan" && method.Parameters is [var arg, var length] && (method.ReturnType.IsKnownType(KnownTypeCode.SpanOfT) || method.ReturnType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) && arg.Type is ByReferenceType && length.Type.IsKnownType(KnownTypeCode.Int32); } internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method, IType constrainedTo = null) { if (method == null) return true; var type = constrainedTo ?? method.DeclaringType; if (type.IsReferenceType == true) return false; // reference types are never implicitly copied if (method.ThisIsRefReadOnly) return false; // no copies for calls on readonly structs return true; } internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca) { return IsUsedAsThisPointerInCall(ldloca, out _, out _); } static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method, out IType constrainedType) { method = null; constrainedType = null; if (ldloca.Variable.Type.IsReferenceType ?? false) return false; ILInstruction inst = ldloca; if (inst.Parent is LdObjIfRef) { inst = inst.Parent; } while (inst.Parent is LdFlda ldflda) { inst = ldflda; } if (inst.ChildIndex != 0) return false; switch (inst.Parent.OpCode) { case OpCode.Call: case OpCode.CallVirt: var callInst = (CallInstruction)inst.Parent; method = callInst.Method; constrainedType = callInst.ConstrainedTo; if (method.IsAccessor) { if (method.AccessorKind == MethodSemanticsAttributes.Getter) { // C# doesn't allow property compound assignments on temporary structs return !(inst.Parent.Parent is CompoundAssignmentInstruction cai && cai.TargetKind == CompoundTargetKind.Property && cai.Target == inst.Parent); } else { // C# doesn't allow calling setters on temporary structs return false; } } return !method.IsStatic; case OpCode.Await: method = ((Await)inst.Parent).GetAwaiterMethod; return true; case OpCode.NullableUnwrap: return ((NullableUnwrap)inst.Parent).RefInput; case OpCode.MatchInstruction: method = ((MatchInstruction)inst.Parent).Method; return true; default: return false; } } static bool IsUsedAsThisPointerInFieldRead(LdLoca ldloca) { if (ldloca.Variable.Type.IsReferenceType ?? false) return false; ILInstruction inst = ldloca; while (inst.Parent is LdFlda ldflda) { inst = ldflda; } return inst != ldloca && inst.Parent is LdObj; } internal static bool IsPassedToInParameter(LdLoca ldloca) { if (ldloca.Parent is not CallInstruction call) { return false; } return call.GetParameter(ldloca.ChildIndex)?.ReferenceKind is ReferenceKind.In; } static bool IsPassedToReadOnlySpanCtor(LdLoca ldloca) { if (ldloca.Parent is not NewObj call) { return false; } var method = call.Method; return method.IsConstructor && method.Parameters.Count == 1 && method.DeclaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT); } internal static bool IsReadOnlySpanCharCtor(IMethod method) { return method.IsConstructor && method.Parameters.Count == 1 && method.DeclaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Char) && method.Parameters[0].Type is ByReferenceType brt && brt.ElementType.IsKnownType(KnownTypeCode.Char); } /// /// Gets whether the instruction, when converted into C#, turns into an l-value that can /// be used to mutate a value-type. /// If this function returns false, the C# compiler would introduce a temporary copy /// when calling a method on a value-type (and any mutations performed by the method will be lost) /// internal static ExpressionClassification ClassifyExpression(ILInstruction inst) { switch (inst.OpCode) { case OpCode.LdLoc: case OpCode.StLoc: ILVariable v = ((IInstructionWithVariableOperand)inst).Variable; if (v.IsRefReadOnly || v.Kind == VariableKind.ForeachLocal || v.Kind == VariableKind.UsingLocal) { return ExpressionClassification.ReadonlyLValue; } else { return ExpressionClassification.MutableLValue; } case OpCode.LdObj: // ldobj typically refers to a storage location, // but readonly fields are an exception. if (IsReadonlyReference(((LdObj)inst).Target)) { return ExpressionClassification.ReadonlyLValue; } else { return ExpressionClassification.MutableLValue; } case OpCode.StObj: // stobj is the same as ldobj. if (IsReadonlyReference(((StObj)inst).Target)) { return ExpressionClassification.ReadonlyLValue; } else { return ExpressionClassification.MutableLValue; } case OpCode.Call: var m = ((CallInstruction)inst).Method; // multi-dimensional array getters are lvalues, // everything else is an rvalue. if (m.DeclaringType.Kind == TypeKind.Array) { return ExpressionClassification.MutableLValue; } else { return ExpressionClassification.RValue; } default: return ExpressionClassification.RValue; // most instructions result in an rvalue } } /// /// Gets whether the ILInstruction will turn into a C# expresion that is considered readonly by the C# compiler. /// internal static bool IsReadonlyReference(ILInstruction addr) { switch (addr) { case LdFlda ldflda: return ldflda.Field.IsReadOnly || (ldflda.Field.DeclaringType.Kind == TypeKind.Struct && IsReadonlyReference(ldflda.Target)); case LdsFlda ldsflda: return ldsflda.Field.IsReadOnly; case LdLoc ldloc: return ldloc.Variable.IsRefReadOnly; case Call call: return call.Method.ReturnTypeIsRefReadOnly; case CallVirt call: return call.Method.ReturnTypeIsRefReadOnly; case CallIndirect calli: return calli.FunctionPointerType.ReturnIsRefReadOnly; case AddressOf _: // C# doesn't allow mutation of value-type temporaries return true; default: if (addr.MatchLdFld(out _, out var field)) return field.ReturnTypeIsRefReadOnly; return false; } } /// /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// /// The next top-level expression /// The variable being eliminated by inlining. /// The expression being inlined static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) { if (findResult.Type == FindResultType.NamedArgument) { var originalStore = (StLoc)inlinedExpression.Parent; return !originalStore.ILStackWasEmpty; } Debug.Assert(findResult.Type == FindResultType.Found); var loadInst = findResult.LoadInst; Debug.Assert(loadInst.IsDescendantOf(next)); // decide based on the source expression being inlined switch (inlinedExpression.OpCode) { case OpCode.DefaultValue: case OpCode.StObj: case OpCode.NumericCompoundAssign: case OpCode.UserDefinedCompoundAssign: case OpCode.Await: case OpCode.SwitchInstruction: return true; case OpCode.LdLoc: if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) { // Roslyn likes to put the result of fetching a state machine field into a temporary variable, // so inline more aggressively in such cases. return true; } break; } if (inlinedExpression.ResultType == StackType.Ref) { // VB likes to use ref locals for compound assignment // (the C# compiler uses ref stack slots instead). // We want to avoid unnecessary ref locals, so we'll always inline them if possible. return true; } var parent = loadInst.Parent; if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) { // inline into nullable ctor call in lifted operator parent = parent.Parent; } if (parent is ILiftableInstruction liftable && liftable.IsLifted) { return true; // inline into lifted operators } // decide based on the new parent into which we are inlining: switch (parent.OpCode) { case OpCode.NullCoalescingInstruction: if (NullableType.IsNullable(v.Type)) return true; // inline nullables into ?? operator break; case OpCode.NullableUnwrap: return true; // inline into ?. operator case OpCode.UserDefinedLogicOperator: case OpCode.DynamicLogicOperatorInstruction: return true; // inline into (left slot of) user-defined && or || operator case OpCode.DynamicGetMemberInstruction: case OpCode.DynamicGetIndexInstruction: if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) return true; // inline into dynamic compound assignments break; case OpCode.DynamicCompoundAssign: return true; case OpCode.GetPinnableReference: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span case OpCode.Call: case OpCode.CallVirt: // Aggressive inline into property/indexer getter calls for compound assignment calls // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter) if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot) { return true; } if (((CallInstruction)parent).Method is SyntheticRangeIndexAccessor) { return true; } break; case OpCode.CallIndirect when loadInst.SlotInfo == CallIndirect.FunctionPointerSlot: return true; case OpCode.LdElema: if (((LdElema)parent).WithSystemIndex) { return true; } break; case OpCode.Leave: case OpCode.YieldReturn: return true; case OpCode.SwitchInstruction: // Preserve type info on switch instruction, if we're inlining a local variable into the switch-value slot. if (v.Kind != VariableKind.StackSlot && loadInst.SlotInfo == SwitchInstruction.ValueSlot) { ((SwitchInstruction)parent).Type ??= v.Type; } return true; //case OpCode.BinaryNumericInstruction when parent.SlotInfo == SwitchInstruction.ValueSlot: case OpCode.StringToInt when parent.SlotInfo == SwitchInstruction.ValueSlot: return true; case OpCode.MatchInstruction: var match = (MatchInstruction)parent; if (match.IsDeconstructTuple || (match.CheckType && match.Variable.Type.IsReferenceType != true)) { return true; } break; } // decide based on the top-level target instruction into which we are inlining: switch (next.OpCode) { case OpCode.IfInstruction: while (parent.MatchLogicNot(out _)) { parent = parent.Parent; } return parent == next; default: return false; } } /// /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. /// public static bool CanInlineInto(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved) { return FindLoadInNext(expr, v, expressionBeingMoved, InliningOptions.None).Type == FindResultType.Found; } internal enum FindResultType { /// /// Found a load; inlining is possible. /// Found, /// /// Load not found and re-ordering not possible. Stop the search. /// Stop, /// /// Load not found, but the expressionBeingMoved can be re-ordered with regards to the /// tested expression, so we may continue searching for the matching load. /// Continue, /// /// Found a load in call, but re-ordering not possible with regards to the /// other call arguments. /// Inlining is not possible, but we might convert the call to named arguments. /// Only used with . /// NamedArgument, /// /// Found a deconstruction. /// Only used with . /// Deconstruction, } internal readonly struct FindResult { public readonly FindResultType Type; public readonly ILInstruction LoadInst; // ldloc or ldloca instruction that loads the variable to be inlined public readonly ILInstruction CallArgument; // argument of call that needs to be promoted to a named argument private FindResult(FindResultType type, ILInstruction loadInst, ILInstruction callArg) { this.Type = type; this.LoadInst = loadInst; this.CallArgument = callArg; } public static readonly FindResult Stop = new FindResult(FindResultType.Stop, null, null); public static readonly FindResult Continue = new FindResult(FindResultType.Continue, null, null); public static FindResult Found(ILInstruction loadInst) { Debug.Assert(loadInst.OpCode == OpCode.LdLoc || loadInst.OpCode == OpCode.LdLoca); return new FindResult(FindResultType.Found, loadInst, null); } public static FindResult NamedArgument(ILInstruction loadInst, ILInstruction callArg) { Debug.Assert(loadInst.OpCode == OpCode.LdLoc || loadInst.OpCode == OpCode.LdLoca); Debug.Assert(callArg.Parent is CallInstruction); return new FindResult(FindResultType.NamedArgument, loadInst, callArg); } public static FindResult Deconstruction(DeconstructInstruction deconstruction) { return new FindResult(FindResultType.Deconstruction, deconstruction, null); } } /// /// Finds the position to inline to. /// /// true = found; false = cannot continue search; null = not found internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options) { if (expr == null) return FindResult.Stop; if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v)) { // Match found, we can inline unless there are slot restrictions. if (expr.Parent.SatisfiesSlotRestrictionForInlining(expr.ChildIndex, expressionBeingMoved)) { return FindResult.Found(expr); } // We cannot inline because the targets slot restrictions are not satisfied if ((options & InliningOptions.AllowChangingOrderOfEvaluationForExceptions) != 0 && expr.SlotInfo == StObj.TargetSlot) { // Special case: inlining will change code semantics, // but we accept that so that we can avoid a ref local. if (expressionBeingMoved is LdFlda ldflda) ldflda.DelayExceptions = true; else if (expressionBeingMoved is LdElema ldelema) ldelema.DelayExceptions = true; return FindResult.Found(expr); } return FindResult.Stop; } else if (expr is Block { Kind: BlockKind.CallWithNamedArgs } block) { return NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved); } else if (options.HasFlag(InliningOptions.FindDeconstruction) && expr is DeconstructInstruction di) { return FindResult.Deconstruction(di); } foreach (var child in expr.Children) { if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved)) return FindResult.Stop; // Recursively try to find the load instruction FindResult r = FindLoadInNext(child, v, expressionBeingMoved, options); if (r.Type != FindResultType.Continue) { if (r.Type == FindResultType.Stop && (options & InliningOptions.IntroduceNamedArguments) != 0 && expr is CallInstruction call) return NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, expressionBeingMoved); return r; } } if (IsSafeForInlineOver(expr, expressionBeingMoved)) return FindResult.Continue; // continue searching else return FindResult.Stop; // abort, inlining not possible } /// /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' /// static bool IsSafeForInlineOver(ILInstruction expr, ILInstruction expressionBeingMoved) { return SemanticHelper.MayReorder(expressionBeingMoved, expr); } /// /// Finds the first call instruction within the instructions that were inlined into inst. /// internal static CallInstruction FindFirstInlinedCall(ILInstruction inst) { foreach (var child in inst.Children) { if (!child.SlotInfo.CanInlineInto) break; var call = FindFirstInlinedCall(child); if (call != null) { return call; } } return inst as CallInstruction; } /// /// Gets whether 'expressionBeingMoved' can be moved from somewhere before 'stmt' to become the replacement of 'targetLoad'. /// public static bool CanMoveInto(ILInstruction expressionBeingMoved, ILInstruction stmt, ILInstruction targetLoad) { Debug.Assert(targetLoad.IsDescendantOf(stmt)); for (ILInstruction inst = targetLoad; inst != stmt; inst = inst.Parent) { if (!inst.Parent.CanInlineIntoSlot(inst.ChildIndex, expressionBeingMoved)) return false; // Check whether re-ordering with predecessors is valid: int childIndex = inst.ChildIndex; for (int i = 0; i < childIndex; ++i) { ILInstruction predecessor = inst.Parent.Children[i]; if (!IsSafeForInlineOver(predecessor, expressionBeingMoved)) return false; } } return true; } /// /// Gets whether 'expressionBeingMoved' can be moved from somewhere before 'stmt' to become the replacement of 'targetLoad'. /// public static bool CanMoveIntoCallVirt(ILInstruction expressionBeingMoved, ILInstruction stmt, CallVirt nestedCallVirt, ILInstruction targetLoad) { Debug.Assert(targetLoad.IsDescendantOf(stmt) && nestedCallVirt.IsDescendantOf(stmt)); ILInstruction thisArg = nestedCallVirt.Arguments[0]; Debug.Assert(thisArg is LdObjIfRef); for (ILInstruction inst = targetLoad; inst != stmt; inst = inst.Parent) { if (!inst.Parent.CanInlineIntoSlot(inst.ChildIndex, expressionBeingMoved)) return false; // Check whether re-ordering with predecessors is valid: int childIndex = inst.ChildIndex; for (int i = 0; i < childIndex; ++i) { ILInstruction predecessor = inst.Parent.Children[i]; if (predecessor != thisArg && !IsSafeForInlineOver(predecessor, expressionBeingMoved)) return false; } } return true; } /// /// Gets whether arg can be un-inlined out of stmt. /// /// internal static bool CanUninline(ILInstruction arg, ILInstruction stmt) { // moving into and moving out-of are equivalent return CanMoveInto(arg, stmt, arg); } } internal enum ExpressionClassification { RValue, MutableLValue, ReadonlyLValue, } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transform for the C# 8 System.Index / System.Range feature /// class IndexRangeTransform : IStatementTransform { /// /// Called by expression transforms. /// Handles the `array[System.Index]` cases. /// public static bool HandleLdElema(LdElema ldelema, ILTransformContext context) { if (!context.Settings.Ranges) return false; if (!ldelema.Array.MatchLdLoc(out ILVariable array)) return false; if (ldelema.Indices.Count != 1) return false; // the index/range feature doesn't support multi-dimensional arrays var index = ldelema.Indices[0]; if (index is CallInstruction call && call.Method.Name == "GetOffset" && call.Method.DeclaringType.IsKnownType(KnownTypeCode.Index)) { // ldelema T(ldloc array, call GetOffset(..., ldlen.i4(ldloc array))) // -> withsystemindex.ldelema T(ldloc array, ...) if (call.Arguments.Count != 2) return false; if (!(call.Arguments[1].MatchLdLen(StackType.I4, out var arrayLoad) && arrayLoad.MatchLdLoc(array))) return false; context.Step("ldelema with System.Index", ldelema); foreach (var node in call.Arguments[1].Descendants) ldelema.AddILRange(node); ldelema.AddILRange(call); ldelema.WithSystemIndex = true; // The method call had a `ref System.Index` argument for the this pointer, but we want a `System.Index` by-value. ldelema.Indices[0] = new LdObj(call.Arguments[0], call.Method.DeclaringType); return true; } else if (index is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub && !bni.IsLifted && !bni.CheckForOverflow) { // ldelema T(ldloc array, binary.sub.i4(ldlen.i4(ldloc array), ...)) // -> withsystemindex.ldelema T(ldloc array, newobj System.Index(..., fromEnd: true)) if (!(bni.Left.MatchLdLen(StackType.I4, out var arrayLoad) && arrayLoad.MatchLdLoc(array))) return false; var indexMethods = new IndexMethods(context.TypeSystem); if (!indexMethods.IsValid) return false; // don't use System.Index if not supported by the target framework context.Step("ldelema indexed from end", ldelema); foreach (var node in bni.Left.Descendants) ldelema.AddILRange(node); ldelema.AddILRange(bni); ldelema.WithSystemIndex = true; ldelema.Indices[0] = MakeIndex(IndexKind.FromEnd, bni.Right, indexMethods); return true; } return false; } class IndexMethods { public readonly IMethod IndexCtor; public readonly IMethod IndexImplicitConv; public readonly IMethod RangeCtor; public IType IndexType => IndexCtor?.DeclaringType; public IType RangeType => RangeCtor?.DeclaringType; public bool IsValid => IndexCtor != null && IndexImplicitConv != null && RangeCtor != null; public readonly IMethod RangeStartAt; public readonly IMethod RangeEndAt; public readonly IMethod RangeGetAll; public IndexMethods(ICompilation compilation) { var indexType = compilation.FindType(KnownTypeCode.Index); foreach (var ctor in indexType.GetConstructors(m => m.Parameters.Count == 2)) { if (ctor.Parameters[0].Type.IsKnownType(KnownTypeCode.Int32) && ctor.Parameters[1].Type.IsKnownType(KnownTypeCode.Boolean)) { this.IndexCtor = ctor; } } foreach (var op in indexType.GetMethods(m => m.IsOperator && m.Name == "op_Implicit")) { if (op.Parameters.Count == 1 && op.Parameters[0].Type.IsKnownType(KnownTypeCode.Int32)) { this.IndexImplicitConv = op; } } var rangeType = compilation.FindType(KnownTypeCode.Range); foreach (var ctor in rangeType.GetConstructors(m => m.Parameters.Count == 2)) { if (ctor.Parameters[0].Type.IsKnownType(KnownTypeCode.Index) && ctor.Parameters[1].Type.IsKnownType(KnownTypeCode.Index)) { this.RangeCtor = ctor; } } foreach (var m in rangeType.GetMethods(m => m.Parameters.Count == 1)) { if (m.Parameters.Count == 1 && m.Parameters[0].Type.IsKnownType(KnownTypeCode.Index)) { if (m.Name == "StartAt") this.RangeStartAt = m; else if (m.Name == "EndAt") this.RangeEndAt = m; } } foreach (var p in rangeType.GetProperties(p => p.IsStatic && p.Name == "All")) { this.RangeGetAll = p.Getter; } } public static bool IsRangeCtor(IMethod method) { return method.SymbolKind == SymbolKind.Constructor && method.Parameters.Count == 2 && method.DeclaringType.IsKnownType(KnownTypeCode.Range) && method.Parameters.All(p => p.Type.IsKnownType(KnownTypeCode.Index)); } } void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.Ranges) return; int startPos = pos; ILVariable containerVar = null; // The container length access may be a separate instruction, or it may be inline with the variable's use if (MatchContainerLengthStore(block.Instructions[pos], out ILVariable containerLengthVar, ref containerVar)) { // stloc containerLengthVar(call get_Length/get_Count(ldloc container)) pos++; } else { // Reset if MatchContainerLengthStore only had a partial match. MatchGetOffset() will then set `containerVar`. containerLengthVar = null; containerVar = null; } if (block.Instructions[pos].MatchStLoc(out var rangeVar, out var rangeVarInit) && rangeVar.Type.IsKnownType(KnownTypeCode.Range)) { // stloc rangeVar(rangeVarInit) pos++; } else { rangeVar = null; rangeVarInit = null; } // stloc startOffsetVar(call GetOffset(startIndexLoad, ldloc length)) if (!(block.Instructions[pos].MatchStLoc(out ILVariable startOffsetVar, out ILInstruction startOffsetVarInit) && startOffsetVar.IsSingleDefinition && startOffsetVar.StackType == StackType.I4)) { // Not our primary indexing/slicing pattern. // However, we might be dealing with a partially-transformed pattern that needs to be extended. ExtendSlicing(); return; } var startIndexKind = MatchGetOffset(startOffsetVarInit, out ILInstruction startIndexLoad, containerLengthVar, ref containerVar); pos++; if (startOffsetVar.LoadCount == 1) { TransformIndexing(); } else if (startOffsetVar.LoadCount == 2) { // might be slicing: startOffset is used once for the slice length calculation, and once for the Slice() method call TransformSlicing(); } void TransformIndexing() { // complex_expr(call get_Item(ldloc container, ldloc startOffsetVar)) if (rangeVar != null) return; if (!(startOffsetVar.LoadInstructions.Single().Parent is CallInstruction call)) return; if (call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter && call.Arguments.Count == 2) { if (call.Method.AccessorOwner?.SymbolKind != SymbolKind.Indexer) return; if (call.Method.Parameters.Count != 1) return; } else if (call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Setter && call.Arguments.Count == 3) { if (call.Method.AccessorOwner?.SymbolKind != SymbolKind.Indexer) return; if (call.Method.Parameters.Count != 2) return; } else if (IsSlicingMethod(call.Method)) { TransformSlicing(sliceLengthWasMisdetectedAsStartOffset: true); return; } else { return; } if (startIndexKind == IndexKind.FromStart) { // FromStart is only relevant for slicing; indexing from the start does not involve System.Index at all. return; } if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind)) { return; } if (!call.IsDescendantOf(block.Instructions[pos])) return; // startOffsetVar might be used deep inside a complex statement, ensure we can inline up to that point: for (int i = startPos; i < pos; i++) { if (!ILInlining.CanInlineInto(block.Instructions[pos], startOffsetVar, block.Instructions[i])) return; } if (!call.Method.Parameters[0].Type.IsKnownType(KnownTypeCode.Int32)) return; if (!MatchContainerVar(call.Arguments[0], ref containerVar)) return; if (!call.Arguments[1].MatchLdLoc(startOffsetVar)) return; var specialMethods = new IndexMethods(context.TypeSystem); if (!specialMethods.IsValid) return; if (!CSharpWillGenerateIndexer(call.Method.DeclaringType, slicing: false)) return; context.Step($"{call.Method.Name} indexed with {startIndexKind}", call); var newMethod = new SyntheticRangeIndexAccessor(call.Method, specialMethods.IndexType, slicing: false); var newCall = CallInstruction.Create(call.OpCode, newMethod); newCall.ConstrainedTo = call.ConstrainedTo; newCall.ILStackWasEmpty = call.ILStackWasEmpty; newCall.Arguments.Add(call.Arguments[0]); newCall.Arguments.Add(MakeIndex(startIndexKind, startIndexLoad, specialMethods)); newCall.Arguments.AddRange(call.Arguments.Skip(2)); newCall.AddILRange(call); for (int i = startPos; i < pos; i++) { newCall.AddILRange(block.Instructions[i]); } call.ReplaceWith(newCall); block.Instructions.RemoveRange(startPos, pos - startPos); } void TransformSlicing(bool sliceLengthWasMisdetectedAsStartOffset = false) { ILVariable sliceLengthVar; ILInstruction sliceLengthVarInit; if (sliceLengthWasMisdetectedAsStartOffset) { // Special case: when slicing without a start point, the slice length calculation is mis-detected as the start offset, // and since it only has a single use, we end in TransformIndexing(), which then calls TransformSlicing // on this code path. sliceLengthVar = startOffsetVar; sliceLengthVarInit = ((StLoc)sliceLengthVar.StoreInstructions.Single()).Value; startOffsetVar = null; startIndexLoad = new LdcI4(0); startIndexKind = IndexKind.TheStart; } else { // stloc containerLengthVar(call get_Length(ldloc containerVar)) // stloc startOffset(call GetOffset(startIndexLoad, ldloc length)) // -- we are here -- // stloc sliceLengthVar(binary.sub.i4(call GetOffset(endIndexLoad, ldloc length), ldloc startOffset)) // complex_expr(call Slice(ldloc containerVar, ldloc startOffset, ldloc sliceLength)) if (!block.Instructions[pos].MatchStLoc(out sliceLengthVar, out sliceLengthVarInit)) return; pos++; } if (!(sliceLengthVar.IsSingleDefinition && sliceLengthVar.LoadCount == 1)) return; if (!MatchSliceLength(sliceLengthVarInit, out IndexKind endIndexKind, out ILInstruction endIndexLoad, containerLengthVar, ref containerVar, startOffsetVar)) return; if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind, endIndexKind)) { return; } if (rangeVar != null) { return; // this should only ever happen in the second step (ExtendSlicing) } if (!(sliceLengthVar.LoadInstructions.Single().Parent is CallInstruction call)) return; if (!call.IsDescendantOf(block.Instructions[pos])) return; if (!IsSlicingMethod(call.Method)) return; if (call.Arguments.Count != 3) return; if (!MatchContainerVar(call.Arguments[0], ref containerVar)) return; if (startOffsetVar == null) { Debug.Assert(startIndexKind == IndexKind.TheStart); if (!call.Arguments[1].MatchLdcI4(0)) return; } else { if (!call.Arguments[1].MatchLdLoc(startOffsetVar)) return; if (!ILInlining.CanMoveInto(startOffsetVarInit, block.Instructions[pos], call.Arguments[1])) return; } if (!call.Arguments[2].MatchLdLoc(sliceLengthVar)) return; if (!ILInlining.CanMoveInto(sliceLengthVarInit, block.Instructions[pos], call.Arguments[2])) return; if (!CSharpWillGenerateIndexer(call.Method.DeclaringType, slicing: true)) return; var specialMethods = new IndexMethods(context.TypeSystem); if (!specialMethods.IsValid) return; context.Step($"{call.Method.Name} sliced with {startIndexKind}..{endIndexKind}", call); var newMethod = new SyntheticRangeIndexAccessor(call.Method, specialMethods.RangeType, slicing: true); var newCall = CallInstruction.Create(call.OpCode, newMethod); newCall.ConstrainedTo = call.ConstrainedTo; newCall.ILStackWasEmpty = call.ILStackWasEmpty; newCall.Arguments.Add(call.Arguments[0]); newCall.Arguments.Add(MakeRange(startIndexKind, startIndexLoad, endIndexKind, endIndexLoad, specialMethods)); newCall.AddILRange(call); for (int i = startPos; i < pos; i++) { newCall.AddILRange(block.Instructions[i]); } call.ReplaceWith(newCall); block.Instructions.RemoveRange(startPos, pos - startPos); } ILInstruction MakeRange(IndexKind startIndexKind, ILInstruction startIndexLoad, IndexKind endIndexKind, ILInstruction endIndexLoad, IndexMethods specialMethods) { if (rangeVar != null) { return rangeVarInit; } else if (startIndexKind == IndexKind.TheStart && endIndexKind == IndexKind.TheEnd && specialMethods.RangeGetAll != null) { return new Call(specialMethods.RangeGetAll); } else if (startIndexKind == IndexKind.TheStart && specialMethods.RangeEndAt != null) { var rangeCtorCall = new Call(specialMethods.RangeEndAt); rangeCtorCall.Arguments.Add(MakeIndex(endIndexKind, endIndexLoad, specialMethods)); return rangeCtorCall; } else if (endIndexKind == IndexKind.TheEnd && specialMethods.RangeStartAt != null) { var rangeCtorCall = new Call(specialMethods.RangeStartAt); rangeCtorCall.Arguments.Add(MakeIndex(startIndexKind, startIndexLoad, specialMethods)); return rangeCtorCall; } else { var rangeCtorCall = new NewObj(specialMethods.RangeCtor); rangeCtorCall.Arguments.Add(MakeIndex(startIndexKind, startIndexLoad, specialMethods)); rangeCtorCall.Arguments.Add(MakeIndex(endIndexKind, endIndexLoad, specialMethods)); return rangeCtorCall; } } void ExtendSlicing() { // We might be dealing with a situation where we executed TransformSlicing() in a previous run of this transform // that only looked at a part of the instructions making up the slicing pattern. // The first run would have mis-detected slicing from end as slicing from start. // This results in code like: // int length = span.Length; // Console.WriteLine(span[GetIndex(1).GetOffset(length)..GetIndex(2).GetOffset(length)].ToString()); // or: // int length = span.Length; // Range range = GetRange(); // Console.WriteLine(span[range.Start.GetOffset(length)..range.End.GetOffset(length)].ToString()); if (containerLengthVar == null) { return; // need a container length to extend with } Debug.Assert(containerLengthVar.IsSingleDefinition); Debug.Assert(containerLengthVar.LoadCount == 1 || containerLengthVar.LoadCount == 2); NewObj rangeCtorCall = null; foreach (var inst in containerLengthVar.LoadInstructions[0].Ancestors) { if (inst is NewObj newobj && IndexMethods.IsRangeCtor(newobj.Method)) { rangeCtorCall = newobj; break; } if (inst == block) break; } if (rangeCtorCall == null) return; // Now match the pattern that TransformSlicing() generated in the IndexKind.FromStart case if (!(rangeCtorCall.Parent is CallInstruction { Method: SyntheticRangeIndexAccessor _ } slicingCall)) return; if (!MatchContainerVar(slicingCall.Arguments[0], ref containerVar)) return; if (!slicingCall.IsDescendantOf(block.Instructions[pos])) return; Debug.Assert(rangeCtorCall.Arguments.Count == 2); if (!MatchIndexImplicitConv(rangeCtorCall.Arguments[0], out var startOffsetInst)) return; if (!MatchIndexImplicitConv(rangeCtorCall.Arguments[1], out var endOffsetInst)) return; var startIndexKind = MatchGetOffset(startOffsetInst, out var startIndexLoad, containerLengthVar, ref containerVar); var endIndexKind = MatchGetOffset(endOffsetInst, out var endIndexLoad, containerLengthVar, ref containerVar); if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind, endIndexKind)) { return; } // holds because we've used containerLengthVar at least once Debug.Assert(startIndexKind != IndexKind.FromStart || endIndexKind != IndexKind.FromStart); if (rangeVar != null) { if (!ILInlining.CanMoveInto(rangeVarInit, block.Instructions[pos], startIndexLoad)) return; if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start")) return; if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End")) return; } var specialMethods = new IndexMethods(context.TypeSystem); if (!specialMethods.IsValid) return; context.Step("Merge containerLengthVar into slicing", slicingCall); rangeCtorCall.ReplaceWith(MakeRange(startIndexKind, startIndexLoad, endIndexKind, endIndexLoad, specialMethods)); for (int i = startPos; i < pos; i++) { slicingCall.AddILRange(block.Instructions[i]); } block.Instructions.RemoveRange(startPos, pos - startPos); } } private bool MatchIndexImplicitConv(ILInstruction inst, out ILInstruction offsetInst) { offsetInst = null; if (!(inst is CallInstruction call)) return false; if (!(call.Method.IsOperator && call.Method.Name == "op_Implicit")) return false; var op = call.Method; if (!(op.Parameters.Count == 1 && op.Parameters[0].Type.IsKnownType(KnownTypeCode.Int32))) return false; if (!op.DeclaringType.IsKnownType(KnownTypeCode.Index)) return false; offsetInst = call.Arguments.Single(); return true; } static bool IsSlicingMethod(IMethod method) { if (method.IsExtensionMethod) return false; if (method.Parameters.Count != 2) return false; if (!method.Parameters.All(p => p.Type.IsKnownType(KnownTypeCode.Int32))) return false; return method.Name == "Slice" || (method.Name == "Substring" && method.DeclaringType.IsKnownType(KnownTypeCode.String)); } /// /// Check that the number of uses of the containerLengthVar variable matches those expected in the pattern. /// private bool CheckContainerLengthVariableUseCount(ILVariable containerLengthVar, IndexKind startIndexKind, IndexKind endIndexKind = IndexKind.FromStart) { int expectedUses = 0; if (startIndexKind != IndexKind.FromStart && startIndexKind != IndexKind.TheStart) expectedUses += 1; if (endIndexKind != IndexKind.FromStart && endIndexKind != IndexKind.TheStart) expectedUses += 1; if (containerLengthVar != null) { return containerLengthVar.LoadCount == expectedUses; } else { return expectedUses <= 1; // can have one inline use } } /// /// Matches 'addressof System.Index(call get_Start/get_End(ldloca rangeVar))' /// static bool MatchIndexFromRange(IndexKind indexKind, ILInstruction indexLoad, ILVariable rangeVar, string accessorName) { if (indexKind != IndexKind.RefSystemIndex) return false; if (!(indexLoad is AddressOf addressOf)) return false; if (!addressOf.Type.IsKnownType(KnownTypeCode.Index)) return false; if (!(addressOf.Value is Call call)) return false; if (call.Method.Name != accessorName) return false; if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.Range)) return false; if (call.Arguments.Count != 1) return false; return call.Arguments[0].MatchLdLoca(rangeVar); } static ILInstruction MakeIndex(IndexKind indexKind, ILInstruction indexLoad, IndexMethods specialMethods) { if (indexKind == IndexKind.RefSystemIndex) { // stloc containerLengthVar(call get_Length/get_Count(ldloc container)) // stloc startOffsetVar(call GetOffset(startIndexLoad, ldloc length)) // complex_expr(call get_Item(ldloc container, ldloc startOffsetVar)) // --> // complex_expr(call get_Item(ldloc container, ldobj startIndexLoad)) return new LdObj(indexLoad, specialMethods.IndexType); } else if (indexKind == IndexKind.FromEnd || indexKind == IndexKind.TheEnd) { // stloc offsetVar(binary.sub.i4(call get_Length/get_Count(ldloc container), startIndexLoad)) // complex_expr(call get_Item(ldloc container, ldloc startOffsetVar)) // --> // complex_expr(call get_Item(ldloc container, newobj System.Index(startIndexLoad, fromEnd: true))) return new NewObj(specialMethods.IndexCtor) { Arguments = { indexLoad, new LdcI4(1) } }; } else { Debug.Assert(indexKind == IndexKind.FromStart || indexKind == IndexKind.TheStart); return new Call(specialMethods.IndexImplicitConv) { Arguments = { indexLoad } }; } } /// /// Gets whether the C# compiler will call `container[int]` when using `container[Index]`. /// private bool CSharpWillGenerateIndexer(IType declaringType, bool slicing) { bool foundInt32Overload = false; bool foundIndexOverload = false; bool foundRangeOverload = false; bool foundCountProperty = false; foreach (var prop in declaringType.GetProperties(p => p.IsIndexer || (p.Name == "Length" || p.Name == "Count"))) { if (prop.IsIndexer && prop.Parameters.Count == 1) { var p = prop.Parameters[0]; if (p.Type.IsKnownType(KnownTypeCode.Int32)) { foundInt32Overload = true; } else if (p.Type.IsKnownType(KnownTypeCode.Index)) { foundIndexOverload = true; } else if (p.Type.IsKnownType(KnownTypeCode.Range)) { foundRangeOverload = true; } } else if (prop.Name == "Length" || prop.Name == "Count") { foundCountProperty = true; } } if (slicing) { return /* foundSlicingMethod && */ foundCountProperty && !foundRangeOverload; } else { return foundInt32Overload && foundCountProperty && !foundIndexOverload; } } /// /// Matches the instruction: /// stloc containerLengthVar(call get_Length/get_Count(ldloc containerVar)) /// static bool MatchContainerLengthStore(ILInstruction inst, out ILVariable lengthVar, ref ILVariable containerVar) { if (!inst.MatchStLoc(out lengthVar, out var init)) return false; if (!(lengthVar.IsSingleDefinition && lengthVar.StackType == StackType.I4)) return false; if (lengthVar.LoadCount == 0 || lengthVar.LoadCount > 2) return false; return MatchContainerLength(init, null, ref containerVar); } /// /// If lengthVar is non-null, matches 'ldloc lengthVar'. /// /// Otherwise, matches the instruction: /// call get_Length/get_Count(ldloc containerVar) /// static bool MatchContainerLength(ILInstruction init, ILVariable lengthVar, ref ILVariable containerVar) { if (lengthVar != null) { Debug.Assert(containerVar != null); return init.MatchLdLoc(lengthVar); } if (!(init is CallInstruction call)) return false; if (call.ResultType != StackType.I4) return false; if (!(call.Method.IsAccessor && call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter)) return false; if (!(call.Method.AccessorOwner is IProperty lengthProp)) return false; if (lengthProp.Name == "Length") { // OK, Length is preferred } else if (lengthProp.Name == "Count") { // Also works, but only if the type doesn't have "Length" if (lengthProp.DeclaringType.GetProperties(p => p.Name == "Length").Any()) return false; } if (!lengthProp.ReturnType.IsKnownType(KnownTypeCode.Int32)) return false; if (lengthProp.IsVirtual && call.OpCode != OpCode.CallVirt) return false; if (call.Arguments.Count != 1) return false; return MatchContainerVar(call.Arguments[0], ref containerVar); } static bool MatchContainerVar(ILInstruction inst, ref ILVariable containerVar) { if (containerVar != null) { return inst.MatchLdLoc(containerVar) || inst.MatchLdLoca(containerVar); } else { return inst.MatchLdLoc(out containerVar) || inst.MatchLdLoca(out containerVar); } } enum IndexKind { /// /// indexLoad is an integer, from the start of the container /// FromStart, /// /// indexLoad is loading the address of a System.Index struct /// RefSystemIndex, /// /// indexLoad is an integer, from the end of the container /// FromEnd, /// /// Always equivalent to `0`, used for the start-index when slicing without a startpoint `a[..end]` /// TheStart, /// /// Always equivalent to `^0`, used for the end-index when slicing without an endpoint `a[start..]` /// TheEnd, } /// /// Matches an instruction computing an offset: /// call System.Index.GetOffset(indexLoad, ldloc containerLengthVar) /// or /// binary.sub.i4(ldloc containerLengthVar, indexLoad) /// /// Anything else not matching these patterns is interpreted as an `int` expression from the start of the container. /// static IndexKind MatchGetOffset(ILInstruction inst, out ILInstruction indexLoad, ILVariable containerLengthVar, ref ILVariable containerVar) { indexLoad = inst; if (MatchContainerLength(inst, containerLengthVar, ref containerVar)) { indexLoad = new LdcI4(0); return IndexKind.TheEnd; } else if (inst is CallInstruction call) { // call System.Index.GetOffset(indexLoad, ldloc containerLengthVar) if (call.Method.Name != "GetOffset") return IndexKind.FromStart; if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.Index)) return IndexKind.FromStart; if (call.Arguments.Count != 2) return IndexKind.FromStart; if (!MatchContainerLength(call.Arguments[1], containerLengthVar, ref containerVar)) return IndexKind.FromStart; indexLoad = call.Arguments[0]; return IndexKind.RefSystemIndex; } else if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub) { if (bni.CheckForOverflow || bni.ResultType != StackType.I4 || bni.IsLifted) return IndexKind.FromStart; // binary.sub.i4(ldloc containerLengthVar, indexLoad) if (!MatchContainerLength(bni.Left, containerLengthVar, ref containerVar)) return IndexKind.FromStart; indexLoad = bni.Right; return IndexKind.FromEnd; } else { return IndexKind.FromStart; } } /// /// Matches an instruction computing a slice length: /// binary.sub.i4(call GetOffset(endIndexLoad, ldloc length), ldloc startOffset)) /// static bool MatchSliceLength(ILInstruction inst, out IndexKind endIndexKind, out ILInstruction endIndexLoad, ILVariable containerLengthVar, ref ILVariable containerVar, ILVariable startOffsetVar) { endIndexKind = default; endIndexLoad = default; if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub) { if (bni.CheckForOverflow || bni.ResultType != StackType.I4 || bni.IsLifted) return false; if (startOffsetVar == null) { // When slicing without explicit start point: `a[..endIndex]` if (!bni.Right.MatchLdcI4(0)) return false; } else { if (!bni.Right.MatchLdLoc(startOffsetVar)) return false; } endIndexKind = MatchGetOffset(bni.Left, out endIndexLoad, containerLengthVar, ref containerVar); return true; } else if (startOffsetVar == null) { // When slicing without explicit start point: `a[..endIndex]`, the compiler doesn't always emit the "- 0". endIndexKind = MatchGetOffset(inst, out endIndexLoad, containerLengthVar, ref containerVar); return true; } else { return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs ================================================ // Copyright (c) 2025 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. #nullable enable using System.Diagnostics.CodeAnalysis; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { static class InlineArrayTransform { internal static bool RunOnExpression(Call inst, StatementTransformContext context) { if (MatchSpanIndexerWithInlineArrayAsSpan(inst, out var type, out var addr, out var index, out bool isReadOnly)) { if (isReadOnly) { context.Step("call get_Item(addressof System.ReadOnlySpan{T}(call InlineArrayAsReadOnlySpan(addr)), index) -> readonly.ldelema.inlinearray(addr, index)", inst); } else { context.Step("call get_Item(addressof System.Span{T}(call InlineArrayAsSpan(addr)), index) -> ldelema.inlinearray(addr, index)", inst); } inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst)); return true; } if (MatchInlineArrayElementRef(inst, out type, out addr, out index, out isReadOnly)) { if (isReadOnly) { context.Step("call InlineArrayElementRefReadOnly(addr, index) -> readonly.ldelema.inlinearray(addr, index)", inst); } else { context.Step("call InlineArrayElementRef(addr, index) -> ldelema.inlinearray(addr, index)", inst); } inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst)); return true; } if (MatchInlineArrayFirstElementRef(inst, out type, out addr, out isReadOnly)) { if (isReadOnly) { context.Step("call InlineArrayFirstElementRefReadOnly(addr) -> readonly.ldelema.inlinearray(addr, ldc.i4 0)", inst); } else { context.Step("call InlineArrayFirstElementRef(addr) -> ldelema.inlinearray(addr, ldc.i4 0)", inst); } inst.ReplaceWith(new LdElemaInlineArray(type, addr, new LdcI4(0)) { IsReadOnly = isReadOnly }.WithILRange(inst)); return true; } return false; } /// /// Matches call get_Item(addressof System.(ReadOnly)Span[[T]](call InlineArrayAs(ReadOnly)Span(addr, length)), index) /// static bool MatchSpanIndexerWithInlineArrayAsSpan(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly) { isReadOnly = false; type = null; addr = null; index = null; if (MatchSpanGetItem(inst.Method, "ReadOnlySpan")) { isReadOnly = true; if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst]) return false; if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsReadOnlySpan", out var inlineArrayType)) return false; if (targetInst.Arguments is not [var addrInst, LdcI4 { Value: var length }]) return false; if (length < 0 || length > inlineArrayType.GetInlineArrayLength()) return false; type = inlineArrayType; addr = addrInst; index = indexInst; return true; } else if (MatchSpanGetItem(inst.Method, "Span")) { if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst]) return false; if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsSpan", out var inlineArrayType)) return false; if (targetInst.Arguments is not [var addrInst, LdcI4 { Value: var length }]) return false; if (length < 0 || length > inlineArrayType.GetInlineArrayLength()) return false; type = inlineArrayType; addr = addrInst; index = indexInst; return true; } else { return false; } } /// /// Matches call InlineArrayElementRef(ReadOnly)(addr, index) /// static bool MatchInlineArrayElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly) { type = null; addr = null; index = null; isReadOnly = false; if (inst.Arguments is not [var addrInst, LdcI4 { Value: var indexValue } indexInst]) return false; addr = addrInst; index = indexInst; if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRef", out var inlineArrayType)) { isReadOnly = false; type = inlineArrayType; } else if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRefReadOnly", out inlineArrayType)) { isReadOnly = true; type = inlineArrayType; } else { return false; } if (indexValue < 0 || indexValue >= inlineArrayType.GetInlineArrayLength()) { return false; } return true; } private static bool MatchInlineArrayFirstElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, out bool isReadOnly) { type = null; addr = null; isReadOnly = false; if (inst.Arguments is not [var addrInst]) return false; if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRef", out var inlineArrayType)) { isReadOnly = false; type = inlineArrayType; addr = addrInst; return true; } if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRefReadOnly", out inlineArrayType)) { isReadOnly = true; type = inlineArrayType; addr = addrInst; return true; } return false; } static bool MatchSpanGetItem(IMethod method, string typeName) { return method is { IsStatic: false, Name: "get_Item", DeclaringType: { Namespace: "System", Name: string name, TypeParameterCount: 1, DeclaringType: null } } && typeName == name; } static bool MatchInlineArrayHelper(IMethod method, string methodName, [NotNullWhen(true)] out IType? inlineArrayType) { inlineArrayType = null; if (method is not { IsStatic: true, Name: var name, DeclaringType: { FullName: "", TypeParameterCount: 0 }, TypeArguments: [var bufferType, _], Parameters: var parameters }) { return false; } if (methodName != name) return false; if (methodName.Contains("FirstElement")) { if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }]) return false; if (!type.Equals(bufferType)) return false; } else { if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }, { Type: var lengthOrIndexParameterType }]) return false; if (!type.Equals(bufferType) || !lengthOrIndexParameterType.IsKnownType(KnownTypeCode.Int32)) return false; } inlineArrayType = bufferType; return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs ================================================ // Copyright (c) 2017 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; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// This transform duplicates return blocks if they return a local variable that was assigned right before the return. /// class InlineReturnTransform : IILTransform { public void Run(ILFunction function, ILTransformContext context) { var instructionsToModify = new List<(BlockContainer, Block, Branch)>(); // Process all leave instructions in a leave-block, that is a block consisting solely of a leave instruction. foreach (var leave in function.Descendants.OfType()) { if (!(leave.Parent is Block leaveBlock && leaveBlock.Instructions.Count == 1)) continue; // Skip, if the leave instruction has no value or the value is not a load of a local variable. if (!leave.Value.MatchLdLoc(out var returnVar) || returnVar.Kind != VariableKind.Local) continue; // If all instructions can be modified, add item to the global list. if (CanModifyInstructions(returnVar, leaveBlock, out var list)) instructionsToModify.AddRange(list); } foreach (var (container, b, br) in instructionsToModify) { Block block = b; // if there is only one branch to this return block, move it to the matching container. // otherwise duplicate the return block. if (block.IncomingEdgeCount == 1) { block.Remove(); } else { block = (Block)block.Clone(); } container.Blocks.Add(block); // adjust the target of the branch to the newly created block. br.TargetBlock = block; } } /// /// Determines a list of all store instructions that write to a given . /// Returns false if any of these instructions does not meet the following criteria: /// - must be a stloc /// - must be a direct child of a block /// - must be the penultimate instruction /// - must be followed by a branch instruction to /// - must have a BlockContainer as ancestor. /// Returns true, if all instructions meet these criteria, and contains a list of 3-tuples. /// Each tuple consists of the target block container, the leave block, and the branch instruction that should be modified. /// static bool CanModifyInstructions(ILVariable returnVar, Block leaveBlock, out List<(BlockContainer, Block, Branch)> instructionsToModify) { instructionsToModify = new List<(BlockContainer, Block, Branch)>(); foreach (var inst in returnVar.StoreInstructions) { if (!(inst is StLoc store)) return false; if (!(store.Parent is Block storeBlock)) return false; if (store.ChildIndex + 2 != storeBlock.Instructions.Count) return false; if (!(storeBlock.Instructions[store.ChildIndex + 1] is Branch br)) return false; if (br.TargetBlock != leaveBlock) return false; var targetBlockContainer = BlockContainer.FindClosestContainer(store); if (targetBlockContainer == null) return false; instructionsToModify.Add((targetBlockContainer, leaveBlock, br)); } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/InterpolatedStringTransform.cs ================================================ // Copyright (c) 2021 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class InterpolatedStringTransform : IStatementTransform { void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.StringInterpolation) return; int interpolationStart = pos; int interpolationEnd; ILInstruction insertionPoint; // stloc v(newobj DefaultInterpolatedStringHandler..ctor(ldc.i4 literalLength, ldc.i4 formattedCount)) if (block.Instructions[pos] is StLoc { Variable: ILVariable { Kind: VariableKind.Local } v, Value: NewObj { Arguments: { Count: 2 } } newObj } stloc && v.Type.IsKnownType(KnownTypeCode.DefaultInterpolatedStringHandler) && newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.DefaultInterpolatedStringHandler) && newObj.Arguments[0].MatchLdcI4(out _) && newObj.Arguments[1].MatchLdcI4(out _)) { // { call MethodName(ldloca v, ...) } do { pos++; } while (IsKnownCall(block, pos, v)); interpolationEnd = pos; // ... call ToStringAndClear(ldloca v) ... if (!FindToStringAndClear(block, pos, interpolationStart, interpolationEnd, v, out insertionPoint)) { return; } if (!(v.StoreCount == 1 && v.AddressCount == interpolationEnd - interpolationStart && v.LoadCount == 0)) { return; } } else { return; } context.Step($"Transform DefaultInterpolatedStringHandler {v.Name}", stloc); v.Kind = VariableKind.InitializerTarget; var replacement = new Block(BlockKind.InterpolatedString); for (int i = interpolationStart; i < interpolationEnd; i++) { replacement.Instructions.Add(block.Instructions[i]); } var callToStringAndClear = insertionPoint; insertionPoint.ReplaceWith(replacement); replacement.FinalInstruction = callToStringAndClear; block.Instructions.RemoveRange(interpolationStart, interpolationEnd - interpolationStart); } private bool IsKnownCall(Block block, int pos, ILVariable v) { if (pos >= block.Instructions.Count - 1) return false; if (!(block.Instructions[pos] is Call call)) return false; if (!(call.Arguments.Count > 1)) return false; if (!call.Arguments[0].MatchLdLoca(v)) return false; if (call.Method.IsStatic) return false; if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.DefaultInterpolatedStringHandler)) return false; switch (call.Method.Name) { case "AppendLiteral" when call.Arguments.Count == 2 && call.Arguments[1] is LdStr: case "AppendFormatted" when call.Arguments.Count == 2: case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdStr: case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdcI4: case "AppendFormatted" when call.Arguments.Count == 4 && call.Arguments[2] is LdcI4 && call.Arguments[3] is LdStr: break; default: return false; } return true; } private bool FindToStringAndClear(Block block, int pos, int interpolationStart, int interpolationEnd, ILVariable v, out ILInstruction insertionPoint) { insertionPoint = null; if (pos >= block.Instructions.Count) return false; // find // ... call ToStringAndClear(ldloca v) ... // in block.Instructions[pos] for (int i = interpolationStart; i < interpolationEnd; i++) { var result = ILInlining.FindLoadInNext(block.Instructions[pos], v, block.Instructions[i], InliningOptions.None); if (result.Type != ILInlining.FindResultType.Found) return false; insertionPoint ??= result.LoadInst.Parent; Debug.Assert(insertionPoint == result.LoadInst.Parent); } return insertionPoint is Call { Arguments: { Count: 1 }, Method: { Name: "ToStringAndClear", IsStatic: false } }; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs ================================================ // 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; using System.Text; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public class IntroduceDynamicTypeOnLocals : IILTransform { public void Run(ILFunction function, ILTransformContext context) { foreach (var nestedFunction in function.Descendants.OfType()) { HashSet dynamicVariables = new(); foreach (var variable in nestedFunction.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; if (variable.Index.HasValue && variable.Kind == VariableKind.Local) { dynamicVariables.Add(variable.Index.Value); } break; } } } } foreach (var variable in nestedFunction.Variables) { if (variable.Index.HasValue && variable.Kind == VariableKind.Local && dynamicVariables.Contains(variable.Index.Value)) { variable.Type = SpecialType.Dynamic; continue; } } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/IntroduceNativeIntTypeOnLocals.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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; using System.Text; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { class IntroduceNativeIntTypeOnLocals : IILTransform { public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.NativeIntegers) return; foreach (var nestedFunction in function.Descendants.OfType()) { Dictionary variableTypeMapping = new(); foreach (var variable in nestedFunction.Variables) { if (variable.Kind != VariableKind.Local && variable.Kind != VariableKind.StackSlot && variable.Kind != VariableKind.PatternLocal && variable.Kind != VariableKind.ForeachLocal && variable.Kind != VariableKind.UsingLocal) { continue; } if (!(variable.Type.IsKnownType(KnownTypeCode.IntPtr) || variable.Type.IsKnownType(KnownTypeCode.UIntPtr))) continue; bool isUsedAsNativeInt = variable.LoadInstructions.Any(IsUsedAsNativeInt); bool isAssignedNativeInt = variable.StoreInstructions.Any(store => IsNativeIntStore(store, context.TypeSystem)); if (isUsedAsNativeInt || isAssignedNativeInt) { variable.Type = variable.Type.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; if (variable.Kind == VariableKind.Local && variable.Index.HasValue) variableTypeMapping[variable.Index.Value] = variable.Type; } } foreach (var variable in nestedFunction.Variables) { if (variable.Kind == VariableKind.Local && variable.Index.HasValue && variableTypeMapping.TryGetValue(variable.Index.Value, out var type)) { variable.Type = type; } } } } static bool IsUsedAsNativeInt(LdLoc load) { return load.Parent switch { BinaryNumericInstruction { UnderlyingResultType: StackType.I } => true, BitNot { UnderlyingResultType: StackType.I } => true, CallInstruction call => call.GetParameter(load.ChildIndex)?.Type.IsCSharpNativeIntegerType() ?? false, _ => false, }; } static bool IsNativeIntStore(IStoreInstruction store, ICompilation compilation) { if (store is StLoc stloc) { switch (stloc.Value) { case BinaryNumericInstruction { UnderlyingResultType: StackType.I }: return true; case Conv { ResultType: StackType.I }: return true; default: var inferredType = stloc.Value.InferType(compilation); return inferredType.IsCSharpNativeIntegerType(); } } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs ================================================ // 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; using System.Text; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { public class IntroduceRefReadOnlyModifierOnLocals : IILTransform { public void Run(ILFunction function, ILTransformContext context) { foreach (var variable in function.Variables) { if (variable.Type.Kind != TypeKind.ByReference || variable.Kind == VariableKind.Parameter) continue; // ref readonly if (IsUsedAsRefReadonly(variable)) { variable.IsRefReadOnly = true; continue; } } } /// /// Infer ref readonly type from usage: /// An ILVariable should be marked as readonly, /// if it's a "by-ref-like" type and the initialized value is known to be readonly. /// bool IsUsedAsRefReadonly(ILVariable variable) { foreach (var store in variable.StoreInstructions.OfType()) { // Check if C# requires that the local is ref-readonly in order to allow the store: if (ILInlining.IsReadonlyReference(store.Value)) return true; // Check whether the local needs to be ref-readonly to avoid changing the semantics of // a readonly.ldelema: ILInstruction val = store.Value; while (val is LdFlda ldflda) { val = ldflda.Target; } if (val is LdElema { IsReadOnly: true }) return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/LdLocaDupInitObjTransform.cs ================================================ // Copyright (c) 2021 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { // stloc s(ldloca v) // stobj System.DateTime(ldloc s, default.value T) // where s.LoadCount > 1 // which is: ldloca; dup; initobj in IL (generated by Roslyn >= 2) // => // stloc v(default.value T) // stloc s(ldloca v) // which is: ldloca; ldloca; initobj in IL (generated by legacy csc) // // The second pattern allows inlining in the subsequent uses. class LdLocaDupInitObjTransform : IILTransform { void IILTransform.Run(ILFunction function, ILTransformContext context) { foreach (var block in function.Descendants.OfType()) { for (int i = 0; i < block.Instructions.Count; i++) { TryTransform(block, i, context); } } } private bool TryTransform(Block block, int i, ILTransformContext context) { if (block.Instructions[i] is not StLoc { Variable: var s, Value: LdLoca { Variable: var v } } inst1) { return false; } if (block.Instructions.ElementAtOrDefault(i + 1) is not StObj inst2) { return false; } if (!(inst2.Target.MatchLdLoc(s) && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst2.Type) && inst2.UnalignedPrefix == 0 && !inst2.IsVolatile && inst2.Value is DefaultValue)) { return false; } context.Step("LdLocaDupInitObjTransform", inst1); block.Instructions[i] = new StLoc(v, inst2.Value).WithILRange(inst2); block.Instructions[i + 1] = inst1; return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs ================================================ // 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 // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Text.RegularExpressions; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Decompiler step for C# 7.0 local functions /// public class LocalFunctionDecompiler : IILTransform { ILTransformContext context; ITypeResolveContext resolveContext; struct LocalFunctionInfo { public List UseSites; public IMethod Method; public ILFunction Definition; /// /// Used to store all synthesized call-site arguments grouped by the parameter index. /// We use a dictionary instead of a simple array, because -1 is used for the this parameter /// and there might be many non-synthesized arguments in between. /// public Dictionary> LocalFunctionArguments; } /// /// The transform works like this: /// /// /// local functions can either be used in method calls, i.e., call and callvirt instructions, /// or can be used as part of the "delegate construction" pattern, i.e., /// newobj Delegate(<target-expression>, ldftn <method>). /// /// As local functions can be declared practically anywhere, we have to take a look at /// all use-sites and infer the declaration location from that. Use-sites can be call, /// callvirt and ldftn instructions. /// After all use-sites are collected we construct the ILAst of the local function /// and add it to the parent function. /// Then all use-sites of the local-function are transformed to a call to the /// LocalFunctionMethod or a ldftn of the LocalFunctionMethod. /// In a next step we handle all nested local functions. /// After all local functions are transformed, we move all local functions that capture /// any variables to their respective declaration scope. /// public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LocalFunctions) return; // Disable the transform if we are decompiling a display-class or local function method: // This happens if a local function or display class is selected in the ILSpy tree view. if (IsLocalFunctionMethod(function.Method, context) || IsLocalFunctionDisplayClass( function.Method.ParentModule.MetadataFile, (TypeDefinitionHandle)function.Method.DeclaringTypeDefinition.MetadataToken, context) ) { return; } this.context = context; this.resolveContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary(); // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. FindUseSites(function, context, localFunctions); ReplaceReferencesToDisplayClassThis(localFunctions.Values); DetermineCaptureAndDeclarationScopes(localFunctions.Values); PropagateClosureParameterArguments(localFunctions); TransformUseSites(localFunctions.Values); } private void ReplaceReferencesToDisplayClassThis(Dictionary.ValueCollection localFunctions) { foreach (var info in localFunctions) { var localFunction = info.Definition; if (localFunction.Method.IsStatic) continue; var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis); if (thisVar == null) continue; var compatibleArgument = FindCompatibleArgument( info, info.UseSites.OfType().Select(u => u.Arguments[0]).ToArray(), ignoreStructure: true ); if (compatibleArgument == null) continue; context.Step($"Replace 'this' with {compatibleArgument}", localFunction); localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(compatibleArgument, thisVar)); DetermineCaptureAndDeclarationScope(info, -1, compatibleArgument); } } private void DetermineCaptureAndDeclarationScopes(Dictionary.ValueCollection localFunctions) { foreach (var info in localFunctions) { context.CancellationToken.ThrowIfCancellationRequested(); if (info.Definition == null) { context.Function.Warnings.Add($"Could not decode local function '{info.Method}'"); continue; } context.StepStartGroup($"Determine and move to declaration scope of " + info.Definition.Name, info.Definition); try { var localFunction = info.Definition; foreach (var useSite in info.UseSites) { DetermineCaptureAndDeclarationScope(info, useSite); if (context.Function.Method.IsConstructor) { if (localFunction.DeclarationScope == null) { localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite); } else { localFunction.DeclarationScope = FindCommonAncestorInstruction(useSite, localFunction.DeclarationScope); if (localFunction.DeclarationScope == null) { localFunction.DeclarationScope = (BlockContainer)context.Function.Body; } } } } if (localFunction.DeclarationScope == null) { localFunction.DeclarationScope = (BlockContainer)context.Function.Body; } ILFunction declaringFunction = GetDeclaringFunction(localFunction); if (declaringFunction != context.Function) { context.Step($"Move {localFunction.Name} from {context.Function.Name} to {declaringFunction.Name}", localFunction); context.Function.LocalFunctions.Remove(localFunction); declaringFunction.LocalFunctions.Add(localFunction); } if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters) { Debug.Assert(false); context.Function.Warnings.Add($"Could not decode local function '{info.Method}'"); if (declaringFunction != context.Function) { declaringFunction.LocalFunctions.Remove(localFunction); } } } finally { context.StepEndGroup(keepIfEmpty: true); } } } private void TransformUseSites(Dictionary.ValueCollection localFunctions) { foreach (var info in localFunctions) { context.CancellationToken.ThrowIfCancellationRequested(); if (info.Definition == null) continue; context.StepStartGroup($"TransformUseSites of " + info.Definition.Name, info.Definition); try { foreach (var useSite in info.UseSites) { context.Step($"Transform use-site at IL_{useSite.StartILOffset:x4}", useSite); switch (useSite) { case NewObj newObj: TransformToLocalFunctionReference(info.Definition, newObj); break; case CallInstruction call: TransformToLocalFunctionInvocation(info.Definition.ReducedMethod, call); break; case LdFtn fnptr: var specializeMethod = info.Definition.ReducedMethod .Specialize(fnptr.Method.Substitution); var replacement = new LdFtn(specializeMethod).WithILRange(fnptr); fnptr.ReplaceWith(replacement); break; default: throw new NotSupportedException(); } } } finally { context.StepEndGroup(); } } } private void PropagateClosureParameterArguments(Dictionary localFunctions) { foreach (var localFunction in context.Function.Descendants.OfType()) { if (localFunction.Kind != ILFunctionKind.LocalFunction) continue; context.CancellationToken.ThrowIfCancellationRequested(); var token = (MethodDefinitionHandle)localFunction.Method.MetadataToken; var info = localFunctions[token]; foreach (var useSite in info.UseSites) { switch (useSite) { case NewObj newObj: AddAsArgument(-1, newObj.Arguments[0]); break; case CallInstruction call: int firstArgumentIndex; if (info.Method.IsStatic) { firstArgumentIndex = 0; } else { firstArgumentIndex = 1; AddAsArgument(-1, call.Arguments[0]); } for (int i = call.Arguments.Count - 1; i >= firstArgumentIndex; i--) { AddAsArgument(i - firstArgumentIndex, call.Arguments[i]); } break; case LdFtn _: // &LocalFunction is only possible, if the local function is declared static, // this means that the local function can be declared in the top-level scope. // Thus, there are no closure parameters that need propagation. break; default: throw new NotSupportedException(); } } context.StepStartGroup($"PropagateClosureParameterArguments of " + info.Definition.Name, info.Definition); try { foreach (var (index, arguments) in info.LocalFunctionArguments) { var targetVariable = info.Definition.Variables.SingleOrDefault(p => p.Kind == VariableKind.Parameter && p.Index == index); if (targetVariable == null) continue; var compatibleArgument = FindCompatibleArgument(info, arguments); if (compatibleArgument == null) continue; context.Step($"Replace '{targetVariable}' with '{compatibleArgument}'", info.Definition); info.Definition.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(compatibleArgument, targetVariable)); } } finally { context.StepEndGroup(keepIfEmpty: true); } void AddAsArgument(int index, ILInstruction argument) { switch (argument) { case LdLoc _: case LdLoca _: case LdFlda _: case LdObj _: if (index >= 0 && !IsClosureParameter(info.Method.Parameters[index], resolveContext)) return; break; default: if (index >= 0 && IsClosureParameter(info.Method.Parameters[index], resolveContext)) info.Definition.Warnings.Add("Could not transform parameter " + index + ": unsupported argument pattern"); return; } if (!info.LocalFunctionArguments.TryGetValue(index, out var arguments)) { arguments = new List(); info.LocalFunctionArguments.Add(index, arguments); } arguments.Add(argument); } } } private ILInstruction FindCompatibleArgument(LocalFunctionInfo info, IList arguments, bool ignoreStructure = false) { foreach (var arg in arguments) { if (arg is IInstructionWithVariableOperand ld2 && (ignoreStructure || info.Definition.IsDescendantOf(ld2.Variable.Function))) return arg; var v = ResolveAncestorScopeReference(arg); if (v != null) return new LdLoc(v); } return null; } private ILVariable ResolveAncestorScopeReference(ILInstruction inst) { if (!inst.MatchLdFld(out var target, out var field)) return null; if (field.Type.Kind != TypeKind.Class) return null; if (!(TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition()) || context.Function.Method.DeclaringType.Equals(field.Type))) return null; foreach (var v in context.Function.Descendants.OfType().SelectMany(f => f.Variables)) { if (!(TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type))) continue; return v; } return null; } private ILFunction GetDeclaringFunction(ILFunction localFunction) { if (localFunction.DeclarationScope == null) return null; ILInstruction inst = localFunction.DeclarationScope; while (inst != null) { if (inst is ILFunction declaringFunction) return declaringFunction; inst = inst.Parent; } return null; } bool TryValidateSkipCount(LocalFunctionInfo info, out int skipCount) { skipCount = 0; var localFunction = info.Definition; if (localFunction.Method.TypeParameters.Count == 0) return true; var parentMethod = ((ILFunction)localFunction.Parent).Method; var method = localFunction.Method; skipCount = parentMethod.DeclaringType.TypeParameterCount - method.DeclaringType.TypeParameterCount; if (skipCount > 0) return false; skipCount += parentMethod.TypeParameters.Count; if (skipCount < 0 || skipCount > method.TypeArguments.Count) return false; if (skipCount > 0) { #if DEBUG foreach (var useSite in info.UseSites) { var callerMethod = useSite.Ancestors.OfType().First().Method; callerMethod = callerMethod.ReducedFrom ?? callerMethod; IMethod m; switch (useSite) { case NewObj newObj: m = ((LdFtn)newObj.Arguments[1]).Method; break; case CallInstruction call: m = call.Method; break; case LdFtn fnptr: m = fnptr.Method; break; default: throw new NotSupportedException(); } var totalSkipCount = skipCount + m.DeclaringType.TypeParameterCount; var methodSkippedArgs = m.DeclaringType.TypeArguments.Concat(m.TypeArguments).Take(totalSkipCount); Debug.Assert(methodSkippedArgs.SequenceEqual(callerMethod.DeclaringType.TypeArguments.Concat(callerMethod.TypeArguments).Take(totalSkipCount))); Debug.Assert(methodSkippedArgs.All(p => p.Kind == TypeKind.TypeParameter)); Debug.Assert(methodSkippedArgs.Select(p => p.Name).SequenceEqual(m.DeclaringType.TypeParameters.Concat(m.TypeParameters).Take(totalSkipCount).Select(p => p.Name))); } #endif } return true; } void FindUseSites(ILFunction function, ILTransformContext context, Dictionary localFunctions) { foreach (var inst in function.Body.Descendants) { context.CancellationToken.ThrowIfCancellationRequested(); if (inst is CallInstruction call && !call.Method.IsLocalFunction && IsLocalFunctionMethod(call.Method, context)) { HandleUseSite(call.Method, call); } else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && IsLocalFunctionMethod(ldftn.Method, context)) { if (ldftn.Parent is NewObj newObj && DelegateConstruction.MatchDelegateConstruction(newObj, out _, out _, out _)) HandleUseSite(ldftn.Method, newObj); else HandleUseSite(ldftn.Method, ldftn); } } void HandleUseSite(IMethod targetMethod, ILInstruction inst) { if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) { context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst); info = new LocalFunctionInfo() { UseSites = new List() { inst }, LocalFunctionArguments = new Dictionary>(), Method = (IMethod)targetMethod.MemberDefinition, }; var rootFunction = context.Function; int skipCount = GetSkipCount(rootFunction, targetMethod); info.Definition = ReadLocalFunctionDefinition(rootFunction, targetMethod, skipCount); localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info); if (info.Definition != null) { FindUseSites(info.Definition, context, localFunctions); } context.StepEndGroup(); } else { info.UseSites.Add(inst); } } } ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod, int skipCount) { var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); var genericContext = GenericContextFromTypeArguments(targetMethod, skipCount); if (genericContext == null) return null; ILFunction function; bool hasBody = methodDefinition.HasBody(); if (!hasBody) { function = new ILFunction(targetMethod, 0, new TypeSystem.GenericContext(genericContext?.ClassTypeParameters, genericContext?.MethodTypeParameters), new Nop(), ILFunctionKind.LocalFunction); } else { var ilReader = context.CreateILReader(); var body = context.PEFile.GetMethodBody(methodDefinition.RelativeVirtualAddress); function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.GetValueOrDefault(), ILFunctionKind.LocalFunction, context.CancellationToken); } // Embed the local function into the parent function's ILAst, so that "Show steps" can show // how the local function body is being transformed. rootFunction.LocalFunctions.Add(function); if (hasBody) { function.DeclarationScope = (BlockContainer)rootFunction.Body; function.CheckInvariant(ILPhase.Normal); var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); function.DeclarationScope = null; } function.ReducedMethod = ReduceToLocalFunction(function.Method, skipCount); return function; } int GetSkipCount(ILFunction rootFunction, IMethod targetMethod) { targetMethod = (IMethod)targetMethod.MemberDefinition; var skipCount = rootFunction.Method.DeclaringType.TypeParameters.Count + rootFunction.Method.TypeParameters.Count - targetMethod.DeclaringType.TypeParameters.Count; if (skipCount < 0) { skipCount = 0; } if (targetMethod.TypeParameters.Count > 0) { var lastParams = targetMethod.Parameters.Where(p => IsClosureParameter(p, this.resolveContext)).SelectMany(p => p.Type.UnwrapByRef().TypeArguments) .Select(pt => (int?)targetMethod.TypeParameters.IndexOf(pt)).DefaultIfEmpty().Max(); if (lastParams != null && lastParams.GetValueOrDefault() + 1 > skipCount) skipCount = lastParams.GetValueOrDefault() + 1; } return skipCount; } static TypeSystem.GenericContext? GenericContextFromTypeArguments(IMethod targetMethod, int skipCount) { if (skipCount < 0 || skipCount > targetMethod.TypeParameters.Count) { Debug.Assert(false); return null; } int total = targetMethod.DeclaringType.TypeParameters.Count + skipCount; if (total == 0) return default(TypeSystem.GenericContext); var classTypeParameters = new List(targetMethod.DeclaringType.TypeParameters); var methodTypeParameters = new List(targetMethod.TypeParameters); var skippedTypeArguments = targetMethod.DeclaringType.TypeArguments.Concat(targetMethod.TypeArguments).Take(total); int idx = 0; foreach (var skippedTA in skippedTypeArguments) { int curIdx; List curParameters; IReadOnlyList curArgs; if (idx < classTypeParameters.Count) { curIdx = idx; curParameters = classTypeParameters; curArgs = targetMethod.DeclaringType.TypeArguments; } else { curIdx = idx - classTypeParameters.Count; curParameters = methodTypeParameters; curArgs = targetMethod.TypeArguments; } if (curArgs[curIdx].Kind != TypeKind.TypeParameter) break; curParameters[curIdx] = (ITypeParameter)skippedTA; idx++; } if (idx != total) { Debug.Assert(false); return null; } return new TypeSystem.GenericContext(classTypeParameters, methodTypeParameters); } static T FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) where T : ILInstruction { var ancestorsOfB = new HashSet(b.Ancestors.OfType()); return a.Ancestors.OfType().FirstOrDefault(ancestorsOfB.Contains); } internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context) { return IsClosureParameter(parameter, context.CurrentTypeDefinition); } internal static bool IsClosureParameter(IParameter parameter, ITypeDefinition currentTypeDefinition) { if (parameter.Type is not ByReferenceType brt) return false; var type = brt.ElementType.GetDefinition(); return type != null && type.Kind == TypeKind.Struct && TransformDisplayClassUsage.IsPotentialClosure(currentTypeDefinition, type); } LocalFunctionMethod ReduceToLocalFunction(IMethod method, int typeParametersToRemove) { int parametersToRemove = 0; for (int i = method.Parameters.Count - 1; i >= 0; i--) { if (!IsClosureParameter(method.Parameters[i], resolveContext)) break; parametersToRemove++; } return new LocalFunctionMethod(method, method.Name, CanBeStaticLocalFunction(), parametersToRemove, typeParametersToRemove); bool CanBeStaticLocalFunction() { if (!context.Settings.StaticLocalFunctions) return false; // Cannot be static because there are closure parameters that will be removed if (parametersToRemove > 0) return false; // no closure parameters, but static: // we can safely assume, this local function can be declared static if (method.IsStatic) return true; // the local function is used in conjunction with a lambda, which means, // it is defined inside the display-class type var declaringType = method.DeclaringTypeDefinition; if (!declaringType.IsCompilerGenerated()) return false; // if there are no instance fields, we can make it a static local function return !declaringType.GetFields(f => !f.IsStatic).Any(); } } static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) { ILInstruction target = useSite.Arguments[0]; target.ReplaceWith(new LdNull().WithILRange(target)); if (target is IInstructionWithVariableOperand withVar && withVar.Variable.Kind == VariableKind.Local) { withVar.Variable.Kind = VariableKind.DisplayClassLocal; } var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1]; var specializeMethod = function.ReducedMethod.Specialize(fnptr.Method.Substitution); var replacement = new LdFtn(specializeMethod).WithILRange((ILInstruction)fnptr); useSite.Arguments[1].ReplaceWith(replacement); } void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite) { var specializeMethod = reducedMethod.Specialize(useSite.Method.Substitution); bool wasInstanceCall = !useSite.Method.IsStatic; var replacement = new Call(specializeMethod); int firstArgumentIndex = wasInstanceCall ? 1 : 0; int argumentCount = useSite.Arguments.Count; int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount)); // copy flags replacement.ConstrainedTo = useSite.ConstrainedTo; replacement.ILStackWasEmpty = useSite.ILStackWasEmpty; replacement.IsTail = useSite.IsTail; // copy IL ranges replacement.AddILRange(useSite); if (wasInstanceCall) { replacement.AddILRange(useSite.Arguments[0]); if (useSite.Arguments[0].MatchLdLocRef(out var variable) && variable.Kind == VariableKind.NamedArgument) { // remove the store instruction of the simple load, if it is a named argument. var storeInst = (ILInstruction)variable.StoreInstructions[0]; ((Block)storeInst.Parent).Instructions.RemoveAt(storeInst.ChildIndex); } } for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]); } useSite.ReplaceWith(replacement); } void DetermineCaptureAndDeclarationScope(LocalFunctionInfo info, ILInstruction useSite) { switch (useSite) { case CallInstruction call: if (DelegateConstruction.MatchDelegateConstruction(useSite, out _, out _, out _)) { // if this is a delegate construction, skip the use-site, because the capture scope // was already determined when analyzing "this". break; } int firstArgumentIndex = info.Definition.Method.IsStatic ? 0 : 1; for (int i = call.Arguments.Count - 1; i >= firstArgumentIndex; i--) { if (!DetermineCaptureAndDeclarationScope(info, i - firstArgumentIndex, call.Arguments[i])) break; } if (firstArgumentIndex > 0) { DetermineCaptureAndDeclarationScope(info, -1, call.Arguments[0]); } break; case LdFtn _: // &LocalFunction is only possible, if the local function is declared static, // this means that the local function can be declared in the top-level scope. // leave info.DeclarationScope null/unassigned. break; default: throw new NotSupportedException(); } } bool DetermineCaptureAndDeclarationScope(LocalFunctionInfo info, int parameterIndex, ILInstruction arg) { ILFunction function = info.Definition; ILVariable closureVar; if (parameterIndex >= 0) { if (!(parameterIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[parameterIndex], resolveContext))) { return false; } } if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) { closureVar = ResolveAncestorScopeReference(arg); if (closureVar == null) return false; } if (closureVar.Kind == VariableKind.NamedArgument) return false; var initializer = GetClosureInitializer(closureVar); if (initializer == null) return false; // determine the capture scope of closureVar and the declaration scope of the function var additionalScope = BlockContainer.FindClosestContainer(initializer); if (closureVar.CaptureScope == null) closureVar.CaptureScope = additionalScope; else { BlockContainer combinedScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); Debug.Assert(combinedScope != null); closureVar.CaptureScope = combinedScope; } if (closureVar.Kind == VariableKind.Local) { closureVar.Kind = VariableKind.DisplayClassLocal; } if (function.DeclarationScope == null) function.DeclarationScope = closureVar.CaptureScope; else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType().First())) function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); return true; ILInstruction GetClosureInitializer(ILVariable variable) { var type = variable.Type.UnwrapByRef().GetDefinition(); if (type == null) return null; if (variable.Kind == VariableKind.Parameter) return null; if (type.Kind == TypeKind.Struct) return Block.GetContainingStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); else return (StLoc)variable.StoreInstructions[0]; } } bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function) { return TreeTraversal.PreOrder(function, f => f.LocalFunctions).Any(f => declarationScope.IsDescendantOf(f.Body)); } internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context) { if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) return false; var opCode = inst.Arguments[1].OpCode; return opCode == OpCode.LdFtn && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context); } public static bool IsLocalFunctionMethod(IMethod method, ILTransformContext context) { if (method.MetadataToken.IsNil) return false; return IsLocalFunctionMethod(method.ParentModule.MetadataFile, (MethodDefinitionHandle)method.MetadataToken, context); } public static bool IsLocalFunctionMethod(MetadataFile module, MethodDefinitionHandle methodHandle, ILTransformContext context = null) { if (context != null && context.PEFile != module) return false; var metadata = module.Metadata; var method = metadata.GetMethodDefinition(methodHandle); var declaringType = method.GetDeclaringType(); if ((method.Attributes & MethodAttributes.Assembly) == 0 || !(method.IsCompilerGenerated(metadata) || declaringType.IsCompilerGenerated(metadata))) return false; if (!ParseLocalFunctionName(metadata.GetString(method.Name), out _, out _)) return false; return true; } public static bool LocalFunctionNeedsAccessibilityChange(MetadataFile module, MethodDefinitionHandle methodHandle) { if (!IsLocalFunctionMethod(module, methodHandle)) return false; var metadata = module.Metadata; var method = metadata.GetMethodDefinition(methodHandle); FindRefStructParameters visitor = new FindRefStructParameters(); method.DecodeSignature(visitor, default); foreach (var h in visitor.RefStructTypes) { var td = metadata.GetTypeDefinition(h); if (td.IsCompilerGenerated(metadata) && td.IsValueType(metadata)) return true; } return false; } public static bool IsLocalFunctionDisplayClass(MetadataFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null) { if (context != null && context.PEFile != module) return false; var metadata = module.Metadata; var type = metadata.GetTypeDefinition(typeHandle); if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPrivate) return false; if (!type.HasGeneratedName(metadata)) return false; var declaringTypeHandle = type.GetDeclaringType(); var declaringType = metadata.GetTypeDefinition(declaringTypeHandle); foreach (var method in declaringType.GetMethods()) { if (!IsLocalFunctionMethod(module, method, context)) continue; var md = metadata.GetMethodDefinition(method); if (md.DecodeSignature(new FindTypeDecoder(typeHandle, module), default).ParameterTypes.Any()) return true; } return false; } /// /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" /// Older versions use "<callerName>g__functionNamex_y" /// static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); internal static bool ParseLocalFunctionName(string name, out string callerName, out string functionName) { callerName = null; functionName = null; if (string.IsNullOrWhiteSpace(name)) return false; var match = functionNameRegex.Match(name); callerName = match.Groups[1].Value; functionName = match.Groups[2].Value; return match.Success; } class FindRefStructParameters : ISignatureTypeProvider { public readonly List RefStructTypes = new List(); public TypeDefinitionHandle GetArrayType(TypeDefinitionHandle elementType, ArrayShape shape) => default; public TypeDefinitionHandle GetFunctionPointerType(MethodSignature signature) => default; public TypeDefinitionHandle GetGenericInstantiation(TypeDefinitionHandle genericType, ImmutableArray typeArguments) => default; public TypeDefinitionHandle GetGenericMethodParameter(Unit genericContext, int index) => default; public TypeDefinitionHandle GetGenericTypeParameter(Unit genericContext, int index) => default; public TypeDefinitionHandle GetModifiedType(TypeDefinitionHandle modifier, TypeDefinitionHandle unmodifiedType, bool isRequired) => default; public TypeDefinitionHandle GetPinnedType(TypeDefinitionHandle elementType) => default; public TypeDefinitionHandle GetPointerType(TypeDefinitionHandle elementType) => default; public TypeDefinitionHandle GetPrimitiveType(PrimitiveTypeCode typeCode) => default; public TypeDefinitionHandle GetSZArrayType(TypeDefinitionHandle elementType) => default; public TypeDefinitionHandle GetByReferenceType(TypeDefinitionHandle elementType) { if (!elementType.IsNil) RefStructTypes.Add(elementType); return elementType; } public TypeDefinitionHandle GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) => default; public TypeDefinitionHandle GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => handle; public TypeDefinitionHandle GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) => default; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs ================================================ // Copyright (c) 2017 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class LockTransform : IBlockTransform { BlockTransformContext context; void IBlockTransform.Run(Block block, BlockTransformContext context) { if (!context.Settings.LockStatement) return; this.context = context; for (int i = context.IndexOfFirstAlreadyTransformedInstruction - 1; i >= 0; i--) { bool changed = DoTransform(block, i); if (changed) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; } // This happens in some cases: // Use correct index after transformation. if (i >= block.Instructions.Count) i = block.Instructions.Count; } bool DoTransform(Block block, int i) { if (TransformLockRoslyn(block, i)) { return true; } if (TransformLockV4(block, i)) { return true; } if (TransformLockV4YieldReturn(block, i)) { return true; } if (TransformLockV2(block, i)) { return true; } return TransformLockMCS(block, i); } } /// /// stloc lockObj(lockExpression) /// call Enter(ldloc lockObj) /// .try BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// /// } finally BlockContainer { /// Block exitBlock (incoming: 1) { /// call Exit(ldloc lockObj) /// leave exitBlock (nop) /// } /// /// } /// => /// .lock (lockExpression) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// bool TransformLockMCS(Block block, int i) { if (i < 2) return false; if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 2] is StLoc objectStore) || !MatchCall(block.Instructions[i - 1] as Call, "Enter", objectStore.Variable)) return false; if (!objectStore.Variable.IsSingleDefinition) return false; if (!(body.TryBlock is BlockContainer tryContainer) || tryContainer.EntryPoint.Instructions.Count == 0 || tryContainer.EntryPoint.IncomingEdgeCount != 1) return false; if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, null, objectStore.Variable)) return false; if (objectStore.Variable.LoadCount > 2) return false; context.Step("LockTransformMCS", block); block.Instructions.RemoveAt(i - 1); block.Instructions.RemoveAt(i - 2); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return true; } /// /// stloc lockObj(ldloc tempVar) /// call Enter(ldloc tempVar) /// .try BlockContainer { /// Block lockBlock(incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } finally BlockContainer { /// Block exitBlock(incoming: 1) { /// call Exit(ldloc lockObj) /// leave exitBlock (nop) /// } /// } /// => /// .lock (lockObj) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// bool TransformLockV2(Block block, int i) { if (i < 2) return false; if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 2] is StLoc objectStore) || !objectStore.Value.MatchLdLoc(out var tempVar) || !MatchCall(block.Instructions[i - 1] as Call, "Enter", tempVar)) return false; if (!objectStore.Variable.IsSingleDefinition) return false; if (!(body.TryBlock is BlockContainer tryContainer) || tryContainer.EntryPoint.Instructions.Count == 0 || tryContainer.EntryPoint.IncomingEdgeCount != 1) return false; if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, null, objectStore.Variable)) return false; if (objectStore.Variable.LoadCount > 1) return false; context.Step("LockTransformV2", block); block.Instructions.RemoveAt(i - 1); block.Instructions.RemoveAt(i - 2); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return true; } /// /// stloc flag(ldc.i4 0) /// .try BlockContainer { /// Block lockBlock (incoming: 1) { /// call Enter(stloc obj(lockObj), ldloca flag) /// call WriteLine() /// leave lockBlock (nop) /// } /// } finally BlockContainer { /// Block (incoming: 1) { /// if (ldloc flag) Block { /// call Exit(ldloc obj) /// } /// leave lockBlock (nop) /// } /// } /// => /// .lock (lockObj) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// bool TransformLockV4(Block block, int i) { if (i < 1) return false; if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc flagStore)) return false; if (!flagStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean) || !flagStore.Value.MatchLdcI4(0)) return false; if (!(body.TryBlock is BlockContainer tryContainer) || !MatchLockEntryPoint(tryContainer.EntryPoint, flagStore.Variable, out StLoc objectStore)) return false; if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, flagStore.Variable, objectStore.Variable)) return false; if (objectStore.Variable.LoadCount > 1) return false; context.Step("LockTransformV4", block); block.Instructions.RemoveAt(i - 1); tryContainer.EntryPoint.Instructions.RemoveAt(0); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return true; } /// /// stloc flag(ldc.i4 0) /// .try BlockContainer { /// Block lockBlock (incoming: 1) { /// stloc obj1(stloc obj2(lockObj)) /// call Enter(ldloc obj2, ldloca flag) /// call WriteLine() /// leave lockBlock (nop) /// } /// } finally BlockContainer { /// Block (incoming: 1) { /// if (ldloc flag) Block { /// call Exit(ldloc obj1) /// } /// leave lockBlock (nop) /// } /// } /// => /// .lock (lockObj) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// bool TransformLockV4YieldReturn(Block block, int i) { if (i < 1) return false; if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc flagStore)) return false; if (!flagStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean) || !flagStore.Value.MatchLdcI4(0)) return false; if (!(body.TryBlock is BlockContainer tryContainer) || !MatchLockEntryPoint(tryContainer.EntryPoint, flagStore.Variable, out ILVariable exitVariable, out var objectStore)) return false; if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, flagStore.Variable, exitVariable)) return false; if (objectStore.Variable.LoadCount > 1) return false; context.Step("LockTransformV4YieldReturn", block); block.Instructions.RemoveAt(i - 1); tryContainer.EntryPoint.Instructions.RemoveRange(0, 2); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return true; } /// /// stloc obj(lockObj) /// stloc flag(ldc.i4 0) /// .try BlockContainer { /// Block lockBlock (incoming: 1) { /// call Enter(ldloc obj, ldloca flag) /// call WriteLine() /// leave lockBlock (nop) /// } /// } finally BlockContainer { /// Block (incoming: 1) { /// if (ldloc flag) Block { /// call Exit(ldloc obj) /// } /// leave lockBlock (nop) /// } /// } /// => /// .lock (lockObj) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// bool TransformLockRoslyn(Block block, int i) { if (i < 2) return false; if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc flagStore) || !(block.Instructions[i - 2] is StLoc objectStore)) return false; if (!objectStore.Variable.IsSingleDefinition || !flagStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean) || !flagStore.Value.MatchLdcI4(0)) return false; if (!(body.TryBlock is BlockContainer tryContainer) || !MatchLockEntryPoint(tryContainer.EntryPoint, flagStore.Variable, objectStore.Variable)) return false; if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, flagStore.Variable, objectStore.Variable)) return false; if (objectStore.Variable.LoadCount > 2) return false; context.Step("LockTransformRoslyn", block); block.Instructions.RemoveAt(i - 1); block.Instructions.RemoveAt(i - 2); tryContainer.EntryPoint.Instructions.RemoveAt(0); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return true; } bool MatchExitBlock(Block entryPoint, ILVariable flag, ILVariable obj) { if (entryPoint.Instructions.Count != 2 || entryPoint.IncomingEdgeCount != 1) return false; if (flag != null) { if (!entryPoint.Instructions[0].MatchIfInstruction(out var cond, out var trueInst) || !(trueInst is Block trueBlock)) return false; if (!(cond.MatchLdLoc(flag) || (cond.MatchCompNotEquals(out var left, out var right) && left.MatchLdLoc(flag) && right.MatchLdcI4(0))) || !MatchExitBlock(trueBlock, obj)) return false; } else { if (!MatchCall(entryPoint.Instructions[0] as Call, "Exit", obj)) return false; } if (!entryPoint.Instructions[1].MatchLeave((BlockContainer)entryPoint.Parent, out var retVal) || !retVal.MatchNop()) return false; return true; } bool MatchExitBlock(Block exitBlock, ILVariable obj) { if (exitBlock.Instructions.Count != 1) return false; if (!MatchCall(exitBlock.Instructions[0] as Call, "Exit", obj)) return false; return true; } bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, ILVariable obj) { if (entryPoint.Instructions.Count == 0 || entryPoint.IncomingEdgeCount != 1) return false; if (!MatchCall(entryPoint.Instructions[0] as Call, "Enter", obj, flag)) return false; return true; } bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, out StLoc obj) { obj = null; if (entryPoint.Instructions.Count == 0 || entryPoint.IncomingEdgeCount != 1) return false; if (!MatchCall(entryPoint.Instructions[0] as Call, "Enter", flag, out obj)) return false; return true; } bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, out ILVariable exitVairable, out StLoc obj) { // This pattern is commonly seen in yield return state machines emitted by the legacy C# compiler. obj = null; exitVairable = null; if (entryPoint.Instructions.Count < 1 || entryPoint.IncomingEdgeCount != 1) return false; if (entryPoint.Instructions[0].MatchStLoc(out exitVairable, out var value) && value is StLoc nestedStloc) { obj = nestedStloc; if (!MatchCall(entryPoint.Instructions[1] as Call, "Enter", nestedStloc.Variable, flag)) return false; return true; } return false; } bool MatchCall(Call call, string methodName, ILVariable flag, out StLoc obj) { obj = null; const string ThreadingMonitor = "System.Threading.Monitor"; if (call == null || call.Method.Name != methodName || call.Method.DeclaringType.FullName != ThreadingMonitor || call.Method.TypeArguments.Count != 0 || call.Arguments.Count != 2) return false; if (!call.Arguments[1].MatchLdLoca(flag) || !(call.Arguments[0] is StLoc val)) return false; obj = val; return true; } bool MatchCall(Call call, string methodName, params ILVariable[] variables) { const string ThreadingMonitor = "System.Threading.Monitor"; if (call == null || call.Method.Name != methodName || call.Method.DeclaringType.FullName != ThreadingMonitor || call.Method.TypeArguments.Count != 0 || call.Arguments.Count != variables.Length) return false; if (!call.Arguments[0].MatchLdLoc(variables[0])) return false; if (variables.Length == 2) { if (!call.Arguments[1].MatchLdLoca(variables[1])) return false; } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs ================================================ using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { using FindResult = ILInlining.FindResult; using FindResultType = ILInlining.FindResultType; public class NamedArgumentTransform : IStatementTransform { internal static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved) { Debug.Assert(child.Parent == call); if (call.IsInstanceCall && child.ChildIndex == 0) return FindResult.Stop; // cannot use named arg to move expressionBeingMoved before this pointer if (call.Method.IsOperator || call.Method.IsAccessor) return FindResult.Stop; // cannot use named arg for operators or accessors if (call.Method is VarArgInstanceMethod) return FindResult.Stop; // CallBuilder doesn't support named args when using varargs if (call.Method.IsConstructor) { IType type = call.Method.DeclaringType; if (type.Kind == TypeKind.Delegate || type.IsAnonymousType()) return FindResult.Stop; } if (call.Method.Parameters.Any(p => string.IsNullOrEmpty(p.Name))) return FindResult.Stop; // cannot use named arguments for (int i = child.ChildIndex; i < call.Arguments.Count; i++) { var r = ILInlining.FindLoadInNext(call.Arguments[i], v, expressionBeingMoved, InliningOptions.None); if (r.Type == FindResultType.Found) { return FindResult.NamedArgument(r.LoadInst, call.Arguments[i]); } } return FindResult.Stop; } internal static FindResult CanExtendNamedArgument(Block block, ILVariable v, ILInstruction expressionBeingMoved) { Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); var firstArg = ((StLoc)block.Instructions[0]).Value; var r = ILInlining.FindLoadInNext(firstArg, v, expressionBeingMoved, InliningOptions.IntroduceNamedArguments); if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument) { return r; // OK, inline into first instruction of block } var call = (CallInstruction)block.FinalInstruction; if (call.IsInstanceCall) { // For instance calls, block.Instructions[0] is the argument // for the 'this' pointer. We can only insert at position 1. if (r.Type == FindResultType.Stop) { // error: can't move expressionBeingMoved after block.Instructions[0] return FindResult.Stop; } // Because we always ensure block.Instructions[0] is the 'this' argument, // it's possible that the place we actually need to inline into // is within block.Instructions[1]: if (block.Instructions.Count > 1) { r = ILInlining.FindLoadInNext(block.Instructions[1], v, expressionBeingMoved, InliningOptions.IntroduceNamedArguments); if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument) { return r; // OK, inline into block.Instructions[1] } } } foreach (var arg in call.Arguments) { if (arg.MatchLdLoc(v)) { return FindResult.NamedArgument(arg, arg); } } return FindResult.Stop; } /// /// Introduce a named argument for 'arg' and evaluate it before the other arguments /// (except for the "this" pointer) /// internal static void IntroduceNamedArgument(ILInstruction arg, ILTransformContext context) { var call = (CallInstruction)arg.Parent; Debug.Assert(context.Function == call.Ancestors.OfType().First()); var type = context.TypeSystem.FindType(arg.ResultType); var v = context.Function.RegisterVariable(VariableKind.NamedArgument, type); context.Step($"Introduce named argument '{v.Name}'", arg); if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs) { // create namedArgBlock: namedArgBlock = new Block(BlockKind.CallWithNamedArgs); call.ReplaceWith(namedArgBlock); namedArgBlock.FinalInstruction = call; if (call.IsInstanceCall) { IType thisVarType = call.ConstrainedTo ?? call.Method.DeclaringType; if (CallInstruction.ExpectedTypeForThisPointer(call.Method.DeclaringType, call.ConstrainedTo) == StackType.Ref) { thisVarType = new ByReferenceType(thisVarType); } var thisArgVar = context.Function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg"); namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0])); call.Arguments[0] = new LdLoc(thisArgVar); } } int argIndex = arg.ChildIndex; Debug.Assert(call.Arguments[argIndex] == arg); namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, new StLoc(v, arg)); call.Arguments[argIndex] = new LdLoc(v); } public void Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.NamedArguments) return; var options = ILInlining.OptionsForBlock(block, pos, context); options |= InliningOptions.IntroduceNamedArguments; ILInlining.InlineOneIfPossible(block, pos, options, context: context); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs ================================================ // Copyright (c) 2017 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; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transform for constructing the NullCoalescingInstruction (if.notnull(a,b), or in C#: ??) /// Note that this transform only handles the case where a,b are reference types. /// /// The ?? operator for nullable value types is handled by NullableLiftingTransform. /// public class NullCoalescingTransform : IStatementTransform { public void Run(Block block, int pos, StatementTransformContext context) { if (!TransformRefTypes(block, pos, context)) { TransformThrowExpressionValueTypes(block, pos, context); } } /// /// Handles NullCoalescingInstruction case 1: reference types. /// bool TransformRefTypes(Block block, int pos, StatementTransformContext context) { if (!(block.Instructions[pos] is StLoc stloc)) return false; if (stloc.Variable.Kind != VariableKind.StackSlot) return false; if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) return false; trueInst = Block.Unwrap(trueInst); // stloc s(valueInst) // if (comp(ldloc s == ldnull)) { // stloc s(fallbackInst) // } // => // stloc s(if.notnull(valueInst, fallbackInst)) if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { context.Step("NullCoalescingTransform: simple (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; } // sometimes the compiler generates: // stloc s(valueInst) // if (comp(ldloc s == ldnull)) { // stloc v(fallbackInst) // stloc s(ldloc v) // } // v must be single-assign and single-use. if (trueInst is Block trueBlock && trueBlock.Instructions.Count == 2 && trueBlock.Instructions[0].MatchStLoc(out var temporary, out fallbackValue) && temporary.IsSingleDefinition && temporary.LoadCount == 1 && trueBlock.Instructions[1].MatchStLoc(stloc.Variable, out var useOfTemporary) && useOfTemporary.MatchLdLoc(temporary)) { context.Step("NullCoalescingTransform: with temporary variable (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; } // stloc obj(valueInst) // if (comp(ldloc obj == ldnull)) { // throw(...) // } // => // stloc obj(if.notnull(valueInst, throw(...))) if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { context.Step("NullCoalescingTransform (reference types + throw expression)", stloc); throwInst.resultType = StackType.O; stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; } return false; } /// /// stloc v(value) /// if (logic.not(call get_HasValue(ldloca v))) throw(...) /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... /// => /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... /// bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) { if (pos + 2 >= block.Instructions.Count) return false; if (!(block.Instructions[pos] is StLoc stloc)) return false; ILVariable v = stloc.Variable; if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) return false; if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!(Block.Unwrap(trueInst) is Throw throwInst)) return false; if (!condition.MatchLogicNot(out var arg)) return false; if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v))) return false; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; var nullCoalescingWithThrow = new NullCoalescingInstruction( NullCoalescingKind.NullableWithValueFallback, stloc.Value, throwInst); var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType(); nullCoalescingWithThrow.UnderlyingResultType = resultType; var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); throwInst.resultType = resultType; result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction return true; } else { // reset the primary position (see remarks on ILInstruction.Parent) stloc.Value = stloc.Value; var children = throwInstParent.Children; children[throwInstChildIndex] = throwInst; return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transform that converts code patterns like "v != null ? v.M() : null" to "v?.M()" /// readonly struct NullPropagationTransform { internal static bool IsProtectedIfInst(IfInstruction ifInst) { // We exclude logic.and to avoid turning // "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))" // into "if ((interfaces?.Count ?? 0) != 0)". return ifInst != null && (ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _)) && IfInstruction.IsInConditionSlot(ifInst); } readonly ILTransformContext context; public NullPropagationTransform(ILTransformContext context) { this.context = context; } enum Mode { /// /// reference type or generic type (comparison is 'comp(ldloc(testedVar) == null)') /// ReferenceType, /// /// nullable type, used by value (comparison is 'call get_HasValue(ldloca(testedVar))') /// NullableByValue, /// /// nullable type, used by reference (comparison is 'call get_HasValue(ldloc(testedVar))') /// NullableByReference, /// /// unconstrained generic type (see the pattern described in TransformNullPropagationOnUnconstrainedGenericExpression) /// UnconstrainedType, } /// /// Check if "condition ? trueInst : falseInst" can be simplified using the null-conditional operator. /// Returns the replacement instruction, or null if no replacement is possible. /// internal ILInstruction Run(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst) { Debug.Assert(context.Settings.NullPropagation); Debug.Assert(!condition.MatchLogicNot(out _), "Caller should pass in positive condition"); if (condition is Comp comp && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) { if (comp.LiftingKind != ComparisonLiftingKind.None) return null; if (comp.Kind == ComparisonKind.Equality) { // testedVar == null ? trueInst : falseInst return TryNullPropagation(testedVar, falseInst, trueInst, Mode.ReferenceType); } else if (comp.Kind == ComparisonKind.Inequality) { return TryNullPropagation(testedVar, trueInst, falseInst, Mode.ReferenceType); } } else if (NullableLiftingTransform.MatchHasValueCall(condition, out ILInstruction loadInst)) { // loadInst.HasValue ? trueInst : falseInst if (loadInst.MatchLdLoca(out testedVar)) { return TryNullPropagation(testedVar, trueInst, falseInst, Mode.NullableByValue); } else if (loadInst.MatchLdLoc(out testedVar)) { return TryNullPropagation(testedVar, trueInst, falseInst, Mode.NullableByReference); } } return null; } /// /// testedVar != null ? nonNullInst : nullInst /// ILInstruction TryNullPropagation(ILVariable testedVar, ILInstruction nonNullInst, ILInstruction nullInst, Mode mode) { bool removedRewrapOrNullableCtor = false; if (NullableLiftingTransform.MatchNullableCtor(nonNullInst, out _, out var arg)) { nonNullInst = arg; removedRewrapOrNullableCtor = true; } else if (nonNullInst.MatchNullableRewrap(out arg)) { nonNullInst = arg; removedRewrapOrNullableCtor = true; } if (!IsValidAccessChain(testedVar, mode, nonNullInst, out var varLoad)) return null; // note: InferType will be accurate in this case because the access chain consists of calls and field accesses IType returnType = nonNullInst.InferType(context.TypeSystem); if (nullInst.MatchLdNull()) { context.Step($"Null propagation (mode={mode}, output=reference type)", nonNullInst); // testedVar != null ? testedVar.AccessChain : null // => testedVar?.AccessChain IntroduceUnwrap(testedVar, varLoad, mode); return new NullableRewrap(nonNullInst); } else if (nullInst.MatchDefaultValue(out var type) && type.IsKnownType(KnownTypeCode.NullableOfT)) { context.Step($"Null propagation (mode={mode}, output=value type)", nonNullInst); // testedVar != null ? testedVar.AccessChain : default(T?) // => testedVar?.AccessChain IntroduceUnwrap(testedVar, varLoad, mode); return new NullableRewrap(nonNullInst); } else if (!removedRewrapOrNullableCtor && NullableType.IsNonNullableValueType(returnType)) { context.Step($"Null propagation (mode={mode}, output=null coalescing)", nonNullInst); // testedVar != null ? testedVar.AccessChain : nullInst // => testedVar?.AccessChain ?? nullInst // (only valid if AccessChain returns a non-nullable value) IntroduceUnwrap(testedVar, varLoad, mode); return new NullCoalescingInstruction( NullCoalescingKind.NullableWithValueFallback, new NullableRewrap(nonNullInst), nullInst ) { UnderlyingResultType = nullInst.ResultType }; } return null; } /// /// if (x != null) x.AccessChain(); /// => x?.AccessChain(); /// internal void RunStatements(Block block, int pos) { if (block.Instructions[pos] is IfInstruction ifInst && ifInst.FalseInst.MatchNop()) { if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) { TryNullPropForVoidCall(testedVar, Mode.ReferenceType, ifInst.TrueInst as Block, ifInst); } else if (NullableLiftingTransform.MatchHasValueCall(ifInst.Condition, out ILInstruction arg)) { if (arg.MatchLdLoca(out testedVar)) { TryNullPropForVoidCall(testedVar, Mode.NullableByValue, ifInst.TrueInst as Block, ifInst); } else if (arg.MatchLdLoc(out testedVar)) { TryNullPropForVoidCall(testedVar, Mode.NullableByReference, ifInst.TrueInst as Block, ifInst); } } } if (TransformNullPropagationOnUnconstrainedGenericExpression(block, pos, out var testedVariable, out var nonNullInst, out var nullInst, out var endBlock)) { var parentInstruction = nonNullInst.Parent; var replacement = TryNullPropagation(testedVariable, nonNullInst, nullInst, Mode.UnconstrainedType); if (replacement == null) return; context.Step("TransformNullPropagationOnUnconstrainedGenericExpression", block); switch (parentInstruction) { case StLoc stloc: stloc.Value = replacement; break; case Leave leave: leave.Value = replacement; break; default: // if this ever happens, the pattern checked by TransformNullPropagationOnUnconstrainedGenericExpression // has changed, but this part of the code was not properly adjusted. throw new NotSupportedException(); } // Remove the fallback conditions and blocks block.Instructions.RemoveRange(pos + 1, 2); // if the endBlock is only reachable through the current block, // combine both blocks. if (endBlock?.IncomingEdgeCount == 1) { block.Instructions.AddRange(endBlock.Instructions); block.Instructions.RemoveAt(pos + 2); endBlock.Remove(); } ILInlining.InlineIfPossible(block, pos, context); } } void TryNullPropForVoidCall(ILVariable testedVar, Mode mode, Block body, IfInstruction ifInst) { if (body == null || body.Instructions.Count != 1) return; var bodyInst = body.Instructions[0]; if (bodyInst.MatchNullableRewrap(out var arg)) { bodyInst = arg; } if (!IsValidAccessChain(testedVar, mode, bodyInst, out var varLoad)) return; context.Step($"Null-propagation (mode={mode}, output=void call)", body); // if (testedVar != null) { testedVar.AccessChain(); } // => testedVar?.AccessChain(); IntroduceUnwrap(testedVar, varLoad, mode); ifInst.ReplaceWith(new NullableRewrap( bodyInst ).WithILRange(ifInst)); } bool IsValidAccessChain(ILVariable testedVar, Mode mode, ILInstruction inst, out ILInstruction finalLoad) { finalLoad = null; int chainLength = 0; while (true) { if (IsValidEndOfChain()) { // valid end of chain finalLoad = inst; return chainLength >= 1; } else if (inst.MatchLdFld(out var target, out _)) { inst = target; } else if (inst.MatchLdFlda(out target, out var f)) { if (target is AddressOf addressOf && f.DeclaringType.Kind == TypeKind.Struct) { inst = addressOf.Value; } else { inst = target; } } else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) { if (call.Arguments.Count == 0) { return false; } if (call.Method.IsStatic && (!call.Method.IsExtensionMethod || !CanTransformToExtensionMethodCall(call, context))) { return false; // only instance or extension methods can be called with ?. syntax } if (call.Method.IsAccessor && !IsGetter(call.Method)) { return false; // setter/adder/remover cannot be called with ?. syntax } inst = call.Arguments[0]; if ((call.ConstrainedTo ?? call.Method.DeclaringType).IsReferenceType == false && inst.MatchAddressOf(out var arg, out _)) { inst = arg; } else if (inst is LdObjIfRef ldObjIfRef) { inst = ldObjIfRef.Target; } // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain if (ArgumentsAfterFirstMayUnwrapNull(call.Arguments)) return false; } else if (inst is LdLen ldLen) { inst = ldLen.Array; } else if (inst is LdElema ldElema) { inst = ldElema.Array; // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain if (ldElema.Indices.Any(i => i.HasFlag(InstructionFlags.MayUnwrapNull))) return false; } else if (inst is NullableUnwrap unwrap) { inst = unwrap.Argument; if (unwrap.RefInput && inst is AddressOf addressOf) { inst = addressOf.Value; } // The argument of the unwrap instruction cannot be the end of the chain, because this // would result in two null-conditional operators immediately following each other, // which is not valid in C#. if (IsValidEndOfChain()) return false; } else if (inst is DynamicGetMemberInstruction dynGetMember) { inst = dynGetMember.Target; } else if (inst is DynamicInvokeMemberInstruction dynInvokeMember) { inst = dynInvokeMember.Arguments[0]; if (ArgumentsAfterFirstMayUnwrapNull(dynInvokeMember.Arguments)) return false; } else if (inst is DynamicGetIndexInstruction dynGetIndex) { inst = dynGetIndex.Arguments[0]; if (ArgumentsAfterFirstMayUnwrapNull(dynGetIndex.Arguments)) return false; } else { // unknown node -> invalid chain return false; } chainLength++; } bool ArgumentsAfterFirstMayUnwrapNull(InstructionCollection arguments) { // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain for (int i = 1; i < arguments.Count; ++i) { if (arguments[i].HasFlag(InstructionFlags.MayUnwrapNull)) { return true; } } return false; } bool IsValidEndOfChain() { switch (mode) { case Mode.ReferenceType: // either reference type (expect: ldloc(testedVar)) or unconstrained generic type (expect: ldloca(testedVar)). return inst.MatchLdLocRef(testedVar); case Mode.NullableByValue: return NullableLiftingTransform.MatchGetValueOrDefault(inst, testedVar); case Mode.NullableByReference: return NullableLiftingTransform.MatchGetValueOrDefault(inst, out ILInstruction arg) && arg.MatchLdLoc(testedVar); case Mode.UnconstrainedType: // unconstrained generic type (expect: ldloc(testedVar)) return inst.MatchLdLoc(testedVar) || (inst.MatchLdObjIfRef(out var testedVarLoad, out _) && testedVarLoad.MatchLdLoc(testedVar)); default: throw new ArgumentOutOfRangeException(nameof(mode)); } } bool CanTransformToExtensionMethodCall(CallInstruction call, ILTransformContext context) { return context.CSharpResolver.CanTransformToExtensionMethodCall(call.Method); } } static bool IsGetter(IMethod method) { return method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter; } private void IntroduceUnwrap(ILVariable testedVar, ILInstruction varLoad, Mode mode) { var oldParentChildren = varLoad.Parent.Children; var oldChildIndex = varLoad.ChildIndex; ILInstruction replacement; switch (mode) { case Mode.ReferenceType: case Mode.UnconstrainedType: // Wrap varLoad in nullable.unwrap: replacement = new NullableUnwrap(varLoad.ResultType, varLoad, refInput: varLoad.ResultType == StackType.Ref); break; case Mode.NullableByValue: Debug.Assert(NullableLiftingTransform.MatchGetValueOrDefault(varLoad, testedVar)); replacement = new NullableUnwrap( varLoad.ResultType, new LdLoc(testedVar).WithILRange(varLoad.Children[0]) ).WithILRange(varLoad); break; case Mode.NullableByReference: replacement = new NullableUnwrap( varLoad.ResultType, new LdLoc(testedVar).WithILRange(varLoad.Children[0]), refInput: true ).WithILRange(varLoad); break; default: throw new ArgumentOutOfRangeException(nameof(mode)); } oldParentChildren[oldChildIndex] = replacement; } // stloc target(targetInst) // stloc defaultTemporary(default.value type) // if (logic.not(comp.o(box `0(ldloc defaultTemporary) != ldnull))) Block fallbackBlock { // stloc defaultTemporary(ldobj type(ldloc target)) // stloc target(ldloca defaultTemporary) // if (comp.o(ldloc defaultTemporary == ldnull)) Block fallbackBlock2 { // stloc resultTemporary(nullInst) // br endBlock // } // } // stloc resultTemporary(constrained[type].call_instruction(ldloc target, ...)) // br endBlock // => // stloc resultTemporary(nullable.rewrap(constrained[type].call_instruction(nullable.unwrap(targetInst), ...))) // // -or- // // stloc target(targetInst) // stloc defaultTemporary(default.value type) // if (logic.not(comp.o(box `0(ldloc defaultTemporary) != ldnull))) Block fallbackBlock { // stloc defaultTemporary(ldobj type(ldloc target)) // stloc target(ldloca defaultTemporary) // if (comp.o(ldloc defaultTemporary == ldnull)) Block fallbackBlock2 { // leave(nullInst) // } // } // leave (constrained[type].call_instruction(ldloc target, ...)) // => // leave (nullable.rewrap(constrained[type].call_instruction(nullable.unwrap(targetInst), ...))) private bool TransformNullPropagationOnUnconstrainedGenericExpression(Block block, int pos, out ILVariable target, out ILInstruction nonNullInst, out ILInstruction nullInst, out Block endBlock) { target = null; nonNullInst = null; nullInst = null; endBlock = null; if (pos + 3 >= block.Instructions.Count) return false; // stloc target(...) if (!block.Instructions[pos].MatchStLoc(out target, out _)) return false; if (!(target.Kind == VariableKind.StackSlot && target.LoadCount == 2 && target.StoreCount == 2)) return false; // stloc defaultTemporary(default.value type) if (!(block.Instructions[pos + 1].MatchStLoc(out var defaultTemporary, out var defaultExpression) && defaultExpression.MatchDefaultValue(out var type))) return false; // In the above pattern the defaultTemporary variable is used two times in stloc and ldloc instructions and once in a ldloca instruction if (!(defaultTemporary.Kind == VariableKind.Local && defaultTemporary.LoadCount == 2 && defaultTemporary.StoreCount == 2 && defaultTemporary.AddressCount == 1)) return false; // if (logic.not(comp.o(box `0(ldloc defaultTemporary) != ldnull))) Block fallbackBlock if (!(block.Instructions[pos + 2].MatchIfInstruction(out var condition, out var fallbackBlock1) && condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(defaultTemporary))) return false; if (!MatchStLocResultTemporary(block, pos, type, target, defaultTemporary, fallbackBlock1, out nonNullInst, out nullInst, out endBlock) && !MatchLeaveResult(block, pos, type, target, defaultTemporary, fallbackBlock1, out nonNullInst, out nullInst)) return false; return true; } // stloc resultTemporary(constrained[type].call_instruction(ldloc target, ...)) // br endBlock private bool MatchStLocResultTemporary(Block block, int pos, IType type, ILVariable target, ILVariable defaultTemporary, ILInstruction fallbackBlock, out ILInstruction nonNullInst, out ILInstruction nullInst, out Block endBlock) { endBlock = null; nonNullInst = null; nullInst = null; if (pos + 4 >= block.Instructions.Count) return false; // stloc resultTemporary(constrained[type].call_instruction(ldloc target, ...)) if (!(block.Instructions[pos + 3].MatchStLoc(out var resultTemporary, out nonNullInst))) return false; // br endBlock if (!(block.Instructions[pos + 4].MatchBranch(out endBlock))) return false; // Analyze Block fallbackBlock if (!(fallbackBlock is Block b && IsFallbackBlock(b, type, target, defaultTemporary, resultTemporary, endBlock, out nullInst))) return false; return true; } private bool MatchLeaveResult(Block block, int pos, IType type, ILVariable target, ILVariable defaultTemporary, ILInstruction fallbackBlock, out ILInstruction nonNullInst, out ILInstruction nullInst) { nonNullInst = null; nullInst = null; // leave (constrained[type].call_instruction(ldloc target, ...)) if (!(block.Instructions[pos + 3] is Leave leave && leave.IsLeavingFunction)) return false; nonNullInst = leave.Value; // Analyze Block fallbackBlock if (!(fallbackBlock is Block b && IsFallbackBlock(b, type, target, defaultTemporary, null, leave.TargetContainer, out nullInst))) return false; return true; } // Block fallbackBlock { // stloc defaultTemporary(ldobj type(ldloc target)) // stloc target(ldloca defaultTemporary) // if (comp.o(ldloc defaultTemporary == ldnull)) Block fallbackBlock { // stloc resultTemporary(ldnull) // br endBlock // } // } private bool IsFallbackBlock(Block block, IType type, ILVariable target, ILVariable defaultTemporary, ILVariable resultTemporary, ILInstruction endBlockOrLeaveContainer, out ILInstruction nullInst) { nullInst = null; if (!(block.Instructions.Count == 3)) return false; // stloc defaultTemporary(ldobj type(ldloc target)) if (!(block.Instructions[0].MatchStLoc(defaultTemporary, out var value))) return false; if (!(value.MatchLdObj(out var inst, out var t) && type.Equals(t) && inst.MatchLdLoc(target))) return false; // stloc target(ldloca defaultTemporary) if (!(block.Instructions[1].MatchStLoc(target, out var defaultAddress) && defaultAddress.MatchLdLoca(defaultTemporary))) return false; // if (comp.o(ldloc defaultTemporary == ldnull)) Block fallbackBlock if (!(block.Instructions[2].MatchIfInstruction(out var condition, out var tmp) && condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(defaultTemporary))) return false; // Block fallbackBlock { // stloc resultTemporary(nullInst) // br endBlock // } var fallbackInst = Block.Unwrap(tmp); if (fallbackInst is Block fallbackBlock && endBlockOrLeaveContainer is Block endBlock) { if (!(fallbackBlock.Instructions.Count == 2)) return false; if (!fallbackBlock.Instructions[0].MatchStLoc(resultTemporary, out nullInst)) return false; if (!fallbackBlock.Instructions[1].MatchBranch(endBlock)) return false; } else { if (!(fallbackInst is Leave fallbackLeave && endBlockOrLeaveContainer is BlockContainer leaveContainer && fallbackLeave.TargetContainer == leaveContainer && !fallbackLeave.Value.MatchNop())) return false; nullInst = fallbackLeave.Value; } return true; } } public class NullPropagationStatementTransform : IStatementTransform { public void Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.NullPropagation) return; new NullPropagationTransform(context).RunStatements(block, pos); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Nullable lifting gets run in two places: /// * the usual form looks at an if-else, and runs within the ExpressionTransforms. /// * the NullableLiftingBlockTransform handles the cases where Roslyn generates /// two 'ret' statements for the null/non-null cases of a lifted operator. /// /// The transform handles the following languages constructs: /// * lifted conversions /// * lifted unary and binary operators /// * lifted comparisons /// * the ?? operator with type Nullable{T} on the left-hand-side /// * the ?. operator (via NullPropagationTransform) /// struct NullableLiftingTransform { readonly ILTransformContext context; List nullableVars; public NullableLiftingTransform(ILTransformContext context) { this.context = context; this.nullableVars = null; } #region Run /// /// Main entry point into the normal code path of this transform. /// Called by expression transform. /// public bool Run(IfInstruction ifInst) { var lifted = Lift(ifInst, ifInst.Condition, ifInst.TrueInst, ifInst.FalseInst); if (lifted != null) { ifInst.ReplaceWith(lifted); return true; } return false; } /// /// VS2017.8 / Roslyn 2.9 started optimizing some cases of /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// to /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" /// so this secondary entry point analyses logic.and as-if it was a short-circuiting &&. /// public bool Run(BinaryNumericInstruction bni) { Debug.Assert(!bni.IsLifted && bni.Operator == BinaryNumericOperator.BitAnd); // caller ensures that bni.Left/bni.Right are booleans var lifted = Lift(bni, bni.Left, bni.Right, new LdcI4(0)); if (lifted != null) { bni.ReplaceWith(lifted); return true; } return false; } /// /// VS2022.10 / Roslyn 4.10.0 adds an optimization that turns /// a == 42 into a.GetValueOrDefault() == 42 without any HasValue check. /// public void Run(Comp comp) { if (!comp.IsLifted && comp.Kind.IsEqualityOrInequality()) { var left = comp.Left; var right = comp.Right; if (MatchGetValueOrDefault(left, out ILInstruction arg) && right.MatchLdcI(out var value) && value != 0) { context.Step("comp(a.GetValueOrDefault() == const) -> comp.lifted(a == const)", comp); comp.LiftingKind = ComparisonLiftingKind.CSharp; comp.Left = new LdObj(arg, ((Call)left).Method.DeclaringType); } else if (MatchGetValueOrDefault(right, out arg) && left.MatchLdcI(out value) && value != 0) { context.Step("comp(const == a.GetValueOrDefault()) -> comp.lifted(const == a)", comp); comp.LiftingKind = ComparisonLiftingKind.CSharp; comp.Right = new LdObj(arg, ((Call)right).Method.DeclaringType); } } } public bool RunStatements(Block block, int pos) { // e.g.: // if (!condition) Block { // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // } // leave IL_0000 (newobj .ctor(exprToLift)) if (pos != block.Instructions.Count - 2) return false; if (!(block.Instructions[pos] is IfInstruction ifInst)) return false; if (!(Block.Unwrap(ifInst.TrueInst) is Leave thenLeave)) return false; if (!ifInst.FalseInst.MatchNop()) return false; if (!(block.Instructions[pos + 1] is Leave elseLeave)) return false; if (elseLeave.TargetContainer != thenLeave.TargetContainer) return false; var lifted = Lift(ifInst, ifInst.Condition, thenLeave.Value, elseLeave.Value); if (lifted != null) { thenLeave.Value = lifted; ifInst.ReplaceWith(thenLeave); block.Instructions.Remove(elseLeave); return true; } return false; } #endregion #region AnalyzeCondition bool AnalyzeCondition(ILInstruction condition) { if (MatchHasValueCall(condition, out ILVariable v)) { if (nullableVars == null) nullableVars = new List(); nullableVars.Add(v); return true; } else if (condition is BinaryNumericInstruction bitand) { if (!(bitand.Operator == BinaryNumericOperator.BitAnd && bitand.ResultType == StackType.I4)) return false; return AnalyzeCondition(bitand.Left) && AnalyzeCondition(bitand.Right); } return false; } bool AnalyzeNegatedCondition(ILInstruction condition) { return condition.MatchLogicNot(out var arg) && AnalyzeCondition(arg); } #endregion #region Main lifting logic /// /// Main entry point for lifting; called by both the expression-transform /// and the block transform. /// ILInstruction Lift(ILInstruction ifInst, ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst) { // ifInst is usually the IfInstruction to which condition belongs; // but can also be a BinaryNumericInstruction. while (condition.MatchLogicNot(out var arg)) { condition = arg; ExtensionMethods.Swap(ref trueInst, ref falseInst); } if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst as IfInstruction)) { var nullPropagated = new NullPropagationTransform(context) .Run(condition, trueInst, falseInst)?.WithILRange(ifInst); if (nullPropagated != null) return nullPropagated; } if (!context.Settings.LiftNullables) return null; if (AnalyzeCondition(condition)) { // (v1 != null && ... && vn != null) ? trueInst : falseInst // => normal lifting return LiftNormal(trueInst, falseInst)?.WithILRange(ifInst); } if (MatchCompOrDecimal(condition, out var comp)) { // This might be a C#-style lifted comparison // (C# checks the underlying value before checking the HasValue bits) if (comp.Kind.IsEqualityOrInequality()) { // for equality/inequality, the HasValue bits must also compare equal/inequal if (comp.Kind == ComparisonKind.Inequality) { // handle inequality by swapping one last time ExtensionMethods.Swap(ref trueInst, ref falseInst); } if (falseInst.MatchLdcI4(0)) { // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false // => a == b return LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst) ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Equality, trueInst); } else if (falseInst.MatchLdcI4(1)) { // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true // => a != b return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst) ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst); } else if (!comp.IsLifted && IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst)) { // (default(T) == null) ? Activator.CreateInstance() : default(T) // => Activator.CreateInstance() return trueInst; } } else if (!comp.IsLifted) { // Not (in)equality, but one of < <= > >=. // Returns false unless all HasValue bits are true. if (falseInst.MatchLdcI4(0) && AnalyzeCondition(trueInst)) { // comp(lhs, rhs) ? (v1 != null && ... && vn != null) : false // => comp.lifted[C#](lhs, rhs) return LiftCSharpComparison(comp, comp.Kind); } if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst)) { // comp(lhs, rhs) ? false : (v1 != null && ... && vn != null) return LiftCSharpComparison(comp, comp.Kind.Negate()); } if (falseInst.MatchLdcI4(1) && AnalyzeNegatedCondition(trueInst)) { // comp(lhs, rhs) ? !(v1 != null && ... && vn != null) : true // => !comp.lifted[C#](lhs, rhs) ILInstruction result = LiftCSharpComparison(comp, comp.Kind); if (result == null) return result; return Comp.LogicNot(result); } if (trueInst.MatchLdcI4(1) && AnalyzeNegatedCondition(falseInst)) { // comp(lhs, rhs) ? true : !(v1 != null && ... && vn != null) ILInstruction result = LiftCSharpComparison(comp, comp.Kind.Negate()); if (result == null) return result; return Comp.LogicNot(result); } } } ILVariable v; // Handle equality comparisons with bool?: if (MatchGetValueOrDefault(condition, out v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean)) { if (MatchHasValueCall(trueInst, v) && falseInst.MatchLdcI4(0)) { // v.GetValueOrDefault() ? v.HasValue : false // ==> v == true context.Step("NullableLiftingTransform: v == true", ifInst); return new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v).WithILRange(trueInst), new LdcI4(1).WithILRange(falseInst) ).WithILRange(ifInst); } else if (trueInst.MatchLdcI4(0) && MatchHasValueCall(falseInst, v)) { // v.GetValueOrDefault() ? false : v.HasValue // ==> v == false context.Step("NullableLiftingTransform: v == false", ifInst); return new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v).WithILRange(falseInst), trueInst // LdcI4(0) ).WithILRange(ifInst); } else if (MatchNegatedHasValueCall(trueInst, v) && falseInst.MatchLdcI4(1)) { // v.GetValueOrDefault() ? !v.HasValue : true // ==> v != true context.Step("NullableLiftingTransform: v != true", ifInst); return new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v).WithILRange(trueInst), falseInst // LdcI4(1) ).WithILRange(ifInst); } else if (trueInst.MatchLdcI4(1) && MatchNegatedHasValueCall(falseInst, v)) { // v.GetValueOrDefault() ? true : !v.HasValue // ==> v != false context.Step("NullableLiftingTransform: v != false", ifInst); return new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v).WithILRange(falseInst), new LdcI4(0).WithILRange(trueInst) ).WithILRange(ifInst); } } // Handle & and | on bool?: if (trueInst.MatchLdLoc(out v)) { if (MatchNullableCtor(falseInst, out var utype, out var arg) && utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(0)) { // condition ? v : (bool?)false // => condition & v context.Step("NullableLiftingTransform: 3vl.bool.and(bool, bool?)", ifInst); return new ThreeValuedBoolAnd(condition, trueInst).WithILRange(ifInst); } if (falseInst.MatchLdLoc(out var v2)) { // condition ? v : v2 if (MatchThreeValuedLogicConditionPattern(condition, out var nullable1, out var nullable2)) { // (nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue)) ? v : v2 if (v == nullable1 && v2 == nullable2) { context.Step("NullableLiftingTransform: 3vl.bool.or(bool?, bool?)", ifInst); return new ThreeValuedBoolOr(trueInst, falseInst).WithILRange(ifInst); } else if (v == nullable2 && v2 == nullable1) { context.Step("NullableLiftingTransform: 3vl.bool.and(bool?, bool?)", ifInst); return new ThreeValuedBoolAnd(falseInst, trueInst).WithILRange(ifInst); } } } } else if (falseInst.MatchLdLoc(out v)) { if (MatchNullableCtor(trueInst, out var utype, out var arg) && utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(1)) { // condition ? (bool?)true : v // => condition | v context.Step("NullableLiftingTransform: 3vl.logic.or(bool, bool?)", ifInst); return new ThreeValuedBoolOr(condition, falseInst).WithILRange(ifInst); } } return null; } private bool IsGenericNewPattern(ILInstruction compLeft, ILInstruction compRight, ILInstruction trueInst, ILInstruction falseInst) { // (default(T) == null) ? Activator.CreateInstance() : default(T) return falseInst.MatchDefaultValue(out var type) && (trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) && type.Kind == TypeKind.TypeParameter && compLeft.MatchDefaultValue(out var type2) && type.Equals(type2) && compRight.MatchLdNull(); } private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2) { // Try to match: nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue) nullable1 = null; nullable2 = null; if (!condition.MatchLogicOr(out var lhs, out var rhs)) return false; if (!MatchGetValueOrDefault(lhs, out nullable1)) return false; if (!NullableType.GetUnderlyingType(nullable1.Type).IsKnownType(KnownTypeCode.Boolean)) return false; if (!rhs.MatchLogicAnd(out lhs, out rhs)) return false; if (!lhs.MatchLogicNot(out var arg)) return false; if (!MatchGetValueOrDefault(arg, out nullable2)) return false; if (!NullableType.GetUnderlyingType(nullable2.Type).IsKnownType(KnownTypeCode.Boolean)) return false; if (!rhs.MatchLogicNot(out arg)) return false; return MatchHasValueCall(arg, nullable1); } #endregion #region CSharpComp static bool MatchCompOrDecimal(ILInstruction inst, out CompOrDecimal result) { result = default(CompOrDecimal); result.Instruction = inst; if (inst is Comp comp) { result.Kind = comp.Kind; result.Left = comp.Left; result.Right = comp.Right; result.IsLifted = comp.IsLifted; return true; } else if (inst is Call call && call.Method.IsOperator && call.Arguments.Count == 2 && !call.IsLifted) { switch (call.Method.Name) { case "op_Equality": result.Kind = ComparisonKind.Equality; break; case "op_Inequality": result.Kind = ComparisonKind.Inequality; break; case "op_LessThan": result.Kind = ComparisonKind.LessThan; break; case "op_LessThanOrEqual": result.Kind = ComparisonKind.LessThanOrEqual; break; case "op_GreaterThan": result.Kind = ComparisonKind.GreaterThan; break; case "op_GreaterThanOrEqual": result.Kind = ComparisonKind.GreaterThanOrEqual; break; default: return false; } result.Left = call.Arguments[0]; result.Right = call.Arguments[1]; return call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal); } return false; } /// /// Represents either non-lifted IL `Comp` or a call to one of the (non-lifted) 6 comparison operators on `System.Decimal`. /// struct CompOrDecimal { public ILInstruction Instruction; public ComparisonKind Kind; public ILInstruction Left; public ILInstruction Right; public bool IsLifted; public IType LeftExpectedType { get { if (Instruction is Call call) { return call.Method.Parameters[0].Type; } else { return SpecialType.UnknownType; } } } public IType RightExpectedType { get { if (Instruction is Call call) { return call.Method.Parameters[1].Type; } else { return SpecialType.UnknownType; } } } internal ILInstruction MakeLifted(ComparisonKind newComparisonKind, ILInstruction left, ILInstruction right) { if (Instruction is Comp comp) { return new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, comp.InputType, comp.Sign, left, right).WithILRange(Instruction); } else if (Instruction is Call call) { IMethod method; if (newComparisonKind == Kind) { method = call.Method; } else if (newComparisonKind == ComparisonKind.Inequality && call.Method.Name == "op_Equality") { method = call.Method.DeclaringType.GetMethods(m => m.Name == "op_Inequality") .FirstOrDefault(m => ParameterListComparer.Instance.Equals(m.Parameters, call.Method.Parameters)); if (method == null) return null; } else { return null; } return new Call(CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(method)) { Arguments = { left, right }, ConstrainedTo = call.ConstrainedTo, ILStackWasEmpty = call.ILStackWasEmpty, IsTail = call.IsTail }.WithILRange(call); } else { return null; } } } #endregion #region Lift...Comparison ILInstruction LiftCSharpEqualityComparison(CompOrDecimal valueComp, ComparisonKind newComparisonKind, ILInstruction hasValueTest) { Debug.Assert(newComparisonKind.IsEqualityOrInequality()); bool hasValueTestNegated = false; while (hasValueTest.MatchLogicNot(out var arg)) { hasValueTest = arg; hasValueTestNegated = !hasValueTestNegated; } // The HasValue comparison must be the same operator as the Value comparison. if (hasValueTest is Comp hasValueComp) { if (valueComp.IsLifted || hasValueComp.IsLifted) return null; // Comparing two nullables: HasValue comparison must be the same operator as the Value comparison if ((hasValueTestNegated ? hasValueComp.Kind.Negate() : hasValueComp.Kind) != newComparisonKind) return null; if (!MatchHasValueCall(hasValueComp.Left, out ILVariable leftVar)) return null; if (!MatchHasValueCall(hasValueComp.Right, out ILVariable rightVar)) return null; nullableVars = new List { leftVar }; var (left, leftBits) = DoLift(valueComp.Left); nullableVars[0] = rightVar; var (right, rightBits) = DoLift(valueComp.Right); if (left != null && right != null && leftBits[0] && rightBits[0] && SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags) ) { context.Step("NullableLiftingTransform: C# (in)equality comparison", valueComp.Instruction); return valueComp.MakeLifted(newComparisonKind, left, right); } } else if (newComparisonKind == ComparisonKind.Equality && !hasValueTestNegated && MatchHasValueCall(hasValueTest, out ILVariable v)) { // Comparing nullable with non-nullable -> we can fall back to the normal comparison code. nullableVars = new List { v }; return LiftCSharpComparison(valueComp, newComparisonKind); } else if (newComparisonKind == ComparisonKind.Inequality && hasValueTestNegated && MatchHasValueCall(hasValueTest, out v)) { // Comparing nullable with non-nullable -> we can fall back to the normal comparison code. nullableVars = new List { v }; return LiftCSharpComparison(valueComp, newComparisonKind); } return null; } /// /// Lift a C# comparison. /// This method cannot be used for (in)equality comparisons where both sides are nullable /// (these special cases are handled in LiftCSharpEqualityComparison instead). /// /// The output instructions should evaluate to false when any of the nullableVars is null /// (except for newComparisonKind==Inequality, where this case should evaluate to true instead). /// Otherwise, the output instruction should evaluate to the same value as the input instruction. /// The output instruction should have the same side-effects (incl. exceptions being thrown) as the input instruction. /// This means unlike LiftNormal(), we cannot rely on the input instruction not being evaluated if /// a variable is null. /// ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind) { if (comp.IsLifted) { // Happens when legacy csc generates 'num.GetValueOrDefault() == const && num.HasValue', // checking HasValue is redundant here, modern Roslyn versions optimize it and our // NullableLifting transform on Comp will already lift the lhs of the logic.and. // Treat this case as if the transform had not undone the optimization yet. if (nullableVars.Count != 1) { return null; } if (comp.Left.MatchLdLoc(nullableVars[0]) || comp.Right.MatchLdLoc(nullableVars[0])) { return comp.MakeLifted(newComparisonKind, comp.Left.Clone(), comp.Right.Clone()); } return null; } var (left, right, bits) = DoLiftBinary(comp.Left, comp.Right, comp.LeftExpectedType, comp.RightExpectedType); // due to the restrictions on side effects, we only allow instructions that are pure after lifting. // (we can't check this before lifting due to the calls to GetValueOrDefault()) if (left != null && right != null && SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags)) { if (!bits.All(0, nullableVars.Count)) { // don't lift if a nullableVar doesn't contribute to the result return null; } context.Step("NullableLiftingTransform: C# comparison", comp.Instruction); return comp.MakeLifted(newComparisonKind, left, right); } return null; } ILInstruction LiftCSharpUserEqualityComparison(CompOrDecimal hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst) { // User-defined equality operator: // if (comp(call get_HasValue(ldloca nullable1) == call get_HasValue(ldloca nullable2))) // if (logic.not(call get_HasValue(ldloca nullable))) // ldc.i4 1 // else // call op_Equality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2) // else // ldc.i4 0 // User-defined inequality operator: // if (comp(call get_HasValue(ldloca nullable1) != call get_HasValue(ldloca nullable2))) // ldc.i4 1 // else // if (call get_HasValue(ldloca nullable)) // call op_Inequality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2)) // else // ldc.i4 0 if (hasValueComp.IsLifted) return null; if (!MatchHasValueCall(hasValueComp.Left, out ILVariable nullable1)) return null; if (!MatchHasValueCall(hasValueComp.Right, out ILVariable nullable2)) return null; if (!nestedIfInst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) return null; if (!MatchHasValueCall(condition, out ILVariable nullable)) return null; if (nullable != nullable1 && nullable != nullable2) return null; if (!falseInst.MatchLdcI4(newComparisonKind == ComparisonKind.Equality ? 1 : 0)) return null; bool trueInstNegated = false; while (trueInst.MatchLogicNot(out var arg)) { trueInstNegated = !trueInstNegated; trueInst = arg; } if (!(trueInst is Call call)) return null; if (!(call.Method.IsOperator && call.Arguments.Count == 2)) return null; bool expectEqualityOperator = (newComparisonKind == ComparisonKind.Equality) ^ trueInstNegated; if (call.Method.Name != (expectEqualityOperator ? "op_Equality" : "op_Inequality")) return null; var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method); if (liftedOperator == null) return null; nullableVars = new List { nullable1 }; var (left, leftBits) = DoLift(call.Arguments[0]); nullableVars[0] = nullable2; var (right, rightBits) = DoLift(call.Arguments[1]); if (left != null && right != null && leftBits[0] && rightBits[0] && SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags) ) { context.Step("NullableLiftingTransform: C# user-defined (in)equality comparison", nestedIfInst); ILInstruction replacement = new Call(liftedOperator) { Arguments = { left, right }, ConstrainedTo = call.ConstrainedTo, ILStackWasEmpty = call.ILStackWasEmpty, IsTail = call.IsTail, }.WithILRange(call); if (trueInstNegated) { replacement = Comp.LogicNot(replacement); } return replacement; } return null; } ILInstruction LiftCSharpUserComparison(ILInstruction trueInst, ILInstruction falseInst) { // (v1 != null && ... && vn != null) ? trueInst : falseInst bool trueInstNegated = false; while (trueInst.MatchLogicNot(out var arg)) { trueInstNegated = !trueInstNegated; trueInst = arg; } if (trueInst is Call call && !call.IsLifted && CSharp.Resolver.CSharpOperators.IsComparisonOperator(call.Method) && falseInst.MatchLdcI4((call.Method.Name == "op_Inequality") ^ trueInstNegated ? 1 : 0)) { // (v1 != null && ... && vn != null) ? call op_LessThan(lhs, rhs) : ldc.i4(0) var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method); if ((call.Method.Name == "op_Equality" || call.Method.Name == "op_Inequality") && nullableVars.Count != 1) { // Equality is special (returns true if both sides are null), only handle it // in the normal code path if we're dealing with only a single nullable var // (comparing nullable with non-nullable). return null; } if (liftedOperator == null) { return null; } context.Step("Lift user-defined comparison operator", trueInst); var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1], call.Method.Parameters[0].Type, call.Method.Parameters[1].Type); if (left != null && right != null && bits.All(0, nullableVars.Count)) { ILInstruction result = new Call(liftedOperator) { Arguments = { left, right }, ConstrainedTo = call.ConstrainedTo, ILStackWasEmpty = call.ILStackWasEmpty, IsTail = call.IsTail }.WithILRange(call); if (trueInstNegated) { result = Comp.LogicNot(result); } return result; } } return null; } #endregion #region LiftNormal / DoLift /// /// Performs nullable lifting. /// /// Produces a lifted instruction with semantics equivalent to: /// (v1 != null && ... && vn != null) ? trueInst : falseInst, /// where the v1,...,vn are the this.nullableVars. /// If lifting fails, returns null. /// ILInstruction LiftNormal(ILInstruction trueInst, ILInstruction falseInst) { if (trueInst.MatchIfInstructionPositiveCondition(out var nestedCondition, out var nestedTrue, out var nestedFalse)) { // Sometimes Roslyn generates pointless conditions like: // if (nullable.HasValue && (!nullable.HasValue || nullable.GetValueOrDefault() == b)) if (MatchHasValueCall(nestedCondition, out ILVariable v) && nullableVars.Contains(v)) { trueInst = nestedTrue; } } bool isNullCoalescingWithNonNullableFallback = false; if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift)) { isNullCoalescingWithNonNullableFallback = true; utype = context.TypeSystem.FindType(trueInst.ResultType); exprToLift = trueInst; if (nullableVars.Count == 1 && exprToLift.MatchLdLoc(nullableVars[0])) { // v.HasValue ? ldloc v : fallback // => v ?? fallback context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst); return new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst) { UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType() }; } ILInstruction result = LiftCSharpUserComparison(trueInst, falseInst); if (result != null) return result; } ILInstruction lifted; if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0])) { // v.HasValue ? call GetValueOrDefault(ldloca v) : fallback // => conv.nop.lifted(ldloc v) ?? fallback // This case is handled separately from DoLift() because // that doesn't introduce nop-conversions. context.Step("v.HasValue ? v.GetValueOrDefault() : fallback => v ?? fallback", trueInst); var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type); lifted = new LdLoc(nullableVars[0]); if (!inputUType.Equals(utype) && utype.ToPrimitiveType() != PrimitiveType.None) { // While the ILAst allows implicit conversions between short and int // (because both map to I4); it does not allow implicit conversions // between short? and int? (structs of different types). // So use 'conv.nop.lifted' to allow the conversion. lifted = new Conv( lifted, inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(), checkForOverflow: false, isLifted: true ); } } else { context.Step("NullableLiftingTransform.DoLift", trueInst); BitSet bits; (lifted, bits) = DoLift(exprToLift); if (lifted == null) { return null; } if (!bits.All(0, nullableVars.Count)) { // don't lift if a nullableVar doesn't contribute to the result return null; } Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted && liftable.UnderlyingResultType == exprToLift.ResultType); } if (isNullCoalescingWithNonNullableFallback) { lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst) { UnderlyingResultType = exprToLift.ResultType }; } else if (!MatchNull(falseInst, utype)) { // Normal lifting, but the falseInst isn't `default(utype?)` // => use the `??` operator to provide the fallback value. lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst) { UnderlyingResultType = exprToLift.ResultType }; } return lifted; } /// /// Recursive function that lifts the specified instruction. /// The input instruction is expected to a subexpression of the trueInst /// (so that all nullableVars are guaranteed non-null within this expression). /// /// Creates a new lifted instruction without modifying the input instruction. /// On success, returns (new lifted instruction, bitset). /// If lifting fails, returns (null, null). /// /// The returned bitset specifies which nullableVars were considered "relevant" for this instruction. /// bitSet[i] == true means nullableVars[i] was relevant. /// /// The new lifted instruction will have equivalent semantics to the input instruction /// if all relevant variables are non-null [except that the result will be wrapped in a Nullable{T} struct]. /// If any relevant variable is null, the new instruction is guaranteed to evaluate to null /// without having any other effect. /// (ILInstruction, BitSet) DoLift(ILInstruction inst) { if (MatchGetValueOrDefault(inst, out ILVariable inputVar)) { // n.GetValueOrDefault() lifted => n. BitSet foundIndices = new BitSet(nullableVars.Count); for (int i = 0; i < nullableVars.Count; i++) { if (nullableVars[i] == inputVar) { foundIndices[i] = true; } } if (foundIndices.Any()) return (new LdLoc(inputVar).WithILRange(inst), foundIndices); else return (null, null); } else if (inst is Conv conv) { var (arg, bits) = DoLift(conv.Argument); if (arg != null) { if (conv.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return (null, null); } var newInst = new Conv(arg, conv.InputType, conv.InputSign, conv.TargetType, conv.CheckForOverflow, isLifted: true).WithILRange(conv); return (newInst, bits); } } else if (inst is BitNot bitnot) { var (arg, bits) = DoLift(bitnot.Argument); if (arg != null) { var newInst = new BitNot(arg, isLifted: true, stackType: bitnot.ResultType).WithILRange(bitnot); return (newInst, bits); } } else if (inst is BinaryNumericInstruction binary) { var (left, right, bits) = DoLiftBinary(binary.Left, binary.Right, SpecialType.UnknownType, SpecialType.UnknownType); if (left != null && right != null) { if (binary.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return (null, null); } var newInst = new BinaryNumericInstruction( binary.Operator, left, right, binary.LeftInputType, binary.RightInputType, binary.CheckForOverflow, binary.Sign, isLifted: true ).WithILRange(binary); return (newInst, bits); } } else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality && MatchGetValueOrDefault(comp.Left, out ILVariable v) && nullableVars.Contains(v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean) && comp.Right.MatchLdcI4(0) ) { // C# doesn't support ComparisonLiftingKind.ThreeValuedLogic, // except for operator! on bool?. var (arg, bits) = DoLift(comp.Left); Debug.Assert(arg != null); var newInst = new Comp(comp.Kind, ComparisonLiftingKind.ThreeValuedLogic, comp.InputType, comp.Sign, arg, comp.Right.Clone()).WithILRange(comp); return (newInst, bits); } else if (inst is Call call && call.Method.IsOperator) { // Lifted user-defined operators, except for comparison operators (as those return bool, not bool?) var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method); if (liftedOperator == null || !NullableType.IsNullable(liftedOperator.ReturnType)) return (null, null); ILInstruction[] newArgs; BitSet newBits; if (call.Arguments.Count == 1) { var (arg, bits) = DoLift(call.Arguments[0]); newArgs = new[] { arg }; newBits = bits; } else if (call.Arguments.Count == 2) { var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1], call.Method.Parameters[0].Type, call.Method.Parameters[1].Type); newArgs = new[] { left, right }; newBits = bits; } else { return (null, null); } if (newBits == null || !newBits.All(0, nullableVars.Count)) { // all nullable vars must be involved when calling a method (side effect) return (null, null); } var newInst = new Call(liftedOperator) { ConstrainedTo = call.ConstrainedTo, IsTail = call.IsTail, ILStackWasEmpty = call.ILStackWasEmpty, }.WithILRange(call); newInst.Arguments.AddRange(newArgs); return (newInst, newBits); } return (null, null); } (ILInstruction, ILInstruction, BitSet) DoLiftBinary(ILInstruction lhs, ILInstruction rhs, IType leftExpectedType, IType rightExpectedType) { var (left, leftBits) = DoLift(lhs); var (right, rightBits) = DoLift(rhs); if (left != null && right == null && SemanticHelper.IsPure(rhs.Flags)) { // Embed non-nullable pure expression in lifted expression. right = NewNullable(rhs.Clone(), rightExpectedType); } if (left == null && right != null && SemanticHelper.IsPure(lhs.Flags)) { // Embed non-nullable pure expression in lifted expression. left = NewNullable(lhs.Clone(), leftExpectedType); } if (left != null && right != null) { var bits = leftBits ?? rightBits; if (rightBits != null) bits.UnionWith(rightBits); return (left, right, bits); } else { return (null, null, null); } } private ILInstruction NewNullable(ILInstruction inst, IType underlyingType) { if (underlyingType == SpecialType.UnknownType) return inst; var nullable = context.TypeSystem.FindType(KnownTypeCode.NullableOfT).GetDefinition(); var ctor = nullable?.Methods.FirstOrDefault(m => m.IsConstructor && m.Parameters.Count == 1); if (ctor != null) { ctor = ctor.Specialize(new TypeParameterSubstitution(new[] { underlyingType }, null)); return new NewObj(ctor) { Arguments = { inst } }; } else { return inst; } } #endregion #region Match...Call /// /// Matches 'call get_HasValue(arg)' /// internal static bool MatchHasValueCall(ILInstruction inst, out ILInstruction arg) { arg = null; if (!(inst is Call call)) return false; if (call.Arguments.Count != 1) return false; if (call.Method.Name != "get_HasValue") return false; if (call.Method.DeclaringTypeDefinition?.KnownTypeCode != KnownTypeCode.NullableOfT) return false; arg = call.Arguments[0]; return true; } /// /// Matches 'call get_HasValue(ldloca v)' /// internal static bool MatchHasValueCall(ILInstruction inst, out ILVariable v) { if (MatchHasValueCall(inst, out ILInstruction arg)) { return arg.MatchLdLoca(out v); } v = null; return false; } /// /// Matches 'call get_HasValue(ldloca v)' /// internal static bool MatchHasValueCall(ILInstruction inst, ILVariable v) { return MatchHasValueCall(inst, out ILVariable v2) && v == v2; } /// /// Matches 'logic.not(call get_HasValue(ldloca v))' /// internal static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) { return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v); } /// /// Matches 'newobj Nullable{underlyingType}.ctor(arg)' /// internal static bool MatchNullableCtor(ILInstruction inst, out IType underlyingType, out ILInstruction arg) { underlyingType = null; arg = null; if (!(inst is NewObj newobj)) return false; if (!newobj.Method.IsConstructor || newobj.Arguments.Count != 1) return false; if (newobj.Method.DeclaringTypeDefinition?.KnownTypeCode != KnownTypeCode.NullableOfT) return false; arg = newobj.Arguments[0]; underlyingType = NullableType.GetUnderlyingType(newobj.Method.DeclaringType); return true; } /// /// Matches 'call Nullable{T}.GetValueOrDefault(arg)' /// internal static bool MatchGetValueOrDefault(ILInstruction inst, out ILInstruction arg) { arg = null; if (!(inst is Call call)) return false; if (call.Method.Name != "GetValueOrDefault" || call.Arguments.Count != 1) return false; if (call.Method.DeclaringTypeDefinition?.KnownTypeCode != KnownTypeCode.NullableOfT) return false; arg = call.Arguments[0]; return true; } /// /// Matches 'call nullableValue.GetValueOrDefault(fallback)' /// internal static bool MatchGetValueOrDefault(ILInstruction inst, out ILInstruction nullableValue, out ILInstruction fallback) { nullableValue = null; fallback = null; if (!(inst is Call call)) return false; if (call.Method.Name != "GetValueOrDefault" || call.Arguments.Count != 2) return false; if (call.Method.DeclaringTypeDefinition?.KnownTypeCode != KnownTypeCode.NullableOfT) return false; nullableValue = call.Arguments[0]; fallback = call.Arguments[1]; return true; } /// /// Matches 'call Nullable{T}.GetValueOrDefault(ldloca v)' /// internal static bool MatchGetValueOrDefault(ILInstruction inst, out ILVariable v) { v = null; return MatchGetValueOrDefault(inst, out ILInstruction arg) && arg.MatchLdLoca(out v); } /// /// Matches 'call Nullable{T}.GetValueOrDefault(ldloca v)' /// internal static bool MatchGetValueOrDefault(ILInstruction inst, ILVariable v) { return MatchGetValueOrDefault(inst, out ILVariable v2) && v == v2; } static bool MatchNull(ILInstruction inst, out IType underlyingType) { underlyingType = null; if (inst.MatchDefaultValue(out IType type)) { underlyingType = NullableType.GetUnderlyingType(type); return NullableType.IsNullable(type); } underlyingType = null; return false; } static bool MatchNull(ILInstruction inst, IType underlyingType) { return MatchNull(inst, out var utype) && utype.Equals(underlyingType); } #endregion } public class NullableLiftingStatementTransform : IStatementTransform { public void Run(Block block, int pos, StatementTransformContext context) { new NullableLiftingTransform(context).RunStatements(block, pos); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs ================================================ // Copyright (c) 2021 Daniel Grunwald, 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. #nullable enable using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { class PatternMatchingTransform : IILTransform { void IILTransform.Run(ILFunction function, ILTransformContext context) { if (!context.Settings.PatternMatching) return; foreach (var container in function.Descendants.OfType()) { ControlFlowGraph? cfg = null; foreach (var block in container.Blocks.Reverse()) { if (PatternMatchValueTypes(block, container, context, ref cfg)) { continue; } if (PatternMatchRefTypes(block, container, context, ref cfg)) { continue; } } container.Blocks.RemoveAll(b => b.Instructions.Count == 0); } } /// Block { /// ... /// stloc V(isinst T(testedOperand)) /// if (comp.o(ldloc V == ldnull)) br falseBlock /// br trueBlock /// } /// /// All other uses of V are in blocks dominated by trueBlock. /// => /// Block { /// ... /// if (match.type[T].notnull(V = testedOperand)) br trueBlock /// br falseBlock /// } /// /// - or - /// /// Block { /// stloc s(isinst T(testedOperand)) /// stloc v(ldloc s) /// if (logic.not(comp.o(ldloc s != ldnull))) br falseBlock /// br trueBlock /// } /// => /// Block { /// ... /// if (match.type[T].notnull(V = testedOperand)) br trueBlock /// br falseBlock /// } /// /// All other uses of V are in blocks dominated by trueBlock. private bool PatternMatchRefTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) { if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) return false; int pos = block.Instructions.Count - 3; if (condition.MatchLdLoc(out var conditionVar)) { // stloc conditionVar(comp.o(ldloc s == ldnull)) // if (logic.not(ldloc conditionVar)) br trueBlock if (pos < 0) return false; if (!(conditionVar.IsSingleDefinition && conditionVar.LoadCount == 1 && conditionVar.Kind == VariableKind.StackSlot)) { return false; } if (!block.Instructions[pos].MatchStLoc(conditionVar, out condition)) return false; pos--; } if (condition.MatchCompEqualsNull(out var loadInNullCheck)) { ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (condition.MatchCompNotEqualsNull(out loadInNullCheck)) { // do nothing } else { return false; } if (!loadInNullCheck.MatchLdLoc(out var s)) return false; if (!s.IsSingleDefinition) return false; if (s.Kind is not (VariableKind.Local or VariableKind.StackSlot)) return false; if (pos < 0) return false; // stloc V(isinst T(testedOperand)) ILInstruction storeToV = block.Instructions[pos]; if (!storeToV.MatchStLoc(out var v, out var value)) return false; if (value.MatchLdLoc(s)) { // stloc v(ldloc s) pos--; if (pos < 0 || !block.Instructions[pos].MatchStLoc(s, out value)) return false; if (v.Kind is not (VariableKind.Local or VariableKind.StackSlot)) return false; if (s.LoadCount != 2) return false; } else { if (v != s) return false; } IType? unboxType; if (value is UnboxAny unboxAny) { // stloc S(unbox.any T(isinst T(testedOperand))) unboxType = unboxAny.Type; value = unboxAny.Argument; } else { unboxType = null; } if (value is not IsInst { Argument: var testedOperand, Type: var type }) return false; if (type.IsReferenceType != true) return false; if (!(unboxType == null || type.Equals(unboxType))) return false; if (!v.Type.Equals(type)) return false; if (!CheckAllUsesDominatedBy(v, container, trueInst, storeToV, loadInNullCheck, context, ref cfg)) return false; context.Step($"Type pattern matching {v.Name}", block); // if (match.type[T].notnull(V = testedOperand)) br trueBlock var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; ifInst.Condition = new MatchInstruction(v, testedOperand) { CheckNotNull = true, CheckType = true }.WithILRange(ifInst.Condition); ifInst.TrueInst = trueInst; block.Instructions[block.Instructions.Count - 1] = falseInst; block.Instructions.RemoveRange(pos, ifInst.ChildIndex - pos); v.Kind = VariableKind.PatternLocal; DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueInst, falseInst, container, context, ref cfg); return true; } private static ILInstruction DetectPropertySubPatterns(MatchInstruction parentPattern, ILInstruction trueInst, ILInstruction parentFalseInst, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) { if (!context.Settings.RecursivePatternMatching) { return trueInst; } while (true) { Block? trueBlock = trueInst as Block; if (!(trueBlock != null || trueInst.MatchBranch(out trueBlock))) { break; } if (!(trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container)) { break; } var nextTrueInst = DetectPropertySubPattern(parentPattern, trueBlock, parentFalseInst, context, ref cfg); if (nextTrueInst != null) { trueInst = nextTrueInst; } else { break; } } return trueInst; } private static ILInstruction? DetectPropertySubPattern(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg) { // if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022 // br IL_0037 if (MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst)) { bool negate = false; if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst)) { if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst)) { return null; } ExtensionMethods.Swap(ref trueInst, ref falseInst); negate = true; } if (MatchInstruction.IsPatternMatch(condition, out var operand, context.Settings)) { if (!PropertyOrFieldAccess(operand, out var target, out _)) { return null; } if (!target.MatchLdLocRef(parentPattern.Variable)) { return null; } if (negate && !context.Settings.PatternCombinators) { return null; } context.Step("Move property sub pattern", condition); if (negate) { condition = Comp.LogicNot(condition); } parentPattern.SubPatterns.Add(condition); } else if (PropertyOrFieldAccess(condition, out var target, out _)) { if (!target.MatchLdLocRef(parentPattern.Variable)) { return null; } if (!negate && !context.Settings.PatternCombinators) { return null; } context.Step("Sub pattern: implicit != 0", condition); parentPattern.SubPatterns.Add(new Comp(negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdcI4(0))); } else { return null; } block.Instructions.Clear(); block.Instructions.Add(trueInst); return trueInst; } else if (block.Instructions[0].MatchStLoc(out var targetVariable, out var operand)) { if (!PropertyOrFieldAccess(operand, out var target, out var member)) { return null; } if (!target.MatchLdLocRef(parentPattern.Variable)) { return null; } if (!targetVariable.Type.Equals(member.ReturnType)) { return null; } if (!CheckAllUsesDominatedBy(targetVariable, (BlockContainer)block.Parent!, block, block.Instructions[0], null, context, ref cfg)) { return null; } context.Step("Property var pattern", block); var varPattern = new MatchInstruction(targetVariable, operand) .WithILRange(block.Instructions[0]); parentPattern.SubPatterns.Add(varPattern); block.Instructions.RemoveAt(0); targetVariable.Kind = VariableKind.PatternLocal; if (targetVariable.Type.IsKnownType(KnownTypeCode.NullableOfT)) { return MatchNullableHasValueCheckPattern(block, varPattern, parentFalseInst, context, ref cfg) ?? block; } var instructionAfterNullCheck = MatchNullCheckPattern(block, varPattern, parentFalseInst, context); if (instructionAfterNullCheck != null) { return DetectPropertySubPatterns(varPattern, instructionAfterNullCheck, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg); } else if (targetVariable.Type.IsReferenceType == false) { return DetectPropertySubPatterns(varPattern, block, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg); } else { return block; } } else { return null; } } private static ILInstruction? MatchNullCheckPattern(Block block, MatchInstruction varPattern, ILInstruction parentFalseInst, ILTransformContext context) { if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst)) { return null; } if (condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(varPattern.Variable)) { ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (condition.MatchCompNotEqualsNull(out arg) && arg.MatchLdLoc(varPattern.Variable)) { } else { return null; } if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst)) { return null; } context.Step("Null check pattern", block); varPattern.CheckNotNull = true; block.Instructions.Clear(); block.Instructions.Add(trueInst); return trueInst; } private static ILInstruction? MatchNullableHasValueCheckPattern(Block block, MatchInstruction varPattern, ILInstruction parentFalseInst, ILTransformContext context, ref ControlFlowGraph? cfg) { if (!(varPattern.Variable.StoreCount == 1 && varPattern.Variable.LoadCount == 0)) { return null; } if (!MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst)) { return null; } if (!NullableLiftingTransform.MatchHasValueCall(condition, varPattern.Variable)) { return null; } if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst)) { if (DetectExitPoints.CompatibleExitInstruction(trueInst, parentFalseInst)) { if (!(varPattern.Variable.AddressCount == 1)) { return null; } context.Step("Nullable.HasValue check -> null pattern", block); varPattern.ReplaceWith(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull())); block.Instructions.Clear(); block.Instructions.Add(falseInst); return falseInst; } return null; } if (varPattern.Variable.AddressCount == 1 && context.Settings.PatternCombinators) { context.Step("Nullable.HasValue check -> not null pattern", block); varPattern.ReplaceWith(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull())); block.Instructions.Clear(); block.Instructions.Add(trueInst); return trueInst; } else if (varPattern.Variable.AddressCount != 2) { return null; } if (!(trueInst.MatchBranch(out var trueBlock) && trueBlock.Parent == block.Parent && trueBlock.IncomingEdgeCount == 1)) { return null; } if (trueBlock.Instructions[0].MatchStLoc(out var newTargetVariable, out var getValueOrDefaultCall) && NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefaultCall, varPattern.Variable)) { context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block); varPattern.CheckNotNull = true; varPattern.Variable = newTargetVariable; newTargetVariable.Kind = VariableKind.PatternLocal; block.Instructions.Clear(); block.Instructions.Add(trueInst); trueBlock.Instructions.RemoveAt(0); return DetectPropertySubPatterns(varPattern, trueBlock, parentFalseInst, (BlockContainer)block.Parent!, context, ref cfg); } else if (MatchBlockContainingOneCondition(trueBlock, out condition, out trueInst, out falseInst)) { if (!(condition is Comp comp && MatchInstruction.IsConstant(comp.Right) && NullableLiftingTransform.MatchGetValueOrDefault(comp.Left, varPattern.Variable))) { return null; } if (!(context.Settings.RelationalPatterns || comp.Kind is ComparisonKind.Equality or ComparisonKind.Inequality)) { return null; } bool negated = false; if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst)) { if (!DetectExitPoints.CompatibleExitInstruction(trueInst, parentFalseInst)) { return null; } ExtensionMethods.Swap(ref trueInst, ref falseInst); negated = true; } if (comp.Kind == (negated ? ComparisonKind.Equality : ComparisonKind.Inequality)) { return null; } if (negated && !context.Settings.PatternCombinators) { return null; } context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block); // varPattern: match (v = testedOperand) // comp: comp.i4(call GetValueOrDefault(ldloca v) != ldc.i4 42) // => // comp.i4.lifted(testedOperand != ldc.i4 42) block.Instructions.Clear(); block.Instructions.Add(trueInst); trueBlock.Instructions.Clear(); comp.Left = varPattern.TestedOperand; comp.LiftingKind = ComparisonLiftingKind.CSharp; if (negated) { comp = Comp.LogicNot(comp); } varPattern.ReplaceWith(comp); return trueInst; } else { return null; } } private static bool PropertyOrFieldAccess(ILInstruction operand, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member) { if (operand is CallInstruction { Method: { SymbolKind: SymbolKind.Accessor, AccessorKind: MethodSemanticsAttributes.Getter, AccessorOwner: { } _member }, Arguments: [var _target] }) { target = _target; member = _member; return true; } else if (operand.MatchLdFld(out target, out var field)) { member = field; return true; } else { member = null; return false; } } private static bool MatchBlockContainingOneCondition(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst) { switch (block.Instructions.Count) { case 2: return block.MatchIfAtEndOfBlock(out condition, out trueInst, out falseInst); case 3: condition = null; if (!block.MatchIfAtEndOfBlock(out var loadTemp, out trueInst, out falseInst)) return false; if (!(loadTemp.MatchLdLoc(out var tempVar) && tempVar.IsSingleDefinition && tempVar.LoadCount == 1)) return false; if (!block.Instructions[0].MatchStLoc(tempVar, out condition)) return false; while (condition.MatchLogicNot(out var arg)) { condition = arg; ExtensionMethods.Swap(ref trueInst, ref falseInst); } return true; default: condition = null; trueInst = null; falseInst = null; return false; } } private static bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst, ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg) { var targetBlock = trueInst as Block; if (targetBlock == null && !trueInst.MatchBranch(out targetBlock)) { return false; } if (targetBlock.Parent != container) return false; if (targetBlock.IncomingEdgeCount != 1) return false; cfg ??= new ControlFlowGraph(container, context.CancellationToken); var targetBlockNode = cfg.GetNode(targetBlock); var uses = v.LoadInstructions.Concat(v.AddressInstructions) .Concat(v.StoreInstructions.Cast()); foreach (var use in uses) { if (use == storeToV || use == loadInNullCheck) continue; Block? found = null; for (ILInstruction? current = use; current != null; current = current.Parent) { if (current.Parent == container) { found = (Block)current; break; } } if (found == null) return false; var node = cfg.GetNode(found); if (!targetBlockNode.Dominates(node)) return false; } return true; } /// Block { /// ... /// [stloc temp(ldloc testedOperand)] /// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock /// br unboxBlock /// } /// /// Block unboxBlock (incoming: 1) { /// stloc V(unbox.any T(ldloc temp)) /// ... /// } /// => /// Block { /// ... /// if (match.type[T].notnull(V = testedOperand)) br unboxBlock /// br falseBlock /// } private bool PatternMatchValueTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) { if (!MatchIsInstBlock(block, out var type, out var testedOperand, out var testedVariable, out var boxType1, out var unboxBlock, out var falseInst)) { return false; } StLoc? tempStore = block.Instructions.ElementAtOrDefault(block.Instructions.Count - 3) as StLoc; if (tempStore == null || !tempStore.Value.MatchLdLoc(testedVariable)) { tempStore = null; } if (!MatchUnboxBlock(unboxBlock, type, out var unboxOperand, out var boxType2, out var storeToV)) { return false; } if (!object.Equals(boxType1, boxType2)) { return false; } if (unboxOperand == testedVariable) { // do nothing } else if (unboxOperand == tempStore?.Variable) { if (!(tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1)) return false; } else { return false; } if (!CheckAllUsesDominatedBy(storeToV.Variable, container, unboxBlock, storeToV, null, context, ref cfg)) return false; context.Step($"PatternMatching with {storeToV.Variable.Name}", block); var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; ifInst.Condition = new MatchInstruction(storeToV.Variable, testedOperand) { CheckNotNull = true, CheckType = true }; ifInst.TrueInst = new Branch(unboxBlock); block.Instructions[^1] = falseInst; unboxBlock.Instructions.RemoveAt(0); if (unboxOperand == tempStore?.Variable) { block.Instructions.Remove(tempStore); } // HACK: condition detection uses StartILOffset of blocks to decide which branch of if-else // should become the then-branch. Change the unboxBlock StartILOffset from an offset inside // the pattern matching machinery to an offset belonging to an instruction in the then-block. unboxBlock.SetILRange(unboxBlock.Instructions[0]); storeToV.Variable.Kind = VariableKind.PatternLocal; DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, unboxBlock, falseInst, container, context, ref cfg); return true; } /// ... /// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock /// br unboxBlock /// - or - /// ... /// if (comp.o(isinst T(box ``0(ldloc testedOperand)) == ldnull)) br falseBlock /// br unboxBlock private bool MatchIsInstBlock(Block block, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? testedOperand, [NotNullWhen(true)] out ILVariable? testedVariable, out IType? boxType, [NotNullWhen(true)] out Block? unboxBlock, [NotNullWhen(true)] out ILInstruction? falseInst) { type = null; testedOperand = null; testedVariable = null; boxType = null; unboxBlock = null; if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out falseInst)) { return false; } if (condition.MatchCompEqualsNull(out var arg)) { ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (condition.MatchCompNotEqualsNull(out arg)) { // do nothing } else { return false; } if (!arg.MatchIsInst(out testedOperand, out type)) { return false; } if (!(testedOperand.MatchBox(out var boxArg, out boxType) && boxType.Kind == TypeKind.TypeParameter)) { boxArg = testedOperand; } if (!boxArg.MatchLdLoc(out testedVariable)) { return false; } return trueInst.MatchBranch(out unboxBlock) && unboxBlock.Parent == block.Parent; } /// Block unboxBlock (incoming: 1) { /// stloc V(unbox.any T(ldloc testedOperand)) /// ... /// - or - /// stloc V(unbox.any T(isinst T(box ``0(ldloc testedOperand)))) /// ... /// } private bool MatchUnboxBlock(Block unboxBlock, IType type, [NotNullWhen(true)] out ILVariable? testedVariable, out IType? boxType, [NotNullWhen(true)] out StLoc? storeToV) { boxType = null; storeToV = null; testedVariable = null; if (unboxBlock.IncomingEdgeCount != 1) return false; storeToV = unboxBlock.Instructions[0] as StLoc; if (storeToV == null) return false; var value = storeToV.Value; if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type))) return false; if (arg.MatchIsInst(out var isinstArg, out var isinstType) && isinstType.Equals(type)) { arg = isinstArg; } if (arg.MatchBox(out var boxArg, out boxType) && boxType.Kind == TypeKind.TypeParameter) { arg = boxArg; } if (!arg.MatchLdLoc(out testedVariable)) { return false; } if (boxType != null && !boxType.Equals(testedVariable.Type)) { return false; } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs ================================================ // Copyright (c) 2017 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 // 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.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { class ProxyCallReplacer : IILTransform { public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AsyncAwait) return; foreach (var inst in function.Descendants.OfType()) { Run(inst, context); } } void Run(CallInstruction inst, ILTransformContext context) { if (inst.Method.IsStatic) return; if (inst.Method.MetadataToken.IsNil || inst.Method.MetadataToken.Kind != HandleKind.MethodDefinition) return; var handle = (MethodDefinitionHandle)inst.Method.MetadataToken; if (!IsDefinedInCurrentOrOuterClass(inst.Method, context.Function.Method.DeclaringTypeDefinition)) return; if (!inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return; var metadata = context.PEFile.Metadata; MethodDefinition methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)inst.Method.MetadataToken); if (!methodDef.HasBody()) return; // Use the callee's generic context var genericContext = new GenericContext(inst.Method); // partially copied from CSharpDecompiler var ilReader = context.CreateILReader(); var body = context.PEFile.GetMethodBody(methodDef.RelativeVirtualAddress); var proxyFunction = ilReader.ReadIL(handle, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken); var transformContext = new ILTransformContext(context, proxyFunction); proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext); if (!(proxyFunction.Body is BlockContainer blockContainer)) return; if (blockContainer.Blocks.Count != 1) return; var block = blockContainer.Blocks[0]; Call call; ILInstruction returnValue; switch (block.Instructions.Count) { case 1: // leave IL_0000 (call Test(ldloc this, ldloc A_1)) if (!block.Instructions[0].MatchLeave(blockContainer, out returnValue)) return; call = returnValue as Call; break; case 2: // call Test(ldloc this, ldloc A_1) // leave IL_0000(nop) call = block.Instructions[0] as Call; if (!block.Instructions[1].MatchLeave(blockContainer, out returnValue)) return; if (!returnValue.MatchNop()) return; break; default: return; } if (call == null || call.Method.IsConstructor) { return; } if (call.Method.IsStatic || call.Method.Parameters.Count != inst.Method.Parameters.Count) { return; } // check if original arguments are only correct ldloc calls for (int i = 0; i < call.Arguments.Count; i++) { var originalArg = call.Arguments[i]; if (!originalArg.MatchLdLoc(out ILVariable var) || var.Kind != VariableKind.Parameter || var.Index != i - 1) { return; } } context.Step("Replace proxy: " + inst.Method.Name + " with " + call.Method.Name, inst); // Apply the wrapper call's substitution to the actual method call. Call newInst = new Call(call.Method.Specialize(inst.Method.Substitution)); // copy flags newInst.ConstrainedTo = call.ConstrainedTo; newInst.ILStackWasEmpty = inst.ILStackWasEmpty; newInst.IsTail = call.IsTail & inst.IsTail; // copy IL ranges newInst.AddILRange(inst); newInst.Arguments.ReplaceList(inst.Arguments); inst.ReplaceWith(newInst); } static bool IsDefinedInCurrentOrOuterClass(IMethod method, ITypeDefinition declaringTypeDefinition) { while (declaringTypeDefinition != null) { if (method.DeclaringTypeDefinition == declaringTypeDefinition) return true; declaringTypeDefinition = declaringTypeDefinition.DeclaringTypeDefinition; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs ================================================ // 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.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL { /// /// Improves code quality by duplicating keyword exits to reduce nesting and restoring IL order. /// /// /// ConditionDetection and DetectSwitchBody both have aggressive inlining policies for else blocks and default cases respectively. /// This can lead to excessive indentation when the entire rest of the method/loop is included in the else block/default case. /// When an If/SwitchInstruction is followed immediately by a keyword exit, the exit can be moved into the child blocks /// allowing the else block or default case to be moved after the if/switch as all prior cases exit. /// Most importantly, this transformation does not change the IL order of any code. /// /// ConditionDetection also has a block exit priority system to assist exit point reduction which in some cases ignores IL order. /// After HighLevelLoopTransform has run, all structures have been detected and preference can be returned to maintaining IL ordering. /// public class ReduceNestingTransform : IILTransform { private ILTransformContext context; public void Run(ILFunction function, ILTransformContext context) { this.context = context; Visit((BlockContainer)function.Body, null); foreach (var node in function.Descendants.OfType()) { EliminateRedundantTryFinally(node, context); } } private void Visit(BlockContainer container, Block continueTarget) { switch (container.Kind) { case ContainerKind.Loop: case ContainerKind.While: continueTarget = container.EntryPoint; break; case ContainerKind.DoWhile: case ContainerKind.For: continueTarget = container.Blocks.Last(); break; } for (int i = 0; i < container.Blocks.Count; i++) { var block = container.Blocks[i]; // Note: it's possible for additional blocks to be appended to the container // by the Visit() call; but there should be no other changes to the Blocks collection. Visit(block, continueTarget); Debug.Assert(container.Blocks[i] == block); } } /// /// Visits a block in context /// /// /// Marks the target block of continue statements. /// The instruction following the end point of the block. Can only be null if the end point is unreachable. private void Visit(Block block, Block continueTarget, ILInstruction nextInstruction = null) { Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable) || nextInstruction != null); // process each instruction in the block. for (int i = 0; i < block.Instructions.Count; i++) { // Transformations may be applied to the current and following instructions but already processed instructions will not be changed var inst = block.Instructions[i]; // the next instruction to be executed. Transformations will change the next instruction, so this is a method instead of a variable ILInstruction NextInsn() => block.Instructions.ElementAtOrDefault(i + 1) ?? nextInstruction; switch (inst) { case BlockContainer container: // visit the contents of the container Visit(container, continueTarget); // reduce nesting in switch blocks if (container.Kind == ContainerKind.Switch && CanDuplicateExit(NextInsn(), continueTarget, out var keywordExit1) && ReduceSwitchNesting(block, container, keywordExit1)) { RemoveRedundantExit(block, nextInstruction); } break; case IfInstruction ifInst: ImproveILOrdering(block, ifInst, continueTarget); // reduce nesting in if/else blocks if (CanDuplicateExit(NextInsn(), continueTarget, out var keywordExit2) && ReduceNesting(block, ifInst, keywordExit2)) RemoveRedundantExit(block, nextInstruction); // visit content blocks if (ifInst.TrueInst is Block trueBlock) Visit(trueBlock, continueTarget, NextInsn()); if (ifInst.FalseInst is Block falseBlock) { if (ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable)) { ExtractElseBlock(ifInst); break; } Visit(falseBlock, continueTarget, NextInsn()); } break; default: // blocks can only exit containers via leave instructions, not fallthrough, so the only relevant context is `continueTarget` VisitContainers(inst, continueTarget); // reducing nesting inside Try/Using/Lock etc, may make the endpoint unreachable. // This should only happen by replacing a Leave with the exit instruction we're about to delete, but I can't see a good way to assert this // This would be better placed in ReduceNesting, but it's more difficult to find the affected instructions/blocks there than here if (i == block.Instructions.Count - 2 && inst.HasFlag(InstructionFlags.EndPointUnreachable)) { context.Step("Remove unreachable exit", block.Instructions.Last()); block.Instructions.RemoveLast(); // This would be the right place to check and fix the redundant continue; in TestCases.Pretty.ReduceNesting.BreakLockInLoop // but doing so would require knowledge of what `inst` is, and how it works. (eg. to target the try block and not catch or finally blocks) } break; } } } // search for child containers to reduce nesting in private void VisitContainers(ILInstruction inst, Block continueTarget) { switch (inst) { case ILFunction _: break; // assume inline ILFunctions are already transformed case BlockContainer cont: Visit(cont, continueTarget); break; default: foreach (var child in inst.Children) VisitContainers(child, continueTarget); break; } } /// /// For an if statement with an unreachable end point and no else block, /// inverts to match IL order of the first statement of each branch /// private void ImproveILOrdering(Block block, IfInstruction ifInst, Block continueTarget) { if (!block.HasFlag(InstructionFlags.EndPointUnreachable) || !ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable) || !ifInst.FalseInst.MatchNop()) return; Debug.Assert(ifInst != block.Instructions.Last()); var trueRangeStart = ConditionDetection.GetStartILOffset(ifInst.TrueInst, out bool trueRangeIsEmpty); var falseRangeStart = ConditionDetection.GetStartILOffset(block.Instructions[block.Instructions.IndexOf(ifInst) + 1], out bool falseRangeIsEmpty); if (trueRangeIsEmpty || falseRangeIsEmpty || falseRangeStart >= trueRangeStart) return; if (block.Instructions.Last() is Leave leave && !leave.IsLeavingFunction && leave.TargetContainer.Kind == ContainerKind.Normal) { // non-keyword leave. Can't move out of the last position in the block (fall-through) without introducing goto, unless it can be replaced with a keyword (return/continue) if (!CanDuplicateExit(block.Instructions.Last(), continueTarget, out var keywordExit)) return; context.Step("Replace leave with keyword exit", ifInst.TrueInst); block.Instructions.Last().ReplaceWith(keywordExit.Clone()); } ConditionDetection.InvertIf(block, ifInst, context); } /// /// Reduce Nesting in if/else statements by duplicating an exit instruction. /// Does not affect IL order /// private bool ReduceNesting(Block block, IfInstruction ifInst, ILInstruction exitInst) { // start tallying stats for heuristics from then and else-if blocks int maxStatements = 0, maxDepth = 0; UpdateStats(ifInst.TrueInst, ref maxStatements, ref maxDepth); // if (cond) { ... } exit; if (ifInst.FalseInst.MatchNop()) { // a separate heuristic to ShouldReduceNesting as there is visual balancing to be performed based on number of statments if (maxDepth < 2) return false; // -> // if (!cond) exit; // ...; exit; EnsureEndPointUnreachable(block, exitInst); Debug.Assert(ifInst == block.Instructions.SecondToLastOrDefault()); // use the same exit the block has. If the block already has one (such as a leave from a try), keep it in place EnsureEndPointUnreachable(ifInst.TrueInst, block.Instructions.Last()); ConditionDetection.InvertIf(block, ifInst, context); // ensure the exit inst of the if instruction is a keyword Debug.Assert(!(ifInst.TrueInst is Block)); if (!ifInst.TrueInst.Match(exitInst).Success) { Debug.Assert(ifInst.TrueInst is Leave); context.Step("Replace leave with keyword exit", ifInst.TrueInst); ifInst.TrueInst.ReplaceWith(exitInst.Clone()); } return true; } // else-if trees are considered as a single group from the root IfInstruction if (GetElseIfParent(ifInst) != null) return false; // find the else block and tally stats for each else-if block while (Block.Unwrap(ifInst.FalseInst) is IfInstruction elseIfInst) { UpdateStats(elseIfInst.TrueInst, ref maxStatements, ref maxDepth); ifInst = elseIfInst; } if (!ShouldReduceNesting(ifInst.FalseInst, maxStatements, maxDepth)) return false; // extract the else block and insert exit points all the way up the else-if tree do { var elseIfInst = GetElseIfParent(ifInst); // if (cond) { ... } else { ... } exit; // -> // if (cond) { ...; exit; } // ...; exit; EnsureEndPointUnreachable(ifInst.TrueInst, exitInst); if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable)) { Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable)); Debug.Assert(ifInst.Parent == block); int removeAfter = ifInst.ChildIndex + 1; if (removeAfter < block.Instructions.Count) { // Remove all instructions that ended up dead // (this should just be exitInst itself) Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst); Debug.Assert(block.Instructions.Last() == exitInst); block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter); } } ExtractElseBlock(ifInst); ifInst = elseIfInst; } while (ifInst != null); return true; } /// /// Reduce Nesting in switch statements by replacing break; in cases with the block exit, and extracting the default case /// Does not affect IL order /// private bool ReduceSwitchNesting(Block parentBlock, BlockContainer switchContainer, ILInstruction exitInst) { // break; from outer container cannot be brought inside the switch as the meaning would change if (exitInst is Leave leave && !leave.IsLeavingFunction) return false; // find the default section, and ensure it has only one incoming edge var switchInst = (SwitchInstruction)switchContainer.EntryPoint.Instructions.Single(); var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1) return false; if (defaultBlock.Parent != switchContainer) return false; // tally stats for heuristic from each case block int maxStatements = 0, maxDepth = 0; foreach (var section in switchInst.Sections) if (section != defaultSection && section.Body.MatchBranch(out var caseBlock) && caseBlock.Parent == switchContainer) UpdateStats(caseBlock, ref maxStatements, ref maxDepth); if (!ShouldReduceNesting(defaultBlock, maxStatements, maxDepth)) return false; Debug.Assert(defaultBlock.HasFlag(InstructionFlags.EndPointUnreachable)); // ensure the default case dominator tree has no exits (branches to other cases) var cfg = new ControlFlowGraph(switchContainer, context.CancellationToken); var defaultNode = cfg.GetNode(defaultBlock); var defaultTree = TreeTraversal.PreOrder(defaultNode, n => n.DominatorTreeChildren).ToList(); if (defaultTree.SelectMany(n => n.Successors).Any(n => !defaultNode.Dominates(n))) return false; if (defaultTree.Count > 1 && !(parentBlock.Parent is BlockContainer)) return false; context.Step("Extract default case of switch", switchContainer); // if the switch container is followed by an instruction, it must be a Leave from a try/pinned/etc or exitInst // When it's a leave from a container, it's better to let the extracted default block 'fall through' rather than duplicating whatever // instruction eventually follows the container if (parentBlock.Instructions.SecondToLastOrDefault() == switchContainer) { if (defaultBlock.Instructions.Last().MatchLeave(switchContainer)) defaultBlock.Instructions.Last().ReplaceWith(parentBlock.Instructions.Last()); parentBlock.Instructions.RemoveLast(); } // replace all break; statements with the exitInst var leaveInstructions = switchContainer.Descendants.Where(inst => inst.MatchLeave(switchContainer)); foreach (var leaveInst in leaveInstructions.ToArray()) leaveInst.ReplaceWith(exitInst.Clone()); // replace the default section branch with a break; defaultSection.Body.ReplaceWith(new Leave(switchContainer)); // remove all default blocks from the switch container var defaultBlocks = defaultTree.Select(c => (Block)c.UserData).ToList(); foreach (var block in defaultBlocks) switchContainer.Blocks.Remove(block); Debug.Assert(parentBlock.Instructions.Last() == switchContainer); parentBlock.Instructions.AddRange(defaultBlock.Instructions); // add any additional blocks from the default case to the parent container Debug.Assert(defaultBlocks[0] == defaultBlock); if (defaultBlocks.Count > 1) { var parentContainer = (BlockContainer)parentBlock.Parent; int insertAt = parentContainer.Blocks.IndexOf(parentBlock) + 1; foreach (var block in defaultBlocks.Skip(1)) parentContainer.Blocks.Insert(insertAt++, block); } return true; } /// /// Checks if an exit is a duplicable keyword exit (return; break; continue;) /// private bool CanDuplicateExit(ILInstruction exit, Block continueTarget, out ILInstruction keywordExit) { keywordExit = exit; if (exit != null && exit.MatchBranch(continueTarget)) return true; // keyword is continue if (!(exit is Leave leave && leave.Value.MatchNop())) return false; // don't duplicate valued returns if (leave.IsLeavingFunction || leave.TargetContainer.Kind != ContainerKind.Normal) return true; // keyword is return || break // leave from a try/pinned/lock etc, check if the target (the instruction following the target container) is duplicable, if so, set keywordExit to that ILInstruction leavingInst = leave.TargetContainer; Debug.Assert(!leavingInst.HasFlag(InstructionFlags.EndPointUnreachable)); while (!(leavingInst.Parent is Block b) || leavingInst == b.Instructions.Last()) { if (leavingInst.Parent is TryFinally tryFinally) { if (leavingInst.SlotInfo == TryFinally.FinallyBlockSlot) { // cannot duplicate leaves from finally containers Debug.Assert(leave.TargetContainer == tryFinally.FinallyBlock); //finally cannot have control flow return false; } if (tryFinally.HasFlag(InstructionFlags.EndPointUnreachable)) { // finally block changes return value/throws an exception? Yikes. Lets leave it alone Debug.Assert(tryFinally.FinallyBlock.HasFlag(InstructionFlags.EndPointUnreachable)); return false; } } else if (leavingInst.Parent is TryFault tryFault && leavingInst.SlotInfo == TryFault.FaultBlockSlot) { // cannot duplicate leaves from fault containers either Debug.Assert(leave.TargetContainer == tryFault.FaultBlock); return false; } leavingInst = leavingInst.Parent; Debug.Assert(!leavingInst.HasFlag(InstructionFlags.EndPointUnreachable)); Debug.Assert(!(leavingInst is ILFunction)); } var block = (Block)leavingInst.Parent; var targetInst = block.Instructions[block.Instructions.IndexOf(leavingInst) + 1]; return CanDuplicateExit(targetInst, continueTarget, out keywordExit); } /// /// Ensures the end point of a block is unreachable by duplicating and appending the [exit] instruction following the end point /// /// The instruction/block of interest /// The next instruction to be executed (provided inst does not exit) private void EnsureEndPointUnreachable(ILInstruction inst, ILInstruction fallthroughExit) { if (!(inst is Block block)) { Debug.Assert(inst.HasFlag(InstructionFlags.EndPointUnreachable)); return; } if (!block.HasFlag(InstructionFlags.EndPointUnreachable)) { context.Step("Duplicate block exit", fallthroughExit); block.Instructions.Add(fallthroughExit.Clone()); } } /// /// Removes a redundant block exit instruction. /// private void RemoveRedundantExit(Block block, ILInstruction implicitExit) { if (block.Instructions.Last().Match(implicitExit).Success) { context.Step("Remove redundant exit", block.Instructions.Last()); block.Instructions.RemoveLast(); } } /// /// Determines if an IfInstruction is an else-if and returns the preceeding (parent) IfInstruction /// /// [else-]if (parent-cond) else { ifInst } /// private IfInstruction GetElseIfParent(IfInstruction ifInst) { Debug.Assert(ifInst.Parent is Block); if (Block.Unwrap(ifInst.Parent) == ifInst && // only instruction in block ifInst.Parent.Parent is IfInstruction elseIfInst && // parent of block is an IfInstruction elseIfInst.FalseInst == ifInst.Parent) // part of the false branch not the true branch return elseIfInst; return null; } /// /// Adds a code path to the current heuristic tally /// private void UpdateStats(ILInstruction inst, ref int maxStatements, ref int maxDepth) { int numStatements = 0; ComputeStats(inst, ref numStatements, ref maxDepth, 0); maxStatements = Math.Max(numStatements, maxStatements); } /// /// Recursively computes the number of statements and maximum nested depth of an instruction /// private void ComputeStats(ILInstruction inst, ref int numStatements, ref int maxDepth, int currentDepth, bool isStatement = true) { if (isStatement) numStatements++; if (currentDepth > maxDepth) { Debug.Assert(isStatement); maxDepth = currentDepth; } // enumerate children statements and containers switch (inst) { case Block block: if (isStatement) numStatements--; // don't count blocks as statements // add each child as a statement (unless we're a named block) foreach (var child in block.Instructions) ComputeStats(child, ref numStatements, ref maxDepth, currentDepth, block.Kind != BlockKind.CallWithNamedArgs && block.Kind != BlockKind.CallInlineAssign); // final instruction as an expression ComputeStats(block.FinalInstruction, ref numStatements, ref maxDepth, currentDepth, false); break; case BlockContainer container: if (!isStatement) numStatements++; //always add a statement for a container in an expression var containerBody = container.EntryPoint; if (container.Kind == ContainerKind.For || container.Kind == ContainerKind.While) { Debug.Assert(isStatement); if (!container.MatchConditionBlock(container.EntryPoint, out _, out containerBody)) throw new NotSupportedException("Invalid condition block in loop."); } // don't count implicit leave. Can't avoid counting for loop initializers but close enough, for loops can have an extra statement of visual weight var lastInst = containerBody.Instructions.Last(); if ((container.Kind == ContainerKind.For || container.Kind == ContainerKind.DoWhile) && lastInst.MatchBranch(container.Blocks.Last()) || (container.Kind == ContainerKind.Loop || container.Kind == ContainerKind.While) && lastInst.MatchBranch(container.Blocks[0]) || container.Kind == ContainerKind.Normal && lastInst.MatchLeave(container) || container.Kind == ContainerKind.Switch) // SwitchInstructyion always counts as a statement anyway, so no need to count the container as well numStatements--; // add the nested body ComputeStats(containerBody, ref numStatements, ref maxDepth, currentDepth + 1); break; case IfInstruction ifInst when ifInst.ResultType == StackType.Void: Debug.Assert(isStatement); // nested then instruction ComputeStats(ifInst.TrueInst, ref numStatements, ref maxDepth, currentDepth + 1); // include all nested else-if instructions at the same depth var elseInst = ifInst.FalseInst; while (Block.Unwrap(elseInst) is IfInstruction elseIfInst) { numStatements++; ComputeStats(elseIfInst.TrueInst, ref numStatements, ref maxDepth, currentDepth + 1); elseInst = elseIfInst.FalseInst; } // include all nested else instruction ComputeStats(elseInst, ref numStatements, ref maxDepth, currentDepth + 1); break; case SwitchSection section: Debug.Assert(!isStatement); // labels are just children of the SwitchInstruction numStatements++; // add a statement for each case label // add all the case blocks at the current depth // most formatters indent switch blocks twice, but we don't want this heuristic to be based on formatting // so we remain conservative and only include the increase in depth from the container and not the labels if (section.Body.MatchBranch(out var caseBlock) && caseBlock.Parent == section.Parent.Parent.Parent) ComputeStats(caseBlock, ref numStatements, ref maxDepth, currentDepth); break; case ILFunction func: int bodyStatements = 0; int bodyMaxDepth = maxDepth; ComputeStats(func.Body, ref bodyStatements, ref bodyMaxDepth, currentDepth); if (bodyStatements >= 2) { // don't count inline functions numStatements += bodyStatements; maxDepth = bodyMaxDepth; } break; default: // search each child instruction. Containers will contain statements and contribute to stats int subStatements = 0; foreach (var child in inst.Children) ComputeStats(child, ref subStatements, ref maxDepth, currentDepth, false); numStatements += subStatements; if (isStatement && subStatements > 0) numStatements--; // don't count the first container, only its contents, because this statement is already counted break; } } /// /// Heuristic to determine whether it is worth duplicating exits into the preceeding sibling blocks (then/else-if/case) /// in order to reduce the nesting of inst by 1 /// /// The instruction heading the nested candidate block /// The number of statements in the largest sibling block /// The relative depth of the most nested statement in the sibling blocks /// private bool ShouldReduceNesting(ILInstruction inst, int maxStatements, int maxDepth) { int maxStatements2 = 0, maxDepth2 = 0; UpdateStats(inst, ref maxStatements2, ref maxDepth2); // if the max depth is 2, always reduce nesting (total depth 3 or more) // if the max depth is 1, reduce nesting if this block is the largest // otherwise reduce nesting only if this block is twice as large as any other return maxDepth2 >= 2 || maxDepth2 >= 1 && maxStatements2 > maxStatements || maxStatements2 >= 2 * maxStatements; } /// /// if (cond) { ...; exit; } else { ... } /// ...; /// -> /// if (cond) { ...; exit; } /// ...; /// ...; /// /// private void ExtractElseBlock(IfInstruction ifInst) { Debug.Assert(ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable)); var block = (Block)ifInst.Parent; var falseBlock = (Block)ifInst.FalseInst; context.Step("Extract else block", ifInst); int insertAt = block.Instructions.IndexOf(ifInst) + 1; for (int i = 0; i < falseBlock.Instructions.Count; i++) block.Instructions.Insert(insertAt++, falseBlock.Instructions[i]); ifInst.FalseInst = new Nop(); } private void EliminateRedundantTryFinally(TryFinally tryFinally, ILTransformContext context) { /* The C# compiler sometimes generates try-finally structures for fixed statements. After our transforms runs, these are redundant and can be removed. .try BlockContainer { Block IL_001a (incoming: 1) { PinnedRegion ... } } finally BlockContainer { Block IL_003e (incoming: 1) { leave IL_003e (nop) } } ==> PinnedRegion */ if (!(tryFinally.FinallyBlock is BlockContainer finallyContainer)) return; if (!finallyContainer.SingleInstruction().MatchLeave(finallyContainer)) return; // Finally is empty and redundant. But we'll delete the block only if there's a PinnedRegion. if (!(tryFinally.TryBlock is BlockContainer tryContainer)) return; if (tryContainer.Blocks.Count != 1) return; var tryBlock = tryContainer.Blocks[0]; if (tryBlock.Instructions.Count == 1) { if (tryBlock.Instructions[0] is PinnedRegion pinnedRegion) { context.Step("Removing try-finally around PinnedRegion", pinnedRegion); tryFinally.ReplaceWith(pinnedRegion); } } else if (tryBlock.Instructions.Count == 2) { if (tryBlock.Instructions[0] is PinnedRegion pinnedRegion && tryBlock.Instructions[1].MatchLeave(tryContainer)) { context.Step("Removing try-finally around PinnedRegion", pinnedRegion); tryFinally.ReplaceWith(pinnedRegion); } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Remove HasInitialValue from locals that are definitely assigned before every use /// (=the initial value is a dead store). /// /// In yield return generators, additionally removes dead 'V = null;' assignments. /// /// Additionally infers IType of stack slots that have StackType.Ref /// public class RemoveDeadVariableInit : IILTransform { public void Run(ILFunction function, ILTransformContext context) { ResetUsesInitialValueFlag(function, context); // Remove dead stores to variables that are never read from. // If the stored value has some side-effect, the value is unwrapped. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. // In yield return + async, the C# compiler tends to store null/default(T) to variables // when the variable goes out of scope. bool removeDeadStores = function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores; var variableQueue = new Queue(function.Variables); while (variableQueue.Count > 0) { var v = variableQueue.Dequeue(); if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) continue; if (!(v.RemoveIfRedundant || removeDeadStores)) continue; // Skip variables that are captured in a mcs yield state-machine // loads of these will only be visible after DelegateConstruction step. if (function.StateMachineCompiledWithMono && v.StateMachineField != null) continue; if (v.LoadCount != 0 || v.AddressCount != 0) continue; foreach (var stloc in v.StoreInstructions.OfType().ToArray()) { if (stloc.Parent is Block block) { context.Step($"Dead store to {v.Name}", stloc); if (SemanticHelper.IsPure(stloc.Value.Flags)) { block.Instructions.Remove(stloc); } else { stloc.ReplaceWith(stloc.Value); } if (stloc.Value is LdLoc ldloc) { variableQueue.Enqueue(ldloc.Variable); } } } } // Try to infer IType of stack slots that are of StackType.Ref: foreach (var v in function.Variables) { if (v.Kind == VariableKind.StackSlot && v.StackType == StackType.Ref && v.AddressCount == 0) { IType newType = null; // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example. foreach (var stloc in v.StoreInstructions.OfType()) { var inferredType = stloc.Value.InferType(context.TypeSystem); // cancel, if types of values do not match exactly if (newType != null && !newType.Equals(inferredType)) { newType = SpecialType.UnknownType; break; } newType = inferredType; } // Only overwrite existing type, if a "better" type was found. if (newType != null && newType != SpecialType.UnknownType) v.Type = newType; } } } internal static void ResetUsesInitialValueFlag(ILFunction function, ILTransformContext context) { var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); function.AcceptVisitor(visitor); foreach (var v in function.Variables) { if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { v.UsesInitialValue = false; } } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/RemoveInfeasiblePathTransform.cs ================================================ // Copyright (c) 2021 Daniel Grunwald, 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. #nullable enable using System.Diagnostics.CodeAnalysis; using System.Linq; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Block IL_0018 (incoming: *) { /// stloc s(ldc.i4 1) /// br IL_0019 /// } /// /// Block IL_0019 (incoming: > 1) { /// if (logic.not(ldloc s)) br IL_0027 /// br IL_001d /// } /// /// replace br IL_0019 with br IL_0027 /// class RemoveInfeasiblePathTransform : IILTransform { void IILTransform.Run(ILFunction function, ILTransformContext context) { foreach (var container in function.Descendants.OfType()) { bool changed = false; foreach (var block in container.Blocks) { changed |= DoTransform(block, context); } if (changed) { container.SortBlocks(deleteUnreachableBlocks: true); } } } private bool DoTransform(Block block, ILTransformContext context) { if (!MatchBlock1(block, out var s, out int value, out var br)) return false; if (!MatchBlock2(br.TargetBlock, s, value, out var exitInst)) return false; context.Step("RemoveInfeasiblePath", br); br.ReplaceWith(exitInst.Clone()); s.RemoveIfRedundant = true; return true; } // Block IL_0018 (incoming: *) { // stloc s(ldc.i4 1) // br IL_0019 // } private bool MatchBlock1(Block block, [NotNullWhen(true)] out ILVariable? variable, out int constantValue, [NotNullWhen(true)] out Branch? branch) { variable = null; constantValue = 0; branch = null; if (block.Instructions.Count != 2) return false; if (block.Instructions[0] is not StLoc { Variable: { Kind: VariableKind.StackSlot } s, Value: LdcI4 { Value: 0 or 1 } valueInst }) { return false; } if (block.Instructions[1] is not Branch br) return false; variable = s; constantValue = valueInst.Value; branch = br; return true; } // Block IL_0019 (incoming: > 1) { // if (logic.not(ldloc s)) br IL_0027 // br IL_001d // } bool MatchBlock2(Block block, ILVariable s, int constantValue, [NotNullWhen(true)] out ILInstruction? exitInst) { exitInst = null; if (block.Instructions.Count != 2) return false; if (block.IncomingEdgeCount <= 1) return false; if (!block.MatchIfAtEndOfBlock(out var load, out var trueInst, out var falseInst)) return false; if (!load.MatchLdLoc(s)) return false; exitInst = constantValue != 0 ? trueInst : falseInst; return exitInst is Branch or Leave { Value: Nop }; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/RemoveUnconstrainedGenericReferenceTypeCheck.cs ================================================ // Copyright (c) 2025 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. #nullable enable using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// [stloc address(...)] /// stloc temp(default.value ``0) /// if (comp.o(ldloc temp == ldnull)) Block IL_002a { /// stloc temp(ldobj ``0(ldloc address)) /// stloc address(ldloca temp) /// } /// stloc V_i(expr_i) /// ...(constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc address), ldloc V_i ...))... /// ///=> /// /// [stloc address(...)] /// stloc address(ldobj.if.ref(ldloc address)) /// stloc V_i(expr_i) /// ...(constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc address), ldloc V_i ...))... /// /// Then ldobj.if.ref in the call is redundant because any object reference was already loaded into an immutable temporary. /// So we can removed and inlining of the arguments (V_i) becomes possible. /// /// Finally the newly created ldobj.if.ref is inlined into the place where the old ldobj.if.ref was. /// /// => /// /// [stloc address(...)] /// ...(constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc address), expr_i ...))... /// /// class RemoveUnconstrainedGenericReferenceTypeCheck : IStatementTransform { void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { int startPos = pos; // stloc temp(default.value ``0) if (!block.Instructions[pos].MatchStLoc(out var temp, out var defaultValue)) return; if (!defaultValue.MatchDefaultValue(out var type)) return; if (temp.StoreCount != 2 || temp.LoadCount != 1 || temp.AddressCount != 1) return; pos++; // if (comp.o(ldloc temp == ldnull)) Block IL_002a { // stloc temp(ldobj ``0(ldloc address)) // stloc address(ldloca temp) // } if (block.Instructions.ElementAtOrDefault(pos) is not IfInstruction ifInst) return; if (!ifInst.Condition.MatchCompEqualsNull(out var tempLoadForNullCheck)) return; if (!tempLoadForNullCheck.MatchLdLoc(temp)) return; if (ifInst.TrueInst is not Block dereferenceBlock) return; if (ifInst.FalseInst is not Nop) return; if (dereferenceBlock.Instructions is not [StLoc tempStore, StLoc addressReassign]) return; if (tempStore.Variable != temp) return; if (!tempStore.Value.MatchLdObj(out var addressLoadForLdObj, type)) return; if (!addressLoadForLdObj.MatchLdLoc(addressReassign.Variable)) return; if (!addressReassign.Value.MatchLdLoca(temp)) return; var address = addressReassign.Variable; if (address.StoreCount != 2 || address.LoadCount != 2 || address.AddressCount != 0) return; pos++; // pos now is the first store to V_i // ...(constrained[``0].callvirt Method(ldobj.if.ref ``0(ldloc address), ldloc V_i ...))... var callTarget = address.LoadInstructions.Single(l => addressLoadForLdObj != l); if (callTarget.Parent is not LdObjIfRef { Parent: CallVirt call } ldobjIfRef) return; if (call.Arguments.Count == 0 || call.Arguments[0] != ldobjIfRef || !type.Equals(call.ConstrainedTo)) return; ILInstruction containingStmt = call; while (containingStmt.Parent != block) { if (containingStmt.Parent == null) return; containingStmt = containingStmt.Parent; } if (containingStmt.ChildIndex < pos) return; // check if we can inline all temporaries used in the call: int temporaryInitIndex = containingStmt.ChildIndex - 1; List<(ILInstruction, ILInstruction)> replacements = new(); for (int argIndex = call.Arguments.Count - 1; argIndex > 0 && temporaryInitIndex >= pos; argIndex--) { var argument = call.Arguments[argIndex]; switch (argument) { case LdLoc load: if (block.Instructions[temporaryInitIndex].MatchStLoc(load.Variable, out var expr) && ILInlining.VariableCanBeUsedForInlining(load.Variable)) { if (!ILInlining.CanMoveIntoCallVirt(expr, containingStmt, call, argument)) { return; } replacements.Add((argument, expr)); temporaryInitIndex--; } break; } } // all stores to V_i processed? if (temporaryInitIndex != pos - 1) { return; } context.Step("RemoveUnconstrainedGenericReferenceTypeCheck", block.Instructions[startPos]); foreach (var (argument, expr) in replacements) { argument.ReplaceWith(expr); } block.Instructions.RemoveRange(startPos, containingStmt.ChildIndex - startPos); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Generic; using System.Linq; using System.Threading; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Live range splitting for IL variables. /// public class SplitVariables : IILTransform { public void Run(ILFunction function, ILTransformContext context) { var groupStores = new GroupStores(function, context.CancellationToken); function.Body.AcceptVisitor(groupStores); // Replace analyzed variables with their split versions: foreach (var inst in function.Descendants.OfType()) { if (groupStores.IsAnalyzedVariable(inst.Variable)) { inst.Variable = groupStores.GetNewVariable(inst); } } function.Variables.RemoveDead(); } static bool IsCandidateVariable(ILVariable v) { switch (v.Kind) { case VariableKind.Local: foreach (var ldloca in v.AddressInstructions) { if (DetermineAddressUse(ldloca, ldloca.Variable) == AddressUse.Unknown) { // If we don't understand how the address is being used, // we can't split the variable. return false; } } return true; case VariableKind.StackSlot: // stack slots: are already split by construction, // except for the locals-turned-stackslots in async functions // or stack slots handled by the infeasible path transform if (v.Function.IsAsync || v.RemoveIfRedundant) goto case VariableKind.Local; else return false; default: // parameters: avoid splitting parameters // pinned locals: must not split (doing so would extend the life of the pin to the end of the method) return false; } } enum AddressUse { Unknown, /// /// Address is immediately used for reading and/or writing, /// without the possibility of the variable being directly stored to (via 'stloc') /// in between the 'ldloca' and the use of the address. /// Immediate, /// /// We support some limited form of ref locals referring to a target variable, /// without giving up splitting of the target variable. /// Requirements: /// * the ref local is single-assignment /// * the ref local is initialized directly with 'ldloca target; stloc ref_local', /// not a derived pointer (e.g. 'ldloca target; ldflda F; stloc ref_local'). /// * all uses of the ref_local are immediate. /// There may be stores to the target variable in between the 'stloc ref_local' and its uses, /// but we handle that case by treating each use of the ref_local as an address access /// of the target variable (as if the ref_local was eliminated via copy propagation). /// WithSupportedRefLocals, } static AddressUse DetermineAddressUse(ILInstruction addressLoadingInstruction, ILVariable targetVar) { switch (addressLoadingInstruction.Parent) { case LdObj _: case StObj stobj when stobj.Target == addressLoadingInstruction: return AddressUse.Immediate; case LdObjIfRef: // This is either an immediate use, or we need to check how the parent uses the address. return DetermineAddressUse(addressLoadingInstruction.Parent, targetVar); case LdFlda ldflda: return DetermineAddressUse(ldflda, targetVar); case Await await: // GetAwaiter() may write to the struct, but shouldn't store the address for later use return AddressUse.Immediate; case CallInstruction call: return HandleCall(addressLoadingInstruction, targetVar, call); case StLoc stloc when stloc.Variable.IsSingleDefinition: // Address stored in local variable: also check all uses of that variable. if (!(stloc.Variable.Kind == VariableKind.StackSlot || stloc.Variable.Kind == VariableKind.Local)) return AddressUse.Unknown; var value = stloc.Value; while (value is LdFlda ldFlda) { value = ldFlda.Target; } if (value.OpCode != OpCode.LdLoca) { // GroupStores only handles ref-locals correctly when they are supported by GetAddressLoadForRefLocalUse(), // which only works for ldflda*(ldloca) return AddressUse.Unknown; } foreach (var load in stloc.Variable.LoadInstructions) { if (DetermineAddressUse(load, targetVar) != AddressUse.Immediate) return AddressUse.Unknown; } return AddressUse.WithSupportedRefLocals; default: return AddressUse.Unknown; } } static AddressUse HandleCall(ILInstruction addressLoadingInstruction, ILVariable targetVar, CallInstruction call) { // Address is passed to method. // We'll assume the method only uses the address locally, // unless we can see an address being returned from the method: IType returnType = (call is NewObj) ? call.Method.DeclaringType : call.Method.ReturnType; if (returnType.IsByRefLike) { // We exclude Span.Item[int index] and ReadOnlySpan.Item[int index], because it is known that this // or members of this cannot be returned by the method. if (!IsSpanOfTIndexerAccessor(call.Method)) { // If the address is returned from the method, it check whether it's consumed immediately. // This can still be fine, as long as we also check the consumer's other arguments for 'stloc targetVar'. if (DetermineAddressUse(call, targetVar) != AddressUse.Immediate) return AddressUse.Unknown; } } foreach (var p in call.Method.Parameters) { // catch "out Span" and similar if (p.Type.SkipModifiers() is ByReferenceType brt && brt.ElementType.IsByRefLike) return AddressUse.Unknown; } // ensure there's no 'stloc target' in between the ldloca and the call consuming the address for (int i = addressLoadingInstruction.ChildIndex + 1; i < call.Arguments.Count; i++) { foreach (var inst in call.Arguments[i].Descendants) { if (inst is StLoc store && store.Variable == targetVar) return AddressUse.Unknown; } } return AddressUse.Immediate; } static bool IsSpanOfTIndexerAccessor(IMethod method) { var declaringType = method.DeclaringType; if (!declaringType.IsKnownType(KnownTypeCode.SpanOfT) && !declaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) return false; return method.AccessorOwner is IProperty { IsIndexer: true, Name: "Item", Parameters: [var param], ReturnType: ByReferenceType { ElementType: var rt } } && param.Type.IsKnownType(KnownTypeCode.Int32) && rt.Equals(declaringType.TypeArguments[0]); } /// /// Given 'ldloc ref_local' and 'ldloca target; stloc ref_local', returns the ldloca. /// This function must return a non-null LdLoca for every use of a SupportedRefLocal. /// static LdLoca GetAddressLoadForRefLocalUse(LdLoc ldloc) { if (!ldloc.Variable.IsSingleDefinition) return null; // only single-definition variables can be supported ref locals var store = ldloc.Variable.StoreInstructions.SingleOrDefault(); if (store is StLoc stloc) { var value = stloc.Value; while (value is LdFlda ldFlda) { value = ldFlda.Target; } return value as LdLoca; } return null; } /// /// Use the union-find structure to merge /// /// /// Instructions in a group are stores to the same variable that must stay together (cannot be split). /// class GroupStores : ReachingDefinitionsVisitor { readonly UnionFind unionFind = new UnionFind(); /// /// For each uninitialized variable, one representative instruction that /// potentially observes the unintialized value of the variable. /// Used to merge together all such loads of the same uninitialized value. /// readonly Dictionary uninitVariableUsage = new Dictionary(); public GroupStores(ILFunction scope, CancellationToken cancellationToken) : base(scope, IsCandidateVariable, cancellationToken) { } protected internal override void VisitLdLoc(LdLoc inst) { base.VisitLdLoc(inst); HandleLoad(inst); var refLocalAddressLoad = GetAddressLoadForRefLocalUse(inst); if (refLocalAddressLoad != null) { // SupportedRefLocal: act as if we copy-propagated the ldloca // to the point of use: HandleLoad(refLocalAddressLoad); } } protected internal override void VisitLdLoca(LdLoca inst) { base.VisitLdLoca(inst); HandleLoad(inst); } void HandleLoad(IInstructionWithVariableOperand inst) { if (IsAnalyzedVariable(inst.Variable)) { if (IsPotentiallyUninitialized(state, inst.Variable)) { // merge all uninit loads together: if (uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad)) { unionFind.Merge(inst, uninitLoad); } else { uninitVariableUsage.Add(inst.Variable, inst); } } foreach (var store in GetStores(state, inst.Variable)) { unionFind.Merge(inst, (IInstructionWithVariableOperand)store); } } } readonly Dictionary newVariables = new Dictionary(); /// /// Gets the new variable for a LdLoc, StLoc or TryCatchHandler instruction. /// internal ILVariable GetNewVariable(IInstructionWithVariableOperand inst) { var representative = unionFind.Find(inst); ILVariable v; if (!newVariables.TryGetValue(representative, out v)) { v = new ILVariable(inst.Variable.Kind, inst.Variable.Type, inst.Variable.StackType, inst.Variable.Index); v.Name = inst.Variable.Name; v.HasGeneratedName = inst.Variable.HasGeneratedName; v.StateMachineField = inst.Variable.StateMachineField; v.InitialValueIsInitialized = inst.Variable.InitialValueIsInitialized; v.UsesInitialValue = false; // we'll set UsesInitialValue when we encounter an uninit load v.RemoveIfRedundant = inst.Variable.RemoveIfRedundant; newVariables.Add(representative, v); inst.Variable.Function.Variables.Add(v); } if (inst.Variable.UsesInitialValue && uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad) && uninitLoad == inst) { v.UsesInitialValue = true; } return v; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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. #nullable enable using System; using System.Diagnostics; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// IL transform that runs on a sequence of statements within a block. /// /// /// Interleaving different statement-combining transforms on a per-statement level /// improves detection of nested constructs. /// For example, array initializers can assume that each element assignment was already /// reduced to a single statement, even if the element contains a high-level construct /// detected by a different transform (e.g. object initializer). /// public interface IStatementTransform { /// /// Runs the transform on the statements within a block. /// /// Note: the transform may only modify block.Instructions[pos..]. /// The transform will be called repeatedly for pos=block.Instructions.Count-1, pos=block.Instructions.Count-2, ..., pos=0. /// /// The current block. /// The starting position where the transform is allowed to work. /// Additional parameters. /// /// Instructions prior to block.Instructions[pos] must not be modified. /// It is valid to read such instructions, but not recommended as those have not been transformed yet. /// /// This function is only called on control-flow blocks with unreachable end-point. /// Thus, the last instruction in the block always must have the EndPointUnreachable flag. /// ==> Instructions with reachable end can't be last. Some transforms use this to save some bounds checks. /// void Run(Block block, int pos, StatementTransformContext context); } /// /// Parameter class holding various arguments for . /// public class StatementTransformContext : ILTransformContext { public BlockTransformContext BlockContext { get; } public StatementTransformContext(BlockTransformContext blockContext) : base(blockContext) { this.BlockContext = blockContext ?? throw new ArgumentNullException(nameof(blockContext)); } /// /// Gets the block on which the transform is running. /// public Block Block => BlockContext.Block; internal bool rerunCurrentPosition; internal int? rerunPosition; /// /// After the current statement transform has completed, /// do not continue with the next statement transform at the same position. /// Instead, re-run all statement transforms (including the current transform) starting at the specified position. /// public void RequestRerun(int pos) { if (rerunPosition == null || pos > rerunPosition) { rerunPosition = pos; } } /// /// After the current statement transform has completed, /// repeat all statement transforms on the current position. /// public void RequestRerun() { rerunCurrentPosition = true; } } /// /// Block transform that runs a list of statement transforms. /// public class StatementTransform : IBlockTransform { readonly IStatementTransform[] children; public StatementTransform(params IStatementTransform[] children) { this.children = children; } public void Run(Block block, BlockTransformContext context) { var ctx = new StatementTransformContext(context); int pos = 0; if (context.IndexOfFirstAlreadyTransformedInstruction == 0) { return; } ctx.rerunPosition = context.IndexOfFirstAlreadyTransformedInstruction - 1; while (pos >= 0) { if (ctx.rerunPosition != null) { Debug.Assert(ctx.rerunPosition >= pos); #if DEBUG for (; pos < ctx.rerunPosition; ++pos) { block.Instructions[pos].ResetDirty(); } #else pos = ctx.rerunPosition.Value; #endif Debug.Assert(pos == ctx.rerunPosition); ctx.rerunPosition = null; } foreach (var transform in children) { transform.Run(block, pos, ctx); #if DEBUG block.Instructions[pos].CheckInvariant(ILPhase.Normal); for (int i = Math.Max(0, pos - 100); i < pos; ++i) { if (block.Instructions[i].IsDirty) { Debug.Fail($"{transform.GetType().Name} modified an instruction before pos"); } } #endif if (ctx.rerunCurrentPosition) { ctx.rerunCurrentPosition = false; ctx.RequestRerun(pos); } if (ctx.rerunPosition != null) { break; } } if (ctx.rerunPosition == null) { pos--; } } // This invariant can be surprisingly expensive to check if the block has thousands // of instructions and is frequently modified by transforms (invalidating the flags each time) // so we'll check this only once at the end of the block. Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable)); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/Stepper.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Exception thrown when an IL transform runs into the . /// public class StepLimitReachedException : Exception { } /// /// Helper class that manages recording transform steps. /// public class Stepper { /// /// Gets whether stepping of built-in transforms is supported in this build of ICSharpCode.Decompiler. /// Usually only debug builds support transform stepping. /// public static bool SteppingAvailable { get { #if STEP return true; #else return false; #endif } } public IList Steps => steps; public int StepLimit { get; set; } = int.MaxValue; public bool IsDebug { get; set; } public class Node { public string Description { get; } public ILInstruction? Position { get; set; } /// /// BeginStep is inclusive. /// public int BeginStep { get; set; } /// /// EndStep is exclusive. /// public int EndStep { get; set; } public IList Children { get; } = new List(); public Node(string description) { Description = description; } } readonly Stack groups; readonly IList steps; int step = 0; public Stepper() { steps = new List(); groups = new Stack(); } /// /// Call this method immediately before performing a transform step. /// Used for debugging the IL transforms. Has no effect in release mode. /// /// May throw in debug mode. /// [DebuggerStepThrough] public void Step(string description, ILInstruction? near = null) { StepInternal(description, near); } [DebuggerStepThrough] private Node StepInternal(string description, ILInstruction? near) { if (step == StepLimit) { if (IsDebug) Debugger.Break(); else throw new StepLimitReachedException(); } var stepNode = new Node($"{step}: {description}") { Position = near, BeginStep = step, EndStep = step + 1 }; var p = groups.PeekOrDefault(); if (p != null) p.Children.Add(stepNode); else steps.Add(stepNode); step++; return stepNode; } [DebuggerStepThrough] public void StartGroup(string description, ILInstruction? near = null) { groups.Push(StepInternal(description, near)); } public void EndGroup(bool keepIfEmpty = false) { var node = groups.Pop(); if (!keepIfEmpty && node.Children.Count == 0) { var col = groups.PeekOrDefault()?.Children ?? steps; Debug.Assert(col.Last() == node); col.RemoveAt(col.Count - 1); } node.EndStep = step; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs ================================================ // Copyright (c) 2017 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. /// public class SwitchOnNullableTransform : IILTransform { public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LiftNullables) return; HashSet changedContainers = new HashSet(); foreach (var block in function.Descendants.OfType()) { bool changed = false; for (int i = block.Instructions.Count - 1; i >= 0; i--) { SwitchInstruction newSwitch; if (MatchSwitchOnNullable(block.Instructions, i, out newSwitch)) { newSwitch.AddILRange(block.Instructions[i - 2]); block.Instructions[i + 1].ReplaceWith(newSwitch); block.Instructions.RemoveRange(i - 2, 3); i -= 2; changed = true; continue; } if (MatchRoslynSwitchOnNullable(block.Instructions, i, out newSwitch)) { newSwitch.AddILRange(block.Instructions[i]); newSwitch.AddILRange(block.Instructions[i + 1]); block.Instructions[i].ReplaceWith(newSwitch); block.Instructions.RemoveAt(i + 1); changed = true; continue; } } if (!changed) continue; SwitchDetection.SimplifySwitchInstruction(block, context); if (block.Parent is BlockContainer container) changedContainers.Add(container); } foreach (var container in changedContainers) container.SortBlocks(deleteUnreachableBlocks: true); } /// /// Matches legacy C# switch on nullable. /// bool MatchSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch) { newSwitch = null; // match first block: // stloc tmp(ldloca switchValueVar) // stloc switchVariable(call GetValueOrDefault(ldloc tmp)) // if (logic.not(call get_HasValue(ldloc tmp))) br nullCaseBlock // br switchBlock if (i < 2) return false; if (!instructions[i - 2].MatchStLoc(out var tmp, out var ldloca) || !instructions[i - 1].MatchStLoc(out var switchVariable, out var getValueOrDefault) || !instructions[i].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!tmp.IsSingleDefinition || tmp.LoadCount != 2) return false; if (!switchVariable.IsSingleDefinition || switchVariable.LoadCount != 1) return false; if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock)) return false; if (!ldloca.MatchLdLoca(out var switchValueVar)) return false; if (!condition.MatchLogicNot(out var getHasValue)) return false; if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction getValueOrDefaultArg)) return false; if (!NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction getHasValueArg)) return false; if (!(getHasValueArg.MatchLdLoc(tmp) && getValueOrDefaultArg.MatchLdLoc(tmp))) return false; // match second block: switchBlock // switch (ldloc switchVariable) { // case [0..1): br caseBlock1 // ... more cases ... // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock // } if (switchBlock.Instructions.Count != 1 || switchBlock.IncomingEdgeCount != 1) return false; if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst)) return false; var nullableType = ((Call)getHasValue).Method.DeclaringType; newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, new LdLoc(switchValueVar), nullableType); return true; } static SwitchInstruction BuildLiftedSwitch(Block nullCaseBlock, SwitchInstruction switchInst, ILInstruction switchValue, IType nullableType) { SwitchInstruction newSwitch = new SwitchInstruction(switchValue); newSwitch.IsLifted = true; newSwitch.Type = nullableType; newSwitch.Sections.AddRange(switchInst.Sections); newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); return newSwitch; } /// /// Matches Roslyn C# switch on nullable. /// bool MatchRoslynSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch) { newSwitch = null; // match first block: // if (logic.not(call get_HasValue(target))) br nullCaseBlock // br switchBlock if (!instructions[i].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock)) return false; if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction target) || !SemanticHelper.IsPure(target.Flags)) return false; // match second block: switchBlock // note: I have seen cases where switchVar is inlined into the switch. // stloc switchVar(call GetValueOrDefault(ldloca tmp)) // switch (ldloc switchVar) { // case [0..1): br caseBlock1 // ... more cases ... // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock // } if (switchBlock.IncomingEdgeCount != 1) return false; SwitchInstruction switchInst; switch (switchBlock.Instructions.Count) { case 2: { // this is the normal case described by the pattern above if (!switchBlock.Instructions[0].MatchStLoc(out var switchVar, out var getValueOrDefault)) return false; if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1) return false; if (!(NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction target2) && target2.Match(target).Success)) return false; if (!(switchBlock.Instructions[1] is SwitchInstruction si)) return false; switchInst = si; break; } case 1: { // this is the special case where `call GetValueOrDefault(ldloca tmp)` is inlined into the switch. if (!(switchBlock.Instructions[0] is SwitchInstruction si)) return false; if (!(NullableLiftingTransform.MatchGetValueOrDefault(si.Value, out ILInstruction target2) && target2.Match(target).Success)) return false; switchInst = si; break; } default: { return false; } } ILInstruction switchValue; if (target.MatchLdLoca(out var v)) switchValue = new LdLoc(v).WithILRange(target); else switchValue = new LdObj(target, ((CallInstruction)getHasValue).Method.DeclaringType); var nullableType = ((Call)getHasValue).Method.DeclaringType; newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue, nullableType); return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs ================================================ // Copyright (c) 2017 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; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { using HashtableInitializer = Dictionary Labels, IfInstruction JumpToNext, Block ContainingBlock, Block Previous, Block Next, bool Transformed)>; /// /// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. /// public class SwitchOnStringTransform : IILTransform { ILTransformContext context; private readonly SwitchAnalysis analysis = new SwitchAnalysis(); public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.SwitchStatementOnString) return; this.context = context; BlockContainer body = (BlockContainer)function.Body; var hashtableInitializers = ScanHashtableInitializerBlocks(body.EntryPoint); HashSet changedContainers = new HashSet(); foreach (var block in function.Descendants.OfType()) { bool changed = false; if (block.IncomingEdgeCount == 0) continue; for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (SimplifyCascadingIfStatements(block.Instructions, ref i)) { changed = true; continue; } if (SimplifyCSharp1CascadingIfStatements(block.Instructions, ref i)) { changed = true; continue; } if (MatchLegacySwitchOnStringWithHashtable(block, hashtableInitializers, ref i)) { changed = true; continue; } if (MatchLegacySwitchOnStringWithDict(block.Instructions, ref i)) { changed = true; continue; } if (MatchRoslynSwitchOnString(block.Instructions, ref i)) { changed = true; continue; } if (MatchRoslynSwitchOnStringUsingLengthAndChar(block, i)) { changed = true; continue; } } if (!changed) continue; SwitchDetection.SimplifySwitchInstruction(block, context); if (block.Parent is BlockContainer container) changedContainers.Add(container); } var omittedBlocks = new Dictionary(); // Remove all transformed hashtable initializers from the entrypoint. foreach (var item in hashtableInitializers) { var (labels, jumpToNext, containingBlock, previous, next, transformed) = item.Value; if (!transformed) continue; if (!omittedBlocks.TryGetValue(previous, out var actual)) actual = previous; context.Step("Remove hashtable initializer", actual); if (jumpToNext != null) { actual.Instructions.SecondToLastOrDefault().ReplaceWith(jumpToNext); } actual.Instructions.LastOrDefault().ReplaceWith(new Branch(next)); omittedBlocks.Add(containingBlock, previous); changedContainers.Add(body); } // If all initializer where removed, remove the initial null check as well. if (hashtableInitializers.Count > 0 && omittedBlocks.Count == hashtableInitializers.Count && body.EntryPoint.Instructions.Count == 2) { if (body.EntryPoint.Instructions[0] is IfInstruction ifInst && ifInst.TrueInst.MatchBranch(out var beginOfMethod) && body.EntryPoint.Instructions[1].MatchBranch(beginOfMethod)) { context.Step("Remove initial null check", body); body.EntryPoint.Instructions.RemoveAt(0); } } foreach (var container in changedContainers) container.SortBlocks(deleteUnreachableBlocks: true); } HashtableInitializer ScanHashtableInitializerBlocks(Block entryPoint) { var hashtables = new HashtableInitializer(); if (entryPoint.Instructions.Count != 2) return hashtables; // match first block: checking compiler-generated Hashtable for null // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-1) != ldnull)) br switchHeadBlock // br tableInitBlock if (!(entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branchToSwitchHead))) return hashtables; if (!entryPoint.Instructions[1].MatchBranch(out var tableInitBlock)) return hashtables; if (!(condition.MatchCompNotEquals(out var left, out var right) && right.MatchLdNull() && MatchDictionaryFieldLoad(left, IsNonGenericHashtable, out var dictField, out var dictionaryType))) return hashtables; if (!branchToSwitchHead.MatchBranch(out var switchHead)) return hashtables; // match second block: initialization of compiler-generated Hashtable // stloc table(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f loadFactor)) // call Add(ldloc table, ldstr value, box System.Int32(ldc.i4 index)) // ... more calls to Add ... // volatile.stobj System.Collections.Hashtable(ldsflda $$method0x600003f - 1, ldloc table) // br switchHeadBlock if (tableInitBlock.IncomingEdgeCount != 1 || tableInitBlock.Instructions.Count < 3) return hashtables; Block previousBlock = entryPoint; while (tableInitBlock != null) { if (!ExtractStringValuesFromInitBlock(tableInitBlock, out var stringValues, out var blockAfterThisInitBlock, dictionaryType, dictField, true)) break; var nextHashtableInitHead = tableInitBlock.Instructions.SecondToLastOrDefault() as IfInstruction; hashtables.Add(dictField, (stringValues, nextHashtableInitHead, tableInitBlock, previousBlock, blockAfterThisInitBlock, false)); previousBlock = tableInitBlock; // if there is another IfInstruction before the end of the block, it might be a jump to the next hashtable init block. // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-2) != ldnull)) br switchHeadBlock if (nextHashtableInitHead != null) { if (!(nextHashtableInitHead.Condition.MatchCompNotEquals(out left, out right) && right.MatchLdNull() && MatchDictionaryFieldLoad(left, IsNonGenericHashtable, out var nextDictField, out _))) break; if (!nextHashtableInitHead.TrueInst.MatchBranch(switchHead)) break; tableInitBlock = blockAfterThisInitBlock; dictField = nextDictField; } else { break; } } return hashtables; } bool SimplifyCascadingIfStatements(InstructionCollection instructions, ref int i) { // match first block: checking switch-value for null or first value (Roslyn) // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock // -or- // if (comp(ldloc switchValueVar == ldnull)) br defaultBlock if (!instructions[i].MatchIfInstruction(out var condition, out var firstBlockOrDefaultJump)) return false; var nextCaseJump = instructions[i + 1]; while (condition.MatchLogicNot(out var arg)) { condition = arg; ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump); } // match call to operator ==(string, string) if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue, out bool isVBCompareString)) return false; if (isVBCompareString) { ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump); } if (firstBlockOrDefaultJump.MatchBranch(out var firstBlock)) { // success } else if (firstBlockOrDefaultJump.MatchLeave(out _)) { firstBlock = null; // success } else { return false; } var values = new List<(string, ILInstruction)>(); var uniqueValues = new HashSet(); int numberOfUniqueMatchesWithCurrentVariable = 0; HashSet caseBlocks = new HashSet(); caseBlocks.Add((Block)instructions[i].Parent); bool AddSwitchSection(string value, ILInstruction inst) { if (!uniqueValues.Add(value)) return false; numberOfUniqueMatchesWithCurrentVariable++; values.Add((value, inst)); return true; } ILInstruction switchValue = null; if (isVBCompareString && string.IsNullOrEmpty(firstBlockValue)) { if (!AddSwitchSection(null, firstBlock ?? firstBlockOrDefaultJump)) return false; if (!AddSwitchSection(string.Empty, firstBlock ?? firstBlockOrDefaultJump)) return false; } else { if (!AddSwitchSection(firstBlockValue, firstBlock ?? firstBlockOrDefaultJump)) return false; } bool removeExtraLoad = false; bool keepAssignmentBefore = false; if (i >= 1 && instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) { // stloc switchValueVar(switchValue) // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock // Newer versions of Roslyn use extra variables: if (i >= 2 && switchValue.MatchLdLoc(out var otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 1 && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out var newSwitchValue)) { switchValue = newSwitchValue; removeExtraLoad = true; } } else if (i >= 1 && instructions[i - 1] is StLoc stloc) { if (stloc.Value.MatchLdLoc(switchValueVar)) { // in case of optimized legacy code there are two stlocs: // stloc otherSwitchValueVar(ldloc switchValue) // stloc switchValueVar(ldloc otherSwitchValueVar) // if (call op_Equality(ldloc otherSwitchValueVar, ldstr value)) br firstBlock var otherSwitchValueVar = switchValueVar; switchValueVar = stloc.Variable; numberOfUniqueMatchesWithCurrentVariable = 0; if (i >= 2 && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) { removeExtraLoad = true; } else { switchValue = new LdLoc(otherSwitchValueVar); } } else { // Variable before the start of the switch is not related to the switch. keepAssignmentBefore = true; switchValue = new LdLoc(switchValueVar); } } else { // Instruction before the start of the switch is not related to the switch. keepAssignmentBefore = true; switchValue = new LdLoc(switchValueVar); } if (!switchValueVar.Type.IsKnownType(KnownTypeCode.String)) { if (!context.Settings.SwitchOnReadOnlySpanChar) return false; if (!switchValueVar.Type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && !switchValueVar.Type.IsKnownType(KnownTypeCode.SpanOfT)) { return false; } } // if instruction must be followed by a branch to the next case if (!nextCaseJump.MatchBranch(out Block currentCaseBlock)) return false; // extract all cases and add them to the values list. ILInstruction nextCaseBlock; do { nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out bool emptyStringEqualsNull, out ILInstruction block); if (nextCaseBlock == null) break; if (emptyStringEqualsNull && string.IsNullOrEmpty(value)) { if (!AddSwitchSection(null, block)) return false; if (!AddSwitchSection(string.Empty, block)) return false; } else { if (!AddSwitchSection(value, block)) return false; } caseBlocks.Add(currentCaseBlock); currentCaseBlock = nextCaseBlock as Block; } while (currentCaseBlock != null); // We didn't find enough cases, exit if (values.Count < 3) return false; context.Step(nameof(SimplifyCascadingIfStatements), instructions[i]); // if the switchValueVar is used in other places as well, do not eliminate the store. if (switchValueVar.LoadCount > numberOfUniqueMatchesWithCurrentVariable || !ValidateUsesOfSwitchValueVariable(switchValueVar, caseBlocks)) { keepAssignmentBefore = true; removeExtraLoad = false; // prevent loads from being deleted after detecting that // we have to keep the assignment before the switch statement switchValue = new LdLoc(switchValueVar); } int offset = firstBlock == null ? 1 : 0; var sections = new List(values.Skip(offset).SelectWithIndex((index, s) => new SwitchSection { Labels = new LongSet(index), Body = s.Item2 is Block b ? new Branch(b) : s.Item2.Clone() })); sections.Add(new SwitchSection { Labels = new LongSet(new LongInterval(0, sections.Count)).Invert(), Body = currentCaseBlock != null ? (ILInstruction)new Branch(currentCaseBlock) : new Leave((BlockContainer)nextCaseBlock) }); var stringToInt = new StringToInt(switchValue, values.Skip(offset).Select(item => item.Item1).ToArray(), switchValueVar.Type); var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); if (removeExtraLoad) { inst.AddILRange(instructions[i - 2]); instructions[i - 2].ReplaceWith(inst); instructions.RemoveRange(i - 1, 3); i -= 2; } else { if (keepAssignmentBefore) { inst.AddILRange(instructions[i]); instructions[i].ReplaceWith(inst); instructions.RemoveAt(i + 1); } else { inst.AddILRange(instructions[i - 1]); instructions[i - 1].ReplaceWith(inst); instructions.RemoveRange(i, 2); i--; } } return true; } private bool ValidateUsesOfSwitchValueVariable(ILVariable switchValueVar, HashSet caseBlocks) { foreach (var use in switchValueVar.LoadInstructions) { bool isValid = false; foreach (var caseBlock in caseBlocks) { if (use.IsDescendantOf(caseBlock)) isValid = true; } if (!isValid) return false; } return true; } bool SimplifyCSharp1CascadingIfStatements(InstructionCollection instructions, ref int i) { if (i < 1) return false; // match first block: // stloc switchValueVar(ldloc temp) // if (comp(ldloc temp == ldnull)) br defaultBlock // br isInternedBlock if (!(instructions[i].MatchIfInstruction(out var condition, out var defaultBlockJump))) return false; if (!instructions[i + 1].MatchBranch(out var isInternedBlock)) return false; if (!defaultBlockJump.MatchBranch(out var defaultOrNullBlock)) return false; if (!(condition.MatchCompEqualsNull(out var tempLoad) && tempLoad.MatchLdLoc(out var temp))) return false; if (!(temp.Kind == VariableKind.StackSlot && temp.LoadCount == 2)) return false; if (!(instructions[i - 1].MatchStLoc(out var switchValueVar, out var switchValue) && switchValue.MatchLdLoc(temp))) return false; // match isInternedBlock: // stloc switchValueVarCopy(call IsInterned(ldloc switchValueVar)) // if (comp(ldloc switchValueVarCopy == ldstr "case1")) br caseBlock1 // br caseHeader2 if (isInternedBlock.IncomingEdgeCount != 1 || isInternedBlock.Instructions.Count != 3) return false; if (!(isInternedBlock.Instructions[0].MatchStLoc(out var switchValueVarCopy, out var arg) && IsIsInternedCall(arg as Call, out arg) && arg.MatchLdLoc(switchValueVar))) return false; switchValueVar = switchValueVarCopy; int conditionOffset = 1; Block currentCaseBlock = isInternedBlock; var values = new List<(string, ILInstruction)>(); if (!switchValueVarCopy.IsSingleDefinition) return false; // each case starts with: // if (comp(ldloc switchValueVar == ldstr "case label")) br caseBlock // br currentCaseBlock while (currentCaseBlock.Instructions[conditionOffset].MatchIfInstruction(out condition, out var caseBlockJump)) { if (currentCaseBlock.Instructions.Count != conditionOffset + 2) break; if (!condition.MatchCompEquals(out var left, out var right)) break; if (!left.MatchLdLoc(switchValueVar)) break; if (!right.MatchLdStr(out string value)) break; if (!(caseBlockJump.MatchBranch(out var caseBlock) || caseBlockJump.MatchLeave((BlockContainer)currentCaseBlock.Parent))) break; if (!currentCaseBlock.Instructions[conditionOffset + 1].MatchBranch(out currentCaseBlock)) break; conditionOffset = 0; values.Add((value, caseBlockJump.Clone())); } if (values.Count != switchValueVarCopy.LoadCount) return false; context.Step(nameof(SimplifyCSharp1CascadingIfStatements), instructions[i]); // switch contains case null: if (currentCaseBlock != defaultOrNullBlock) { values.Add((null, new Branch(defaultOrNullBlock))); } var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = b.Item2 })); sections.Add(new SwitchSection { Labels = new LongSet(new LongInterval(0, sections.Count)).Invert(), Body = new Branch(currentCaseBlock) }); var stringToInt = new StringToInt(switchValue, values.SelectArray(item => item.Item1), context.TypeSystem.FindType(KnownTypeCode.String)); var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); inst.AddILRange(instructions[i - 1]); instructions[i].ReplaceWith(inst); instructions.RemoveAt(i + 1); instructions.RemoveAt(i - 1); return true; } bool IsIsInternedCall(Call call, out ILInstruction argument) { if (call != null && call.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && call.Method.IsStatic && call.Method.Name == "IsInterned" && call.Arguments.Count == 1) { argument = call.Arguments[0]; return true; } argument = null; return false; } /// /// Each case consists of two blocks: /// 1. block: /// if (call op_Equality(ldloc switchVariable, ldstr value)) br caseBlock /// br nextBlock /// -or- /// if (comp(ldloc switchValueVar == ldnull)) br nextBlock /// br caseBlock /// 2. block is caseBlock /// This method matches the above pattern or its inverted form: /// the call to ==(string, string) is wrapped in logic.not and the branch targets are reversed. /// Returns the next block that follows in the block-chain. /// The is updated if the value gets copied to a different variable. /// See comments below for more info. /// ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out bool emptyStringEqualsNull, out ILInstruction caseBlockOrLeave) { value = null; caseBlockOrLeave = null; emptyStringEqualsNull = false; if (currentBlock.IncomingEdgeCount != 1 || currentBlock.Instructions.Count != 2) return null; if (!currentBlock.MatchIfAtEndOfBlock(out var condition, out var caseBlockBranch, out var nextBlockBranch)) return null; if (!MatchStringEqualityComparison(condition, switchVariable, out value, out bool isVBCompareString)) { return null; } if (isVBCompareString) { ExtensionMethods.Swap(ref caseBlockBranch, ref nextBlockBranch); emptyStringEqualsNull = true; } if (caseBlockBranch.MatchBranch(out var caseBlock)) { caseBlockOrLeave = caseBlock; } else if (caseBlockBranch.MatchLeave(out _)) { caseBlockOrLeave = caseBlockBranch; } else { return null; } if (nextBlockBranch.MatchBranch(out Block nextBlock)) { // success return nextBlock; } else if (nextBlockBranch.MatchLeave(out BlockContainer blockContainer)) { // success return blockContainer; } else { return null; } } /// /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. /// bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, ref int i) { // match first block: checking switch-value for null: // (In some cases, i.e., if switchValueVar is a parameter, the initial store is optional.) // stloc switchValueVar(switchValue) // if (comp(ldloc switchValueVar == ldnull)) br nullCase // br nextBlock if (!instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump)) return false; if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull())) return false; // Extract switchValueVar if (!left.MatchLdLoc(out var switchValueVar) || !switchValueVar.IsSingleDefinition) return false; // If the switchValueVar is a stack slot and there is an assignment involving it right before the // switch-value null-check, we use the previously assigned variable as switchValueVar. ILInstruction switchValue; if (switchValueVar.Kind == VariableKind.StackSlot && instructions.ElementAtOrDefault(i - 1) is StLoc extraStore && extraStore.Value.MatchLdLoc(switchValueVar)) { switchValueVar = extraStore.Variable; switchValue = extraStore.Value; } else { switchValue = null; } if (!switchValueVar.Type.IsKnownType(KnownTypeCode.String)) return false; // either br nullCase or leave container BlockContainer leaveContainer = null; if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock) && !exitBlockJump.MatchLeave(out leaveContainer)) return false; var nextBlockJump = instructions.ElementAtOrDefault(i + 1) as Branch; if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) return false; // match second block: checking compiler-generated Dictionary for null // if (comp(volatile.ldobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600000c-1) != ldnull)) br caseNullBlock // br dictInitBlock var nextBlock = nextBlockJump.TargetBlock; if (nextBlock.Instructions.Count != 2 || !nextBlock.Instructions[0].MatchIfInstruction(out condition, out var tryGetValueBlockJump)) return false; if (!tryGetValueBlockJump.MatchBranch(out var tryGetValueBlock)) return false; if (!nextBlock.Instructions[1].MatchBranch(out var dictInitBlock) || dictInitBlock.IncomingEdgeCount != 1) return false; if (!(condition.MatchCompNotEquals(out left, out right) && right.MatchLdNull() && MatchDictionaryFieldLoad(left, IsStringToIntDictionary, out var dictField, out var dictionaryType))) return false; // match third block: initialization of compiler-generated Dictionary // stloc dict(newobj Dictionary..ctor(ldc.i4 valuesLength)) // call Add(ldloc dict, ldstr value, ldc.i4 index) // ... more calls to Add ... // volatile.stobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600003f-1, ldloc dict) // br switchHeadBlock if (dictInitBlock.IncomingEdgeCount != 1 || dictInitBlock.Instructions.Count < 3) return false; if (!ExtractStringValuesFromInitBlock(dictInitBlock, out var stringValues, out var blockAfterInit, dictionaryType, dictField, false)) return false; if (tryGetValueBlock != blockAfterInit) return false; // match fourth block: TryGetValue on compiler-generated Dictionary // if (logic.not(call TryGetValue(volatile.ldobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600000c-1), ldloc switchValueVar, ldloca switchIndexVar))) br defaultBlock // br switchBlock if (tryGetValueBlock.IncomingEdgeCount != 2 || tryGetValueBlock.Instructions.Count != 2) return false; if (!tryGetValueBlock.Instructions[0].MatchIfInstruction(out condition, out var defaultBlockJump)) return false; if (!defaultBlockJump.MatchBranch(out var defaultBlock) && !((leaveContainer != null && defaultBlockJump.MatchLeave(leaveContainer)) || defaultBlockJump.MatchLeave(out _))) return false; if (!(condition.MatchLogicNot(out var arg) && arg is CallInstruction c && c.Method.Name == "TryGetValue" && MatchDictionaryFieldLoad(c.Arguments[0], IsStringToIntDictionary, out var dictField2, out _) && dictField2.Equals(dictField))) return false; if (!c.Arguments[1].MatchLdLoc(switchValueVar) || !c.Arguments[2].MatchLdLoca(out var switchIndexVar)) return false; if (!tryGetValueBlock.Instructions[1].MatchBranch(out var switchBlock)) return false; // match fifth block: switch-instruction block // switch (ldloc switchVariable) { // case [0..1): br caseBlock1 // ... more cases ... // case [long.MinValue..0),[13..long.MaxValue]: br defaultBlock // } // mcs has a bug: when there is only one case it still generates the full-blown Dictionary pattern, // but uses only a simple if statement instead of the switch instruction. if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count == 0) return false; var sections = new List(); switch (switchBlock.Instructions[0]) { case SwitchInstruction switchInst: if (switchBlock.Instructions.Count != 1) return false; if (!switchInst.Value.MatchLdLoc(switchIndexVar)) return false; sections.AddRange(switchInst.Sections); break; case IfInstruction ifInst: if (switchBlock.Instructions.Count != 2) return false; if (!ifInst.Condition.MatchCompEquals(out left, out right)) return false; if (!left.MatchLdLoc(switchIndexVar)) return false; if (!right.MatchLdcI4(0)) return false; sections.Add(new SwitchSection() { Body = ifInst.TrueInst, Labels = new LongSet(0) }.WithILRange(ifInst)); sections.Add(new SwitchSection() { Body = switchBlock.Instructions[1], Labels = new LongSet(0).Invert() }.WithILRange(switchBlock.Instructions[1])); break; } // mcs: map sections without a value to the default section, if possible if (!FixCasesWithoutValue(sections, stringValues)) return false; // switch contains case null: if (nullValueCaseBlock != null && nullValueCaseBlock != defaultBlock) { if (!AddNullSection(sections, stringValues, nullValueCaseBlock)) { return false; } } else if (leaveContainer != null && !defaultBlockJump.MatchLeave(leaveContainer)) { if (!AddNullSection(sections, stringValues, (Leave)exitBlockJump)) { return false; } } context.Step(nameof(MatchLegacySwitchOnStringWithDict), instructions[i]); bool keepAssignmentBefore = false; if (switchValueVar.LoadCount > 2 || switchValue == null) { switchValue = new LdLoc(switchValueVar); keepAssignmentBefore = true; } var stringToInt = new StringToInt(switchValue, stringValues, switchValueVar.Type); var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); instructions[i + 1].ReplaceWith(inst); if (keepAssignmentBefore) { // delete if (comp(ldloc switchValueVar == ldnull)) inst.AddILRange(instructions[i]); instructions.RemoveAt(i); i--; } else { // delete both the if and the assignment before inst.AddILRange(instructions[i - 1]); instructions.RemoveRange(i - 1, 2); i -= 2; } return true; } bool FixCasesWithoutValue(List sections, List<(string, int)> stringValues) { bool HasLabel(SwitchSection section) { return section.Labels.Values.Any(i => stringValues.Any(value => i == value.Item2)); } // Pick the section with the most labels as default section. // And collect all sections that have no value mapped to them. SwitchSection defaultSection = sections.First(); List sectionsWithoutLabels = new List(); foreach (var section in sections) { if (section == defaultSection) continue; if (section.Labels.Count() > defaultSection.Labels.Count()) { if (!HasLabel(defaultSection)) sectionsWithoutLabels.Add(defaultSection); defaultSection = section; continue; } if (!HasLabel(section)) sectionsWithoutLabels.Add(section); } foreach (var section in sectionsWithoutLabels) { defaultSection.Labels = defaultSection.Labels.UnionWith(section.Labels); if (section.HasNullLabel) defaultSection.HasNullLabel = true; sections.Remove(section); } return true; } bool AddNullSection(List sections, List<(string Value, int Index)> stringValues, Block nullValueCaseBlock) { return AddNullSection(sections, stringValues, new Branch(nullValueCaseBlock)); } bool AddNullSection(List sections, List<(string Value, int Index)> stringValues, ILInstruction body) { var label = new LongSet(stringValues.Max(item => item.Index) + 1); var possibleConflicts = sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); if (possibleConflicts.Length > 1) return false; else if (possibleConflicts.Length == 1) { if (possibleConflicts[0].Labels.Count() == 1) return false; // cannot remove only label possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); } stringValues.Add((null, (int)label.Values.First())); sections.Add(new SwitchSection() { Labels = label, Body = body }); return true; } /// /// Matches 'volatile.ldobj dictionaryType(ldsflda dictField)' /// bool MatchDictionaryFieldLoad(ILInstruction inst, Func typeMatcher, out IField dictField, out IType dictionaryType) { dictField = null; dictionaryType = null; return inst.MatchLdObj(out var dictionaryFieldLoad, out dictionaryType) && typeMatcher(dictionaryType) && dictionaryFieldLoad.MatchLdsFlda(out dictField) && (dictField.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || dictField.Name.StartsWith("$$method", StringComparison.Ordinal)); } /// /// Matches and extracts values from Add-call sequences. /// bool ExtractStringValuesFromInitBlock(Block block, out List<(string, int)> values, out Block blockAfterInit, IType dictionaryType, IField dictionaryField, bool isHashtablePattern) { values = null; blockAfterInit = null; // stloc dictVar(newobj Dictionary..ctor(ldc.i4 valuesLength)) // -or- // stloc dictVar(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f4 loadFactor)) if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && newObjDict is NewObj newObj)) return false; if (!newObj.Method.DeclaringType.Equals(dictionaryType)) return false; int valuesLength = 0; if (newObj.Arguments.Count == 2) { if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) return false; if (!newObj.Arguments[1].MatchLdcF4(0.5f)) return false; } else if (newObj.Arguments.Count == 1) { if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) return false; } values = new List<(string, int)>(valuesLength); int i = 0; while (MatchAddCall(dictionaryType, block.Instructions[i + 1], dictVar, out var index, out var value)) { values.Add((value, index)); i++; } // final store to compiler-generated variable: // volatile.stobj dictionaryType(ldsflda dictionaryField, ldloc dictVar) if (!(block.Instructions[i + 1].MatchStObj(out var loadField, out var dictVarLoad, out var dictType) && dictType.Equals(dictionaryType) && loadField.MatchLdsFlda(out var dictField) && dictField.Equals(dictionaryField) && dictVarLoad.MatchLdLoc(dictVar))) return false; if (isHashtablePattern && block.Instructions[i + 2] is IfInstruction) { return block.Instructions[i + 3].MatchBranch(out blockAfterInit); } return block.Instructions[i + 2].MatchBranch(out blockAfterInit); } /// /// call Add(ldloc dictVar, ldstr value, ldc.i4 index) /// -or- /// call Add(ldloc dictVar, ldstr value, box System.Int32(ldc.i4 index)) /// bool MatchAddCall(IType dictionaryType, ILInstruction inst, ILVariable dictVar, out int index, out string value) { value = null; index = -1; if (!(inst is CallInstruction c && c.Method.Name == "Add" && c.Arguments.Count == 3)) return false; if (!c.Arguments[0].MatchLdLoc(dictVar)) return false; if (!c.Arguments[1].MatchLdStr(out value)) { if (c.Arguments[1].MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.String) && field.Name == "Empty") { value = ""; } else { return false; } } if (!(c.Method.DeclaringType.Equals(dictionaryType) && !c.Method.IsStatic)) return false; return (c.Arguments[2].MatchLdcI4(out index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(out index))); } bool IsStringToIntDictionary(IType dictionaryType) { if (dictionaryType.FullName != "System.Collections.Generic.Dictionary") return false; if (dictionaryType.TypeArguments.Count != 2) return false; return dictionaryType.TypeArguments[0].IsKnownType(KnownTypeCode.String) && dictionaryType.TypeArguments[1].IsKnownType(KnownTypeCode.Int32); } bool IsNonGenericHashtable(IType dictionaryType) { if (dictionaryType.FullName != "System.Collections.Hashtable") return false; if (dictionaryType.TypeArguments.Count != 0) return false; return true; } bool MatchLegacySwitchOnStringWithHashtable(Block block, HashtableInitializer hashtableInitializers, ref int i) { // match first block: checking switch-value for null // stloc tmp(ldloc switch-value) // stloc switchVariable(ldloc tmp) // if (comp(ldloc tmp == ldnull)) br nullCaseBlock // br getItemBlock if (block.Instructions.Count != i + 4) return false; if (!block.Instructions[i].MatchStLoc(out var tmp, out var switchValue)) return false; if (!block.Instructions[i + 1].MatchStLoc(out var switchVariable, out var tmpLoad) || !tmpLoad.MatchLdLoc(tmp)) return false; if (!block.Instructions[i + 2].MatchIfInstruction(out var condition, out var nullCaseBlockBranch)) return false; if (!block.Instructions[i + 3].MatchBranch(out var getItemBlock) || !(nullCaseBlockBranch.MatchBranch(out var nullCaseBlock) || nullCaseBlockBranch is Leave)) return false; if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(tmp))) return false; // match second block: get_Item on compiler-generated Hashtable // stloc tmp2(call get_Item(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f - 1), ldloc switchVariable)) // stloc switchVariable(ldloc tmp2) // if (comp(ldloc tmp2 == ldnull)) br defaultCaseBlock // br switchBlock if (getItemBlock.IncomingEdgeCount != 1 || getItemBlock.Instructions.Count != 4) return false; if (!(getItemBlock.Instructions[0].MatchStLoc(out var tmp2, out var getItem) && getItem is Call getItemCall && getItemCall.Method.Name == "get_Item")) return false; if (!getItemBlock.Instructions[1].MatchStLoc(out var switchVariable2, out var tmp2Load) || !tmp2Load.MatchLdLoc(tmp2)) return false; if (!ILVariableEqualityComparer.Instance.Equals(switchVariable, switchVariable2)) return false; if (!getItemBlock.Instructions[2].MatchIfInstruction(out condition, out var defaultBlockBranch)) return false; if (!getItemBlock.Instructions[3].MatchBranch(out var switchBlock) || !(defaultBlockBranch.MatchBranch(out var defaultBlock) || defaultBlockBranch is Leave)) return false; if (!(condition.MatchCompEquals(out left, out right) && right.MatchLdNull() && left.MatchLdLoc(tmp2))) return false; if (!(getItemCall.Arguments.Count == 2 && MatchDictionaryFieldLoad(getItemCall.Arguments[0], IsNonGenericHashtable, out var dictField, out _) && getItemCall.Arguments[1].MatchLdLoc(switchVariable))) return false; // Check if there is a hashtable init block at the beginning of the method if (!hashtableInitializers.TryGetValue(dictField, out var info)) return false; var stringValues = info.Labels; // match third block: switch-instruction block // switch (ldobj System.Int32(unbox System.Int32(ldloc switchVariable))) { // case [0..1): br caseBlock1 // ... more cases ... // case [long.MinValue..0),[13..long.MaxValue]: br defaultBlock // } if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 1) return false; if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst && switchInst.Value.MatchLdObj(out var target, out var ldobjType) && target.MatchUnbox(out var arg, out var unboxType) && arg.MatchLdLoc(switchVariable2) && ldobjType.IsKnownType(KnownTypeCode.Int32) && unboxType.Equals(ldobjType))) return false; var sections = new List(switchInst.Sections); // switch contains case null: if (!(nullCaseBlockBranch is Leave) && nullCaseBlock != defaultBlock) { if (!AddNullSection(sections, stringValues, nullCaseBlock)) { return false; } } context.Step(nameof(MatchLegacySwitchOnStringWithHashtable), block.Instructions[i]); var stringToInt = new StringToInt(switchValue, stringValues, context.TypeSystem.FindType(KnownTypeCode.String)); var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); inst.AddILRange(block.Instructions[i]); block.Instructions[i].ReplaceWith(inst); block.Instructions.RemoveRange(i + 1, 3); info.Transformed = true; hashtableInitializers[dictField] = info; return true; } bool FindHashtableInitBlock(Block entryPoint, out List<(string, int)> stringValues, out IField dictField, out Block blockAfterThisInitBlock, out ILInstruction thisSwitchInitJumpInst, out ILInstruction nextSwitchInitJumpInst) { stringValues = null; dictField = null; blockAfterThisInitBlock = null; nextSwitchInitJumpInst = null; thisSwitchInitJumpInst = null; if (entryPoint.Instructions.Count != 2) return false; // match first block: checking compiler-generated Hashtable for null // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-1) != ldnull)) br switchHeadBlock // br tableInitBlock if (!(entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branchToSwitchHead))) return false; if (!entryPoint.Instructions[1].MatchBranch(out var tableInitBlock)) return false; if (!(condition.MatchCompNotEquals(out var left, out var right) && right.MatchLdNull() && MatchDictionaryFieldLoad(left, IsNonGenericHashtable, out dictField, out var dictionaryType))) return false; if (!branchToSwitchHead.MatchBranch(out var switchHead)) return false; thisSwitchInitJumpInst = entryPoint.Instructions[0]; // match second block: initialization of compiler-generated Hashtable // stloc table(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f loadFactor)) // call Add(ldloc table, ldstr value, box System.Int32(ldc.i4 index)) // ... more calls to Add ... // volatile.stobj System.Collections.Hashtable(ldsflda $$method0x600003f - 1, ldloc table) // br switchHeadBlock if (tableInitBlock.IncomingEdgeCount != 1 || tableInitBlock.Instructions.Count < 3) return false; if (!ExtractStringValuesFromInitBlock(tableInitBlock, out stringValues, out blockAfterThisInitBlock, dictionaryType, dictField, true)) return false; // if there is another IfInstruction before the end of the block, it might be a jump to the next hashtable init block. // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-2) != ldnull)) br switchHeadBlock if (tableInitBlock.Instructions.SecondToLastOrDefault() is IfInstruction nextHashtableInitHead) { if (!(nextHashtableInitHead.Condition.MatchCompNotEquals(out left, out right) && right.MatchLdNull() && MatchDictionaryFieldLoad(left, IsNonGenericHashtable, out var nextSwitchInitField, out _))) return false; if (!nextHashtableInitHead.TrueInst.MatchBranch(switchHead)) return false; nextSwitchInitJumpInst = nextHashtableInitHead; } return true; } bool MatchRoslynSwitchOnString(InstructionCollection instructions, ref int i) { if (i >= instructions.Count - 1) return false; // stloc switchValueVar(switchValue) // if (comp(ldloc switchValueVar == ldnull)) br nullCase // br nextBlock InstructionCollection switchBlockInstructions = instructions; int switchBlockInstructionsOffset = i; Block nullValueCaseBlock = null; ILInstruction instForNullCheck = null; if (instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump) && condition.MatchCompEqualsNull(out instForNullCheck)) { var nextBlockJump = instructions[i + 1] as Branch; if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) return false; if (!exitBlockJump.MatchBranch(out nullValueCaseBlock)) return false; switchBlockInstructions = nextBlockJump.TargetBlock.Instructions; switchBlockInstructionsOffset = 0; } // stloc switchValueVar(call ComputeStringHash(switchValueLoad)) // switch (ldloc switchValueVar) { // case [211455823..211455824): br caseBlock1 // ... more cases ... // case [long.MinValue..-365098645),...,[1697255802..long.MaxValue]: br defaultBlock // } if (!(switchBlockInstructionsOffset + 1 < switchBlockInstructions.Count && switchBlockInstructions[switchBlockInstructionsOffset + 1] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var switchValueVar) && MatchComputeStringOrReadOnlySpanHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad))) { return false; } if (instForNullCheck != null && !instForNullCheck.MatchLdLoc(switchValueLoad.Variable)) { return false; } var stringValues = new List<(string Value, ILInstruction TargetBlockOrLeave)>(); SwitchSection defaultSection = switchInst.GetDefaultSection(); if (!(defaultSection.Body.MatchBranch(out Block exitOrDefaultBlock) || defaultSection.Body.MatchLeave(out _))) return false; foreach (var section in switchInst.Sections) { if (section == defaultSection) continue; // extract target block if (!section.Body.MatchBranch(out Block target)) return false; string stringValue; bool emptyStringEqualsNull; if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) { stringValue = ""; emptyStringEqualsNull = false; } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue, out emptyStringEqualsNull)) { return false; } if (currentExitBlock != exitOrDefaultBlock) return false; if (emptyStringEqualsNull && string.IsNullOrEmpty(stringValue)) { stringValues.Add((null, targetOrLeave)); stringValues.Add((string.Empty, targetOrLeave)); } else { stringValues.Add((stringValue, targetOrLeave)); } } if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) { stringValues.Add((null, nullValueCaseBlock)); } // In newer Roslyn versions (>=3.7) the null check appears in the default case, not prior to the switch. ILInstruction exitOrDefault = exitOrDefaultBlock; if (!stringValues.Any(pair => pair.Value == null) && IsNullCheckInDefaultBlock(ref exitOrDefault, switchValueLoad.Variable, out nullValueCaseBlock)) { stringValues.Add((null, nullValueCaseBlock)); } exitOrDefaultBlock = (Block)exitOrDefault; context.Step(nameof(MatchRoslynSwitchOnString), switchValueLoad); if (exitOrDefaultBlock != null) { // change TargetBlock in case it was modified by IsNullCheckInDefaultBlock() ((Branch)defaultSection.Body).TargetBlock = exitOrDefaultBlock; } ILInstruction switchValueInst = switchValueLoad; if (instructions == switchBlockInstructions) { // stloc switchValueLoadVariable(switchValue) // stloc switchValueVar(call ComputeStringHash(ldloc switchValueLoadVariable)) // switch (ldloc switchValueVar) { bool keepAssignmentBefore; // if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it. if (i >= 1 && instructions[i - 1].MatchStLoc(switchValueLoad.Variable, out var switchValueTmp) && switchValueLoad.Variable.IsSingleDefinition && switchValueLoad.Variable.LoadCount == switchInst.Sections.Count) { switchValueInst = switchValueTmp; keepAssignmentBefore = false; } else { keepAssignmentBefore = true; } // replace stloc switchValueVar(call ComputeStringHash(...)) with new switch instruction var newSwitch = ReplaceWithSwitchInstruction(i); // remove old switch instruction newSwitch.AddILRange(instructions[i + 1]); instructions.RemoveAt(i + 1); // remove extra assignment if (!keepAssignmentBefore) { newSwitch.AddILRange(instructions[i - 1]); instructions.RemoveRange(i - 1, 1); i -= 1; } } else { bool keepAssignmentBefore; // if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it. if (i >= 2 && instructions[i - 2].MatchStLoc(out var temporary, out var temporaryValue) && instructions[i - 1].MatchStLoc(switchValueLoad.Variable, out var tempLoad) && tempLoad.MatchLdLoc(temporary)) { switchValueInst = temporaryValue; keepAssignmentBefore = false; } else { keepAssignmentBefore = true; } // replace null check with new switch instruction var newSwitch = ReplaceWithSwitchInstruction(i); newSwitch.AddILRange(switchInst); // remove jump instruction to switch block newSwitch.AddILRange(instructions[i + 1]); instructions.RemoveAt(i + 1); // remove extra assignment if (!keepAssignmentBefore) { newSwitch.AddILRange(instructions[i - 2]); instructions.RemoveRange(i - 2, 2); i -= 2; } } return true; SwitchInstruction ReplaceWithSwitchInstruction(int offset) { var defaultLabel = new LongSet(new LongInterval(0, stringValues.Count)).Invert(); var values = new string[stringValues.Count]; var sections = new SwitchSection[stringValues.Count]; foreach (var (idx, (value, bodyInstruction)) in stringValues.WithIndex()) { values[idx] = value; var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction; sections[idx] = new SwitchSection { Labels = new LongSet(idx), Body = body }; } var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, values, switchValueLoad.Variable.Type)); newSwitch.Sections.AddRange(sections); newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); instructions[offset].ReplaceWith(newSwitch); return newSwitch; } } private bool MatchRoslynSwitchOnStringUsingLengthAndChar(Block block, int i) { var instructions = block.Instructions; // implements https://github.com/dotnet/roslyn/pull/66081 // if (comp(ldloc switchValueVar == ldnull)) br nullCase // br nextBlock Block switchOnLengthBlock; int switchOnLengthBlockStartOffset; Block nullCase = null; if (instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump) && condition.MatchCompEqualsNull(out var ldloc) && ldloc is LdLoc { Variable: var switchValueVar }) { if (!instructions[i + 1].MatchBranch(out var nextBlock)) return false; if (!exitBlockJump.MatchBranch(out nullCase) && !exitBlockJump.MatchLeave(out _)) return false; // if (comp(ldloc switchValueVar == ldnull)) br ... // br switchOnLengthBlock if (nextBlock.IncomingEdgeCount == 1 && nextBlock.Instructions[0].MatchIfInstruction(out condition, out _) && condition.MatchCompEqualsNull(out ldloc) && ldloc.MatchLdLoc(switchValueVar)) { if (!nextBlock.Instructions[1].MatchBranch(out switchOnLengthBlock)) return false; } else { switchOnLengthBlock = nextBlock; } if (switchOnLengthBlock.IncomingEdgeCount != 1) return false; switchOnLengthBlockStartOffset = 0; } else { switchOnLengthBlock = block; switchValueVar = null; // will be extracted in MatchSwitchOnLengthBlock switchOnLengthBlockStartOffset = i; } ILInstruction defaultCase = null; if (!MatchSwitchOnLengthBlock(ref switchValueVar, switchOnLengthBlock, switchOnLengthBlockStartOffset, out var blocksByLength)) return false; List<(string, ILInstruction)> stringValues = new(); foreach (var b in blocksByLength) { if (b.Length.Count() != 1) { if (b.TargetBlock != nullCase) return false; } else { int length = (int)b.Length.Intervals[0].Start; switch (b.TargetBlock) { case Leave leave: break; case Block targetBlock: if (MatchSwitchOnCharBlock(targetBlock, length, switchValueVar, out var mapping)) { foreach (var item in mapping) { if (!stringValues.Any(x => x.Item1 == item.StringValue)) { stringValues.Add(item); } else { return false; } } } else if (MatchRoslynCaseBlockHead(targetBlock, switchValueVar, out var bodyOrLeave, out var exit, out string stringValue, out _)) { if (exit != defaultCase) return false; if (!stringValues.Any(x => x.Item1 == stringValue)) { stringValues.Add((stringValue, bodyOrLeave)); } else { return false; } } else if (length == 0) { stringValues.Add(("", b.TargetBlock)); } else { return false; } break; default: return false; } } } if (!stringValues.Any(pair => pair.Item1 == null)) { if (IsNullCheckInDefaultBlock(ref defaultCase, switchValueVar, out var nullBlock)) { stringValues.Add((null, nullBlock)); } else if (nullCase != null && nullCase != defaultCase) { stringValues.Add((null, nullCase)); } } context.Step(nameof(MatchRoslynSwitchOnStringUsingLengthAndChar), instructions[i]); var defaultLabel = new LongSet(new LongInterval(0, stringValues.Count)).Invert(); var values = new string[stringValues.Count]; var sections = new SwitchSection[stringValues.Count]; foreach (var (idx, (value, bodyInstruction)) in stringValues.WithIndex()) { values[idx] = value; var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction; sections[idx] = new SwitchSection { Labels = new LongSet(idx), Body = body }; } var newSwitch = new SwitchInstruction(new StringToInt(new LdLoc(switchValueVar), values, switchValueVar.Type)); newSwitch.Sections.AddRange(sections); newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultCase is Block b2 ? new Branch(b2) : defaultCase }); newSwitch.AddILRange(instructions[i]); if (nullCase != null) { newSwitch.AddILRange(instructions[i + 1]); } instructions[i] = newSwitch; instructions.RemoveRange(i + 1, instructions.Count - (i + 1)); return true; bool MatchGetChars(ILInstruction instruction, ILVariable switchValueVar, out int index) { index = -1; if (context.Settings.SwitchOnReadOnlySpanChar && instruction.MatchLdObj(out var target, out var type) && type.IsKnownType(KnownTypeCode.UInt16)) { return target is Call call && (call.Method.FullNameIs("System.ReadOnlySpan", "get_Item") || call.Method.FullNameIs("System.Span", "get_Item")) && call.Arguments.Count == 2 && call.Arguments[0].MatchLdLoca(switchValueVar) && call.Arguments[1].MatchLdcI4(out index); } else { return instruction is Call call && call.Method.FullNameIs("System.String", "get_Chars") && call.Arguments.Count == 2 && call.Arguments[0].MatchLdLoc(switchValueVar) && call.Arguments[1].MatchLdcI4(out index); } } bool MatchSwitchOnCharBlock(Block block, int length, ILVariable switchValueVar, out List<(string StringValue, ILInstruction BodyOrLeave)> results) { results = null; if (block.IncomingEdgeCount != 1) return false; SwitchInstruction @switch; List> sections; int index; switch (block.Instructions.Count) { case 1: @switch = block.Instructions[0] as SwitchInstruction; if (@switch == null) return false; if (!MatchGetChars(@switch.Value, switchValueVar, out index)) return false; sections = @switch.Sections.SelectList(s => new KeyValuePair(s.Labels, s.Body)); break; case 2: if (!block.Instructions[0].MatchStLoc(out var charTempVar, out var getCharsCall)) return false; if (!MatchGetChars(getCharsCall, switchValueVar, out index)) return false; if (index < 0) return false; @switch = block.Instructions[1] as SwitchInstruction; if (@switch == null) return false; if (!@switch.Value.MatchLdLoc(charTempVar)) return false; sections = @switch.Sections.SelectList(s => new KeyValuePair(s.Labels, s.Body)); break; default: if (!analysis.AnalyzeBlock(block)) { return false; } if (!block.Instructions[0].MatchStLoc(out charTempVar, out getCharsCall)) return false; if (!MatchGetChars(getCharsCall, switchValueVar, out index)) return false; if (index < 0) return false; if (analysis.SwitchVariable != charTempVar) return false; sections = analysis.Sections; break; } if (index >= length) return false; bool hasDefaultSection = false; foreach (var (labels, body) in sections) { if (labels.Count() == 1) { char ch = unchecked((char)labels.Values.Single()); if (!body.MatchBranch(out var targetBlock)) return false; if (length == 1) { results ??= new(); results.Add((ch.ToString(), targetBlock)); } else { while (MatchRoslynCaseBlockHead(targetBlock, switchValueVar, out var bodyOrLeave, out var exit, out var stringValue, out _)) { if (stringValue.Length != length || stringValue[index] != ch) return false; results ??= new(); results.Add((stringValue, bodyOrLeave)); if (exit == nullCase) break; targetBlock = exit; } } } else if (!hasDefaultSection) { hasDefaultSection = true; } else { return false; } } return results?.Count > 0; } bool MatchSwitchOnLengthBlock(ref ILVariable switchValueVar, Block switchOnLengthBlock, int startOffset, out List<(LongSet Length, ILInstruction TargetBlock)> blocks) { blocks = null; SwitchInstruction @switch; ILInstruction getLengthCall; ILVariable lengthVar; switch (switchOnLengthBlock.Instructions.Count - startOffset) { case 1: @switch = switchOnLengthBlock.Instructions[startOffset] as SwitchInstruction; if (@switch == null) return false; getLengthCall = @switch.Value; break; case 2: if (!switchOnLengthBlock.Instructions[startOffset].MatchStLoc(out lengthVar, out getLengthCall)) return false; @switch = switchOnLengthBlock.Instructions[startOffset + 1] as SwitchInstruction; if (@switch == null) return false; if (!@switch.Value.MatchLdLoc(lengthVar)) return false; break; case 3: @switch = null; if (!switchOnLengthBlock.Instructions[startOffset].MatchStLoc(out lengthVar, out getLengthCall)) return false; if (!switchOnLengthBlock.Instructions[startOffset + 1].MatchIfInstruction(out var cond, out var gotoLength)) return false; if (!gotoLength.MatchBranch(out var target)) return false; if (!switchOnLengthBlock.Instructions[startOffset + 2].MatchBranch(out var gotoElse)) return false; if (!cond.MatchCompEquals(out var lhs, out var rhs)) { if (!cond.MatchCompNotEquals(out lhs, out rhs)) return false; var t = target; target = gotoElse; gotoElse = t; } defaultCase = gotoElse; if (!lhs.MatchLdLoc(lengthVar) || !rhs.MatchLdcI4(out int length)) return false; blocks = new() { (new LongSet(length), target), (new LongSet(length).Invert(), defaultCase) }; break; default: return false; } if (getLengthCall is not Call call || call.Arguments.Count != 1 || call.Method.Name != "get_Length") { return false; } var declaringTypeCode = call.Method.DeclaringTypeDefinition?.KnownTypeCode; switch (declaringTypeCode) { case KnownTypeCode.String: if (!call.Arguments[0].MatchLdLoc(switchValueVar)) return false; break; case KnownTypeCode.ReadOnlySpanOfT: case KnownTypeCode.SpanOfT: if (!context.Settings.SwitchOnReadOnlySpanChar) return false; if (!call.Arguments[0].MatchLdLoca(out switchValueVar)) return false; break; default: return false; } if (@switch == null) return true; blocks = new(@switch.Sections.Count); foreach (var section in @switch.Sections) { if (section.HasNullLabel) return false; if (!section.Body.MatchBranch(out var target) && !section.Body.MatchLeave(out _)) return false; ILInstruction targetInst = target ?? section.Body; if (section.Labels.Count() != 1) { defaultCase ??= targetInst; if (defaultCase != targetInst) return false; } else { blocks.Add((section.Labels, targetInst)); } } return true; } } /// /// Matches: /// Block oldDefaultBlock (incoming: 1) { /// if (comp.o(ldloc switchVar == ldnull)) br nullValueCaseBlock /// br newDefaultBlock /// } /// private bool IsNullCheckInDefaultBlock(ref ILInstruction exitOrDefault, ILVariable switchVar, out Block nullValueCaseBlock) { nullValueCaseBlock = null; if (exitOrDefault is not Block exitOrDefaultBlock) return false; if (!exitOrDefaultBlock.Instructions[0].MatchIfInstruction(out var condition, out var thenBranch)) return false; if (!(condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(switchVar))) return false; if (!thenBranch.MatchBranch(out nullValueCaseBlock)) return false; if (nullValueCaseBlock.Parent != exitOrDefaultBlock.Parent) return false; if (!exitOrDefaultBlock.Instructions[1].MatchBranch(out var elseBlock)) return false; if (elseBlock.Parent != exitOrDefaultBlock.Parent) return false; exitOrDefault = elseBlock; return true; } /// /// Matches (and the negated version): /// if (call op_Equality(ldloc switchValueVar, stringValue)) br body /// br exit /// bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue, out bool emptyStringEqualsNull) { bodyOrLeave = null; defaultOrExitBlock = null; stringValue = null; emptyStringEqualsNull = false; if (target.Instructions.Count != 2) return false; if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) { // Special case: sometimes we don't have an if, because bodyBranch==exitBranch // and the C# compiler optimized out the if. // Example: // Block IL_0063 (incoming: 1) { // call op_Equality(ldloc V_4, ldstr "rowno") // leave IL_0000(nop) // } condition = target.Instructions[0]; bodyBranch = target.Instructions[1]; } ILInstruction exitBranch = target.Instructions[1]; // Handle negated conditions first: while (condition.MatchLogicNot(out var expr)) { ExtensionMethods.Swap(ref exitBranch, ref bodyBranch); condition = expr; } if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue, out bool isVBCompareString)) return false; if (isVBCompareString) { ExtensionMethods.Swap(ref exitBranch, ref bodyBranch); emptyStringEqualsNull = true; } if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _))) return false; if (bodyBranch.MatchLeave(out _)) { bodyOrLeave = bodyBranch; return true; } if (bodyBranch.MatchBranch(out var bodyBlock)) { bodyOrLeave = bodyBlock; return true; } return false; } /// /// Block target(incoming: 1) { /// if (comp.o(ldloc switchValueVar == ldnull)) br exit /// br lengthCheckBlock /// } /// /// Block lengthCheckBlock(incoming: 1) { /// if (logic.not(call get_Length(ldloc switchValueVar))) br body /// br exit /// } /// bool MatchRoslynEmptyStringCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock) { bodyOrLeave = null; defaultOrExitBlock = null; if (target.Instructions.Count != 2 || target.IncomingEdgeCount != 1) return false; if (!target.Instructions[0].MatchIfInstruction(out var nullComparisonCondition, out var exitBranch)) return false; if (!nullComparisonCondition.MatchCompEqualsNull(out var arg) || !arg.MatchLdLoc(switchValueVar)) return false; if (!target.Instructions[1].MatchBranch(out Block lengthCheckBlock)) return false; if (lengthCheckBlock.Instructions.Count != 2 || lengthCheckBlock.IncomingEdgeCount != 1) return false; if (!lengthCheckBlock.Instructions[0].MatchIfInstruction(out var lengthCheckCondition, out var exitBranch2)) return false; ILInstruction bodyBranch; if (lengthCheckCondition.MatchLogicNot(out arg)) { bodyBranch = exitBranch2; exitBranch2 = lengthCheckBlock.Instructions[1]; lengthCheckCondition = arg; } else { bodyBranch = lengthCheckBlock.Instructions[1]; } if (!(exitBranch2.MatchBranch(out defaultOrExitBlock) || exitBranch2.MatchLeave(out _))) return false; if (!MatchStringLengthCall(lengthCheckCondition, switchValueVar)) return false; if (!exitBranch.Match(exitBranch2).Success) return false; if (bodyBranch.MatchLeave(out _)) { bodyOrLeave = bodyBranch; return true; } if (bodyBranch.MatchBranch(out var bodyBlock)) { bodyOrLeave = bodyBlock; return true; } return false; } /// /// call get_Length(ldloc switchValueVar) /// bool MatchStringLengthCall(ILInstruction inst, ILVariable switchValueVar) { return inst is Call call && call.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && call.Method.IsAccessor && call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter && call.Method.AccessorOwner.Name == "Length" && call.Arguments.Count == 1 && call.Arguments[0].MatchLdLoc(switchValueVar); } /// /// Matches /// 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' /// - or - /// 'stloc(targetVar, call ComputeSpanHash(ldloc switchValue))' /// - or - /// 'stloc(targetVar, call ComputeReadOnlySpanHash(ldloc switchValue))' /// internal static bool MatchComputeStringOrReadOnlySpanHashCall(ILInstruction inst, ILVariable targetVar, out LdLoc switchValue) { switchValue = null; if (!inst.MatchStLoc(targetVar, out var value)) return false; if (value is Call c && c.Arguments.Count == 1 && c.Method.Name is "ComputeStringHash" or "ComputeSpanHash" or "ComputeReadOnlySpanHash" && c.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { if (c.Arguments[0] is not LdLoc ldloc) return false; switchValue = ldloc; return true; } return false; } /// /// Matches 'call string.op_Equality(ldloc(variable), ldstr stringValue)' /// or 'comp(ldloc(variable) == ldnull)' /// or 'call SequenceEqual(ldloc variable, call AsSpan(ldstr stringValue))' /// bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue, out bool isVBCompareString) { return MatchStringEqualityComparison(condition, out var v, out stringValue, out isVBCompareString) && v == variable; } /// /// Matches 'call string.op_Equality(ldloc(variable), ldstr stringValue)' /// or 'comp(ldloc(variable) == ldnull)' /// or 'call SequenceEqual(ldloc variable, call AsSpan(ldstr stringValue))' /// bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue, out bool isVBCompareString) { stringValue = null; variable = null; isVBCompareString = false; while (condition is Comp comp && comp.Kind == ComparisonKind.Inequality && comp.Right.MatchLdcI4(0)) { // if (x != 0) == if (x) condition = comp.Left; } if (condition is Call c) { ILInstruction left, right; if (c.Method.IsOperator && c.Method.Name == "op_Equality" && c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2) { left = c.Arguments[0]; right = c.Arguments[1]; } else if (c.Method.IsStatic && c.Method.Name == "CompareString" && c.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators" && c.Arguments.Count == 3) { left = c.Arguments[0]; right = c.Arguments[1]; // VB CompareString(): return 0 on equality -> condition is effectively negated. // Also, the empty string is considered equal to null. isVBCompareString = true; if (!c.Arguments[2].MatchLdcI4(0)) { // Option Compare Text: case insensitive comparison is not supported in C# return false; } } else if (c.Method.IsStatic && c.Method.Name == "SequenceEqual" && c.Method.DeclaringType.FullName == "System.MemoryExtensions" && c.Arguments.Count == 2) { left = c.Arguments[0]; if (c.Arguments[1] is Call { Method.IsStatic: true, Method.Name: "AsSpan", Method.DeclaringType.FullName: "System.MemoryExtensions", Arguments: [var ldStr] } asSpanCall) { right = ldStr; } else { return false; } } else { return false; } return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue); } else if (condition.MatchCompEqualsNull(out var arg)) { stringValue = null; return arg.MatchLdLoc(out variable); } else { return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs ================================================ // Copyright (c) 2015 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; using System.Reflection.Metadata; using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transforms array initialization pattern of System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray. /// For collection and object initializers see /// public class TransformArrayInitializers : IStatementTransform { StatementTransformContext context; void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.ArrayInitializers) return; this.context = context; try { if (DoTransform(context.Function, block, pos)) return; if (DoTransformMultiDim(context.Function, block, pos)) return; if (context.Settings.StackAllocInitializers && DoTransformStackAllocInitializer(block, pos)) return; if (DoTransformInlineRuntimeHelpersInitializeArray(block, pos)) return; } finally { this.context = null; } } bool DoTransform(ILFunction function, Block body, int pos) { if (pos >= body.Instructions.Count - 2) return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var arrayLength)) { if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { context.Step("HandleRuntimeHelperInitializeArray: single-dim", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return true; } if (arrayLength.Length == 1) { if (HandleSimpleArrayInitializer(function, body, pos + 1, v, arrayLength, out var arrayValues, out var instructionsToRemove)) { context.Step("HandleSimpleArrayInitializer: single-dim", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(arrayValues.Select( t => { var (indices, value) = t; if (value == null) value = GetNullExpression(elementType); return StElem(new LdLoc(tempStore), indices, value, elementType); } )); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return true; } if (HandleJaggedArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out ILVariable finalStore, out values, out instructionsToRemove)) { context.Step("HandleJaggedArrayInitializer: single-dim", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex((i, value) => StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(finalStore, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return true; } } } return false; } internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out ILInstruction replacement) { replacement = null; if (!context.Settings.ArrayInitializers) return false; if (!MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size)) return false; if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) return false; var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem); replacement = DecodeArrayInitializerOrUTF8StringLiteral(context, elementType, initialValue, size); return replacement != null; } internal static bool TransformRuntimeHelpersCreateSpanInitialization(Call inst, StatementTransformContext context, out ILInstruction replacement) { replacement = null; if (!context.Settings.ArrayInitializers) return false; if (!MatchRuntimeHelpersCreateSpan(inst, context, out var elementType, out var field)) return false; if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) return false; if (IsSubPatternOfCpblkInitializer(inst)) return false; var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem); var elementTypeSize = elementType.GetSize(); if (elementTypeSize <= 0 || initialValue.Length % elementTypeSize != 0) return false; var size = initialValue.Length / elementTypeSize; replacement = DecodeArrayInitializerOrUTF8StringLiteral(context, elementType, initialValue, size); return replacement != null; } private static bool IsSubPatternOfCpblkInitializer(Call inst) { if (inst.Parent is not AddressOf { Parent: Call { Parent: Cpblk cpblk } get_Item }) return false; return MatchGetStaticFieldAddress(get_Item, out _); } private static ILInstruction DecodeArrayInitializerOrUTF8StringLiteral(StatementTransformContext context, IType elementType, BlobReader initialValue, int size) { if (context.Settings.Utf8StringLiterals && elementType.IsKnownType(KnownTypeCode.Byte) && DecodeUTF8String(initialValue, size, out string text)) { return new LdStrUtf8(text); } var valuesList = new List(); if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList)) { var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType)); return BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray()); } return null; } private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out string text) { if (size > blob.RemainingBytes) { text = null; return false; } for (int i = 0; i < size; i++) { byte val = blob.CurrentPointer[i]; if (val == 0 && i == size - 1 && size > 1) { // Allow explicit null-termination character. } else if (val < 0x20 && val is not ((byte)'\r' or (byte)'\n' or (byte)'\t')) { // If the string has control characters, it's probably binary data and not a string. text = null; return false; } } text = Encoding.UTF8.GetString(blob.CurrentPointer, size); // Only use UTF8 string literal if we can perfectly roundtrip the data byte[] bytes = Encoding.UTF8.GetBytes(text); return MemoryExtensions.SequenceEqual(bytes, new ReadOnlySpan(blob.CurrentPointer, size)); } static bool MatchSpanTCtorWithPointerAndSize(NewObj newObj, StatementTransformContext context, out IType elementType, out FieldDefinition field, out int size) { field = default; size = default; elementType = null; IType type = newObj.Method.DeclaringType; if (!type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) return false; if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1) return false; elementType = type.TypeArguments[0]; if (!newObj.Arguments[0].UnwrapConv(ConversionKind.StopGCTracking).MatchLdsFlda(out var member)) return false; if (member.MetadataToken.IsNil) return false; if (!newObj.Arguments[1].MatchLdcI4(out size)) return false; field = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)member.MetadataToken); return true; } static bool MatchRuntimeHelpersCreateSpan(Call inst, StatementTransformContext context, out IType elementType, out FieldDefinition field) { field = default; elementType = null; if (!IsRuntimeHelpers(inst.Method.DeclaringType)) return false; if (inst.Arguments.Count != 1) return false; if (inst.Method is not { Name: "CreateSpan", TypeArguments: [var type] }) return false; elementType = type; if (!inst.Arguments[0].UnwrapConv(ConversionKind.StopGCTracking).MatchLdMemberToken(out var member)) return false; if (member.MetadataToken.IsNil) return false; field = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)member.MetadataToken); return true; } bool DoTransformMultiDim(ILFunction function, Block body, int pos) { if (pos >= body.Instructions.Count - 2) return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var length)) { if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { context.Step("HandleRuntimeHelpersInitializeArray: multi-dim", inst); var block = BlockFromInitializer(v, elementType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); ILInlining.InlineIfPossible(body, pos, context); return true; } if (HandleSimpleArrayInitializer(function, body, pos + 1, v, length, out var arrayValues, out var instructionsToRemove)) { context.Step("HandleSimpleArrayInitializer: multi-dim", inst); var block = new Block(BlockKind.ArrayInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, length.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(arrayValues.Select( t => { var (indices, value) = t; if (value == null) value = GetNullExpression(elementType); return StElem(new LdLoc(tempStore), indices, value, elementType); } )); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); return true; } } return false; } bool DoTransformStackAllocInitializer(Block body, int pos) { if (pos >= body.Instructions.Count - 2) return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var locallocExpr) && locallocExpr.MatchLocAlloc(out var lengthInst)) { if (lengthInst.MatchLdcI(out var lengthInBytes) && HandleCpblkInitializer(body, pos + 1, v, lengthInBytes, out var blob, out var elementType)) { context.Step("HandleCpblkInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); while (blob.RemainingBytes > 0) { block.Instructions.Add(StElemPtr(tempStore, blob.Offset, ReadElement(ref blob, elementType), elementType)); } block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(pos + 1); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return true; } if (HandleSequentialLocAllocInitializer(body, pos + 1, v, locallocExpr, out elementType, out StObj[] values, out int instructionsToRemove)) { context.Step("HandleSequentialLocAllocInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); block.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return true; } } 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) { blob = default; elementType = null; if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size)) return false; if (!dest.MatchLdLoc(v) || !MatchGetStaticFieldAddress(src, out var field) || !size.MatchLdcI4((int)length)) return false; if (!(v.IsSingleDefinition && v.LoadCount == 2)) return false; if (field.MetadataToken.IsNil) return false; if (!block.Instructions[pos + 1].MatchStLoc(out var finalStore, out var value)) { var otherLoadOfV = v.LoadInstructions.FirstOrDefault(l => !(l.Parent is Cpblk)); if (otherLoadOfV == null) return false; finalStore = otherLoadOfV.Parent.Extract(context); if (finalStore == null) return false; value = ((StLoc)finalStore.StoreInstructions[0]).Value; } var fd = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken); if (!fd.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) return false; if (value.MatchLdLoc(v)) { elementType = ((PointerType)finalStore.Type).ElementType; } else if (value is NewObj { Arguments: { Count: 2 } } newObj && newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT) && newObj.Arguments[0].MatchLdLoc(v) && newObj.Arguments[1].MatchLdcI4(out var elementCount)) { elementType = ((ParameterizedType)newObj.Method.DeclaringType).TypeArguments[0]; if (elementCount != length / elementType.GetSize()) return false; } else { return false; } blob = fd.GetInitialValue(context.PEFile, context.TypeSystem); return true; } static 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", TypeParameterCount: 0 }; unsafe bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove) { int elementCount = 0; long minExpectedOffset = 0; values = null; elementType = null; instructionsToRemove = 0; if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction)) return false; BlobReader blob = default; if (lengthInstruction.MatchLdcI(out long byteCount)) { if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size)) { if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) 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++) { // match the basic stobj pattern if (!block.Instructions[i].MatchStObj(out ILInstruction target, out var value, out var currentType) || value.Descendants.OfType().Any(inst => inst.Variable == store)) break; // 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; } // match the target // should be either ldloc store (at offset 0) // or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize' if (!target.MatchLdLoc(store)) { if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) return false; if (!left.MatchLdLoc(store)) break; var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow); if (offsetInst == null) return false; if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset) break; minExpectedOffset = offset; } if (values == 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]; } if (minExpectedOffset >= values.Length) break; values[minExpectedOffset] = (StObj)block.Instructions[i]; elementCount++; } if (values == null || store.Kind != VariableKind.StackSlot || store.StoreCount != 1 || store.AddressCount != 0 || store.LoadCount > values.Length + 1) return false; if (store.LoadInstructions.Last().Parent is StLoc finalStore) { elementType = ((PointerType)finalStore.Variable.Type).ElementType; } instructionsToRemove += elementCount; return elementCount <= values.Length; } ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type) { ILInstruction targetInst; if (storeInstruction.Target.MatchLdLoc(out _)) targetInst = new LdLoc(target); else if (storeInstruction.Target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { var old = (BinaryNumericInstruction)storeInstruction.Target; targetInst = new BinaryNumericInstruction(BinaryNumericOperator.Add, new LdLoc(target), right, old.CheckForOverflow, old.Sign); } else throw new NotSupportedException("This should never happen: Bug in HandleSequentialLocAllocInitializer!"); return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type); } StObj StElemPtr(ILVariable target, int offset, ILInstruction value, IType type) { var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction( BinaryNumericOperator.Add, new LdLoc(target), new Conv(new LdcI4(offset), PrimitiveType.I, false, Sign.Signed), false, Sign.None ); return new StObj(targetInst, value, type); } /// /// Handle simple case where RuntimeHelpers.InitializeArray is not used. /// internal static bool HandleSimpleArrayInitializer(ILFunction function, Block block, int pos, ILVariable store, int[] arrayLength, out (ILInstruction[] Indices, ILInstruction Value)[] values, out int instructionsToRemove) { instructionsToRemove = 0; int elementCount = 0; int length = arrayLength.Aggregate(1, (t, l) => t * l); // Cannot pre-allocate the result array, because we do not know yet, // whether there is in fact an array initializer. // To prevent excessive allocations, use min(|block|, arraySize) als initial capacity. // This should prevent list-resizing as well as out-of-memory errors. values = null; var valuesList = new List<(ILInstruction[] Indices, ILInstruction Value)>(Math.Min(block.Instructions.Count, length)); int[] nextMinimumIndex = new int[arrayLength.Length]; ILInstruction[] CalculateNextIndices(InstructionCollection indices, out bool exactMatch) { var nextIndices = new ILInstruction[arrayLength.Length]; exactMatch = true; if (indices == null) { for (int k = 0; k < nextIndices.Length; k++) { nextIndices[k] = new LdcI4(nextMinimumIndex[k]); } } else { bool previousComponentWasGreater = false; for (int k = 0; k < indices.Count; k++) { if (!indices[k].MatchLdcI4(out int index)) return null; // index must be in range [0..length[ and must be greater than or equal to nextMinimumIndex // to avoid running out of bounds or accidentally reordering instructions or overwriting previous instructions. // However, leaving array slots empty is allowed, as those are filled with default values when the // initializer block is generated. if (index < 0 || index >= arrayLength[k] || (!previousComponentWasGreater && index < nextMinimumIndex[k])) return null; nextIndices[k] = new LdcI4(nextMinimumIndex[k]); if (index != nextMinimumIndex[k]) { exactMatch = false; // this flag allows us to check whether the whole set of indices is smaller: // [3, 3] should be smaller than [4, 0] even though 3 > 0. if (index > nextMinimumIndex[k]) previousComponentWasGreater = true; } } } for (int k = nextMinimumIndex.Length - 1; k >= 0; k--) { nextMinimumIndex[k]++; if (nextMinimumIndex[k] < arrayLength[k] || k == 0) break; nextMinimumIndex[k] = 0; } return nextIndices; } int i = pos; int step; while (i < block.Instructions.Count) { InstructionCollection indices; // stobj elementType(ldelema elementType(ldloc store, indices), value) if (block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType type)) { if (!(target is LdElema ldelem && ldelem.Array.MatchLdLoc(store))) break; indices = ldelem.Indices; step = 1; // stloc s(ldelema elementType(ldloc store, indices)) // stobj elementType(ldloc s, value) } else if (block.Instructions[i].MatchStLoc(out var addressTemporary, out var addressValue)) { if (!(addressTemporary.IsSingleDefinition && addressTemporary.LoadCount == 1)) break; if (!(addressValue is LdElema ldelem && ldelem.Array.MatchLdLoc(store))) break; if (!(i + 1 < block.Instructions.Count)) break; if (!block.Instructions[i + 1].MatchStObj(out target, out value, out type)) break; if (!(target.MatchLdLoc(addressTemporary) && ldelem.Array.MatchLdLoc(store))) break; indices = ldelem.Indices; step = 2; } else { break; } if (value.Descendants.OfType().Any(inst => inst.Variable == store)) break; if (indices.Count != arrayLength.Length) break; bool exact; if (length <= 0) break; do { var nextIndices = CalculateNextIndices(indices, out exact); if (nextIndices == null) return false; if (exact) { valuesList.Add((nextIndices, value)); elementCount++; instructionsToRemove += step; } else { valuesList.Add((nextIndices, null)); } } while (valuesList.Count < length && !exact); i += step; } if (i < block.Instructions.Count) { if (block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType type)) { // An element of the array is modified directly after the initializer: // Abort transform, so that partial initializers are not constructed. if (target is LdElema ldelem && ldelem.Array.MatchLdLoc(store)) return false; } } if (pos + instructionsToRemove >= block.Instructions.Count) return false; if (elementCount == 0) return false; bool mustTransform = ILInlining.IsCatchWhenBlock(block) || ILInlining.IsInConstructorInitializer(function, block.Instructions[pos]); // If there are not enough user-defined elements after scanning all instructions, we can abort the transform. // This avoids unnecessary allocations and OOM crashes (in case of int.MaxValue). if (elementCount < length / 3 - 5) { if (!mustTransform) return false; // .NET does not allow allocation of arrays >= 2 GB. if (length >= int.MaxValue) return false; } for (int j = valuesList.Count; j < length; j++) { var nextIndices = CalculateNextIndices(null, out _); if (nextIndices == null) return false; valuesList.Add((nextIndices, null)); } values = valuesList.ToArray(); return true; } bool HandleJaggedArrayInitializer(Block block, int pos, ILVariable store, IType elementType, int length, out ILVariable finalStore, out ILInstruction[] values, out int instructionsToRemove) { instructionsToRemove = 0; finalStore = null; // Cannot pre-allocate the result array, because we do not know yet, // whether there is in fact an array initializer. // To prevent excessive allocations, use min(|block|, arraySize) als initial capacity. // This should prevent list-resizing as well as out-of-memory errors. values = null; var valuesList = new List(Math.Min(block.Instructions.Count, length)); for (int i = 0; i < length; i++) { // 1. Instruction: (optional) temporary copy of store bool hasTemporaryCopy = block.Instructions[pos].MatchStLoc(out var temp, out var storeLoad) && storeLoad.MatchLdLoc(store); ILInstruction initializer; if (hasTemporaryCopy) { if (!MatchJaggedArrayStore(block, pos + 1, temp, i, out initializer, out _)) return false; } else { if (!MatchJaggedArrayStore(block, pos, store, i, out initializer, out _)) return false; } valuesList.Add(initializer); int inc = hasTemporaryCopy ? 3 : 2; pos += inc; instructionsToRemove += inc; } // In case there is an extra copy of the store variable // Remove it and use its value instead. if (block.Instructions[pos].MatchStLoc(out finalStore, out var array)) { if (!array.MatchLdLoc(store)) return false; instructionsToRemove++; } else { finalStore = store; } values = valuesList.ToArray(); return true; } bool MatchJaggedArrayStore(Block block, int pos, ILVariable store, int index, out ILInstruction initializer, out IType type) { initializer = null; type = null; // 3. Instruction: stobj(ldelema(ldloc temp, ldc.i4 0), ldloc tempArrayLoad) var finalInstruction = block.Instructions.ElementAtOrDefault(pos + 1); if (finalInstruction == null || !finalInstruction.MatchStObj(out var tempAccess, out var tempArrayLoad, out type) || !tempArrayLoad.MatchLdLoc(out var initializerStore)) { return false; } if (!(tempAccess is LdElema elemLoad) || !elemLoad.Array.MatchLdLoc(store) || elemLoad.Indices.Count != 1 || !elemLoad.Indices[0].MatchLdcI4(index)) { return false; } // 2. Instruction: stloc(temp) with block (array initializer) var nextInstruction = block.Instructions.ElementAtOrDefault(pos); return nextInstruction != null && nextInstruction.MatchStLoc(initializerStore, out initializer) && initializer.OpCode == OpCode.Block; } static Block BlockFromInitializer(ILVariable v, IType elementType, int[] arrayLength, ILInstruction[] values) { var block = new Block(BlockKind.ArrayInitializer); block.Instructions.Add(new StLoc(v, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); int step = arrayLength.Length + 1; var indices = new List(); for (int i = 0; i < values.Length / step; i++) { // values array is filled backwards var value = values[step * i]; indices.EnsureCapacity(step - 1); for (int j = step - 1; j >= 1; j--) { indices.Add(values[step * i + j]); } block.Instructions.Add(StElem(new LdLoc(v), indices.ToArray(), value, elementType)); indices.Clear(); } block.FinalInstruction = new LdLoc(v); return block; } static bool MatchNewArr(ILInstruction instruction, out IType arrayType, out int[] length) { length = null; arrayType = null; if (!(instruction is NewArr newArr)) return false; arrayType = newArr.Type; var args = newArr.Indices; length = new int[args.Count]; for (int i = 0; i < args.Count; i++) { if (!args[i].MatchLdcI4(out int value) || value <= 0) return false; length[i] = value; } return true; } bool MatchInitializeArrayCall(ILInstruction instruction, out ILInstruction array, out FieldDefinition field) { array = null; field = default; if (!(instruction is Call call) || call.Arguments.Count != 2) return false; IMethod method = call.Method; if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null) return false; if (!IsRuntimeHelpers(method.DeclaringType)) return false; array = call.Arguments[0]; if (!call.Arguments[1].MatchLdMemberToken(out var member)) return false; if (member.MetadataToken.IsNil) return false; field = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)member.MetadataToken); return true; } bool HandleRuntimeHelpersInitializeArray(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos) { values = null; foundPos = -1; if (MatchInitializeArrayCall(body.Instructions[pos], out var arrayInst, out var field) && arrayInst.MatchLdLoc(array)) { if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) { var valuesList = new List(); BlobReader initialValue; try { initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem); } catch (BadImageFormatException ex) { array.Function.Warnings.Add($"IL_{body.Instructions[pos].ILRanges.FirstOrDefault().Start:x4}: {ex.Message}"); return false; } if (DecodeArrayInitializer(arrayType, initialValue, arrayLength, valuesList)) { values = valuesList.ToArray(); foundPos = pos; return true; } } } return false; } /// /// call InitializeArray(newarr T(size), ldmembertoken fieldToken) /// => /// Block (ArrayInitializer) { /// stloc i(newarr T(size)) /// stobj T(ldelema T(... indices ...), value) /// final: ldloc i /// } /// bool DoTransformInlineRuntimeHelpersInitializeArray(Block body, int pos) { var inst = body.Instructions[pos]; if (!MatchInitializeArrayCall(inst, out var arrayInst, out var field)) return false; if (!MatchNewArr(arrayInst, out var elementType, out var arrayLength)) return false; if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) return false; var valuesList = new List(); var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem); if (!DecodeArrayInitializer(elementType, initialValue, arrayLength, valuesList)) return false; context.Step("InlineRuntimeHelpersInitializeArray: single-dim", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType, arrayLength.Length)); var block = BlockFromInitializer(tempStore, elementType, arrayLength, valuesList.ToArray()); body.Instructions[pos] = block; ILInlining.InlineIfPossible(body, pos, context); return true; } static bool DecodeArrayInitializer(IType type, BlobReader initialValue, int[] arrayLength, List output) { TypeCode typeCode = ReflectionHelper.GetTypeCode(type); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Byte: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadByte())); case TypeCode.SByte: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadSByte())); case TypeCode.Int16: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt16())); case TypeCode.Char: case TypeCode.UInt16: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); case TypeCode.Int32: case TypeCode.UInt32: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt32())); case TypeCode.Int64: case TypeCode.UInt64: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI8(r.ReadInt64())); case TypeCode.Single: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF4(r.ReadSingle())); case TypeCode.Double: return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF8(r.ReadDouble())); case TypeCode.Object: case TypeCode.Empty: var typeDef = type.GetDefinition(); if (typeDef != null && typeDef.Kind == TypeKind.Enum) return DecodeArrayInitializer(typeDef.EnumUnderlyingType, initialValue, arrayLength, output); return false; default: return false; } } delegate ILInstruction ValueDecoder(ref BlobReader reader); static bool DecodeArrayInitializer(BlobReader initialValue, int[] arrayLength, List output, TypeCode elementType, ValueDecoder decoder) { int elementSize = ElementSizeOf(elementType); var totalLength = arrayLength.Aggregate(1, (t, l) => t * l); if (initialValue.RemainingBytes < (totalLength * elementSize)) return false; output.EnsureCapacity(totalLength + totalLength * arrayLength.Length); for (int i = 0; i < totalLength; i++) { output.Add(decoder(ref initialValue)); int next = i; for (int j = arrayLength.Length - 1; j >= 0; j--) { output.Add(new LdcI4(next % arrayLength[j])); next /= arrayLength[j]; } } return true; } static ILInstruction StElem(ILInstruction array, ILInstruction[] indices, ILInstruction value, IType type) { if (type.GetStackType() != value.ResultType) { value = new Conv(value, type.ToPrimitiveType(), false, Sign.None); } return new StObj(new LdElema(type, array, indices) { DelayExceptions = true }, value, type); } internal static ILInstruction GetNullExpression(IType elementType) { ITypeDefinition typeDef = elementType.GetEnumUnderlyingType().GetDefinition(); if (typeDef == null) return new DefaultValue(elementType); switch (typeDef.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.Char: case KnownTypeCode.SByte: case KnownTypeCode.Byte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: case KnownTypeCode.Int32: case KnownTypeCode.UInt32: return new LdcI4(0); case KnownTypeCode.Int64: case KnownTypeCode.UInt64: return new LdcI8(0); case KnownTypeCode.Single: return new LdcF4(0); case KnownTypeCode.Double: return new LdcF8(0); case KnownTypeCode.Decimal: return new LdcDecimal(0); case KnownTypeCode.Void: throw new ArgumentException("void is not a valid element type!"); case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: default: return new DefaultValue(elementType); } } static int ElementSizeOf(TypeCode elementType) { switch (elementType) { case TypeCode.Boolean: case TypeCode.Byte: case TypeCode.SByte: return 1; case TypeCode.Char: case TypeCode.Int16: case TypeCode.UInt16: return 2; case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Single: return 4; case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Double: return 8; default: throw new ArgumentOutOfRangeException(nameof(elementType)); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs ================================================ // Copyright (c) 2015 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.Diagnostics; using System.Linq; using System.Linq.Expressions; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Constructs compound assignments and inline assignments. /// /// /// This is a statement transform; /// but some portions are executed as an expression transform instead /// (with HandleCompoundAssign() as entry point) /// public class TransformAssignment : IStatementTransform { StatementTransformContext context; void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { this.context = context; if (context.Settings.MakeAssignmentExpressions) { if (TransformInlineAssignmentStObjOrCall(block, pos) || TransformInlineAssignmentLocal(block, pos)) { // both inline assignments create a top-level stloc which might affect inlining context.RequestRerun(); return; } } if (context.Settings.IntroduceIncrementAndDecrement) { if (TransformPostIncDecOperatorWithInlineStore(block, pos) || TransformPostIncDecOperator(block, pos) || TransformPreIncDecOperatorWithInlineStore(block, pos)) { // again, new top-level stloc might need inlining: context.RequestRerun(); return; } } } /// /// stloc s(value) /// stloc l(ldloc s) /// stobj(..., ldloc s) /// where ... is pure and does not use s or l, /// and where neither the 'stloc s' nor the 'stobj' truncates /// --> /// stloc l(stobj (..., value)) /// /// e.g. used for inline assignment to instance field /// /// -or- /// /// /// stloc s(value) /// stobj (..., ldloc s) /// where ... is pure and does not use s, and where the 'stobj' does not truncate /// --> /// stloc s(stobj (..., value)) /// /// e.g. used for inline assignment to static field /// /// -or- /// /// /// stloc s(value) /// call set_Property(..., ldloc s) /// where the '...' arguments are pure and not using 's' /// --> /// stloc s(Block InlineAssign { call set_Property(..., stloc i(value)); final: ldloc i }) /// new temporary 'i' has type of the property; transform only valid if 'stloc i' doesn't truncate /// bool TransformInlineAssignmentStObjOrCall(Block block, int pos) { var inst = block.Instructions[pos] as StLoc; // in some cases it can be a compiler-generated local if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local)) return false; if (IsImplicitTruncation(inst.Value, inst.Variable.Type, context.TypeSystem)) { // 'stloc s' is implicitly truncating the value return false; } ILVariable local; int nextPos; if (block.Instructions[pos + 1] is StLoc localStore) { // with extra local if (localStore.Variable.Kind != VariableKind.Local || !localStore.Value.MatchLdLoc(inst.Variable)) return false; // if we're using an extra local, we'll delete "s", so check that that doesn't have any additional uses if (!(inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 2)) return false; local = localStore.Variable; nextPos = pos + 2; } else { local = inst.Variable; localStore = null; nextPos = pos + 1; if (local.LoadCount == 1 && local.AddressCount == 0) { // inline assignment would produce a dead store in this case, which is ugly // and causes problems with the deconstruction transform. return false; } } if (block.Instructions[nextPos] is StObj stobj) { // unaligned.stobj cannot be inlined in C# if (stobj.UnalignedPrefix > 0) return false; if (!stobj.Value.MatchLdLoc(inst.Variable)) return false; if (!SemanticHelper.IsPure(stobj.Target.Flags) || inst.Variable.IsUsedWithin(stobj.Target)) return false; var pointerType = stobj.Target.InferType(context.TypeSystem); IType newType = stobj.Type; if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointerType, stobj.Type)) { if (pointerType is ByReferenceType byref) newType = byref.ElementType; else if (pointerType is PointerType pointer) newType = pointer.ElementType; } var truncation = CheckImplicitTruncation(inst.Value, newType, context.TypeSystem); if (truncation == ImplicitTruncationResult.ValueChanged) { // 'stobj' is implicitly truncating the value return false; } if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch) { // Change the sign of the type to skip implicit truncation newType = SwapSign(newType, context.TypeSystem); } context.Step("Inline assignment stobj", stobj); stobj.Type = newType; block.Instructions.Remove(localStore); block.Instructions.Remove(stobj); stobj.Value = inst.Value; inst.ReplaceWith(new StLoc(local, stobj)); // note: our caller will trigger a re-run, which will call HandleStObjCompoundAssign if applicable return true; } else if (block.Instructions[nextPos] is CallInstruction call) { // call must be a setter call: if (!(call.OpCode == OpCode.Call || call.OpCode == OpCode.CallVirt)) return false; if (call.ResultType != StackType.Void || call.Arguments.Count == 0) return false; IProperty property = call.Method.AccessorOwner as IProperty; if (property == null) return false; if (!call.Method.Equals(property.Setter)) return false; if (!(property.IsIndexer || property.Setter.Parameters.Count == 1)) { // this is a parameterized property, which cannot be expressed as C# code. // setter calls are not valid in expression context, if property syntax cannot be used. return false; } if (!call.Arguments.Last().MatchLdLoc(inst.Variable)) return false; foreach (var arg in call.Arguments.SkipLast(1)) { if (!SemanticHelper.IsPure(arg.Flags) || inst.Variable.IsUsedWithin(arg)) return false; } if (IsImplicitTruncation(inst.Value, call.Method.Parameters.Last().Type, context.TypeSystem)) { // setter call is implicitly truncating the value return false; } // stloc s(Block InlineAssign { call set_Property(..., stloc i(value)); final: ldloc i }) context.Step("Inline assignment call", call); block.Instructions.Remove(localStore); block.Instructions.Remove(call); var newVar = context.Function.RegisterVariable(VariableKind.StackSlot, call.Method.Parameters.Last().Type); call.Arguments[call.Arguments.Count - 1] = new StLoc(newVar, inst.Value); var inlineBlock = new Block(BlockKind.CallInlineAssign) { Instructions = { call }, FinalInstruction = new LdLoc(newVar) }; inst.ReplaceWith(new StLoc(local, inlineBlock)); // because the ExpressionTransforms don't look into inline blocks, manually trigger HandleCallCompoundAssign if (HandleCompoundAssign(call, context)) { // if we did construct a compound assignment, it should have made our inline block redundant: Debug.Assert(!inlineBlock.IsConnected); } return true; } else { return false; } } private static IType SwapSign(IType type, ICompilation compilation) { return type.ToPrimitiveType() switch { PrimitiveType.I1 => compilation.FindType(KnownTypeCode.Byte), PrimitiveType.I2 => compilation.FindType(KnownTypeCode.UInt16), PrimitiveType.I4 => compilation.FindType(KnownTypeCode.UInt32), PrimitiveType.I8 => compilation.FindType(KnownTypeCode.UInt64), PrimitiveType.U1 => compilation.FindType(KnownTypeCode.SByte), PrimitiveType.U2 => compilation.FindType(KnownTypeCode.Int16), PrimitiveType.U4 => compilation.FindType(KnownTypeCode.Int32), PrimitiveType.U8 => compilation.FindType(KnownTypeCode.Int64), PrimitiveType.I => compilation.FindType(KnownTypeCode.UIntPtr), PrimitiveType.U => compilation.FindType(KnownTypeCode.IntPtr), _ => throw new ArgumentException("Type must have an opposing sign: " + type, nameof(type)) }; } static ILInstruction UnwrapSmallIntegerConv(ILInstruction inst, out Conv conv) { conv = inst as Conv; if (conv != null && conv.Kind == ConversionKind.Truncate && conv.TargetType.IsSmallIntegerType()) { // for compound assignments to small integers, the compiler emits a "conv" instruction return conv.Argument; } else { return inst; } } static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType, DecompilerSettings settings) { if (!NumericCompoundAssign.IsBinaryCompatibleWithType(binary, targetType, settings)) return false; if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow)) return false; // conv does not match binary operation return true; } static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall, out Action finalizeMatch) { finalizeMatch = null; if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) return false; if (setterCall.OpCode != getterCall.OpCode) return false; var owner = getterCall.Method.AccessorOwner as IProperty; if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter)) return false; if (setterCall.Arguments.Count != getterCall.Arguments.Count + 1) return false; // Ensure that same arguments are passed to getterCall and setterCall: for (int j = 0; j < getterCall.Arguments.Count; j++) { if (setterCall.Arguments[j].MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { if (getterCall.Arguments[j].MatchLdLoc(v)) { // OK, setter call argument is saved in temporary that is re-used for getter call if (finalizeMatch == null) { finalizeMatch = AdjustArguments; } continue; } } if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags)) return false; if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) return false; } return true; void AdjustArguments(ILTransformContext context) { Debug.Assert(setterCall.Arguments.Count == getterCall.Arguments.Count + 1); for (int j = 0; j < getterCall.Arguments.Count; j++) { if (setterCall.Arguments[j].MatchStLoc(out var v, out var value)) { Debug.Assert(v.IsSingleDefinition && v.LoadCount == 1); Debug.Assert(getterCall.Arguments[j].MatchLdLoc(v)); getterCall.Arguments[j] = value; } } } } /// /// Transform compound assignments where the return value is not being used, /// or where there's an inlined assignment within the setter call. /// /// Patterns handled: /// 1. /// callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value)) /// ==> compound.op.new(callvirt get_Property(ldloc S_1), value) /// 2. /// callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) /// ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value)) /// 3. /// stobj(target, binary.op(ldobj(target), ...)) /// where target is pure /// => compound.op(target, ...) /// /// /// Called by ExpressionTransforms, or after the inline-assignment transform for setters. /// internal static bool HandleCompoundAssign(ILInstruction compoundStore, StatementTransformContext context) { if (!context.Settings.MakeAssignmentExpressions || !context.Settings.IntroduceIncrementAndDecrement) return false; if (compoundStore is CallInstruction && compoundStore.SlotInfo != Block.InstructionSlot) { // replacing 'call set_Property' with a compound assignment instruction // changes the return value of the expression, so this is only valid on block-level. return false; } if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue, context.TypeSystem)) return false; // targetType = The type of the property/field/etc. being stored to. // setterValue = The value being stored. var storeInSetter = setterValue as StLoc; if (storeInSetter != null) { // We'll move the stloc to top-level: // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value)) setterValue = storeInSetter.Value; if (storeInSetter.Variable.Type.IsSmallIntegerType()) { // 'stloc v' implicitly truncates the value. // Ensure that type of 'v' matches the type of the property: if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize()) return false; if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign()) return false; } } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { if (compoundStore is StLoc) { // transform local variables only for user-defined operators return false; } if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType, context.Settings)) return false; context.Step($"Compound assignment (binary.numeric)", compoundStore); finalizeMatch?.Invoke(context); newInst = new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) return false; if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name, context.Settings) == null) return false; rhs = operatorCall.Arguments[1]; } else if (operatorCall.Arguments.Count == 1) { if (!UserDefinedCompoundAssign.IsIncrementOrDecrement(operatorCall.Method, context.Settings)) return false; // use a dummy node so that we don't need a dedicated instruction for user-defined unary operator calls rhs = new LdcI4(1); } else { return false; } if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step($"Compound assignment (user-defined binary)", compoundStore); finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, rhs); } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (dynamic binary)", compoundStore); finalizeMatch?.Invoke(context); newInst = new DynamicCompoundAssign(ToCompound(dynamicBinaryOp.Operation), dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); static ExpressionType ToCompound(ExpressionType from) { return from switch { ExpressionType.Add => ExpressionType.AddAssign, ExpressionType.AddChecked => ExpressionType.AddAssignChecked, ExpressionType.And => ExpressionType.AndAssign, ExpressionType.Divide => ExpressionType.DivideAssign, ExpressionType.ExclusiveOr => ExpressionType.ExclusiveOrAssign, ExpressionType.LeftShift => ExpressionType.LeftShiftAssign, ExpressionType.Modulo => ExpressionType.ModuloAssign, ExpressionType.Multiply => ExpressionType.MultiplyAssign, ExpressionType.MultiplyChecked => ExpressionType.MultiplyAssignChecked, ExpressionType.Or => ExpressionType.OrAssign, ExpressionType.Power => ExpressionType.PowerAssign, ExpressionType.RightShift => ExpressionType.RightShiftAssign, ExpressionType.Subtract => ExpressionType.SubtractAssign, ExpressionType.SubtractChecked => ExpressionType.SubtractAssignChecked, _ => from }; } } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation if (compoundStore is StLoc) { // transform local variables only for user-defined operators return false; } if (concatCall.Arguments.Count != 2) return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) return false; var arg = concatCall.Arguments[0]; if (arg is Call call && CallBuilder.IsStringToReadOnlySpanCharImplicitConversion(call.Method)) { arg = call.Arguments[0]; if (!(concatCall.Arguments[1] is NewObj { Arguments: [AddressOf addressOf] } newObj) || !ILInlining.IsReadOnlySpanCharCtor(newObj.Method)) { return false; } } if (!IsMatchingCompoundLoad(arg, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (string concatenation)", compoundStore); finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, concatCall.Arguments[1]); } else { return false; } newInst.AddILRange(setterValue); if (storeInSetter != null) { storeInSetter.Value = newInst; newInst = storeInSetter; context.RequestRerun(); // moving stloc to top-level might trigger inlining } compoundStore.ReplaceWith(newInst); if (newInst.Parent is Block inlineAssignBlock && inlineAssignBlock.Kind == BlockKind.CallInlineAssign) { // It's possible that we first replaced the instruction in an inline-assign helper block. // In such a situation, we know from the block invariant that we're have a storeInSetter. Debug.Assert(storeInSetter != null); Debug.Assert(storeInSetter.Variable.IsSingleDefinition && storeInSetter.Variable.LoadCount == 1); Debug.Assert(inlineAssignBlock.Instructions.Single() == storeInSetter); Debug.Assert(inlineAssignBlock.FinalInstruction.MatchLdLoc(storeInSetter.Variable)); // Block CallInlineAssign { stloc I_0(compound.op(...)); final: ldloc I_0 } // --> compound.op(...) inlineAssignBlock.ReplaceWith(storeInSetter.Value); } return true; } /// /// stloc s(value) /// stloc l(ldloc s) /// where neither 'stloc s' nor 'stloc l' truncates the value /// --> /// stloc s(stloc l(value)) /// bool TransformInlineAssignmentLocal(Block block, int pos) { var inst = block.Instructions[pos] as StLoc; var nextInst = block.Instructions.ElementAtOrDefault(pos + 1) as StLoc; if (inst == null || nextInst == null) return false; if (inst.Variable.Kind != VariableKind.StackSlot) return false; if (!(nextInst.Variable.Kind == VariableKind.Local || nextInst.Variable.Kind == VariableKind.Parameter)) return false; if (!nextInst.Value.MatchLdLoc(inst.Variable)) return false; if (IsImplicitTruncation(inst.Value, inst.Variable.Type, context.TypeSystem)) { // 'stloc s' is implicitly truncating the stack value return false; } if (IsImplicitTruncation(inst.Value, nextInst.Variable.Type, context.TypeSystem)) { // 'stloc l' is implicitly truncating the stack value return false; } if (nextInst.Variable.StackType == StackType.Ref) { // ref locals need to be initialized when they are declared, so // we can only use inline assignments when we know that the // ref local is definitely assigned. // We don't have an easy way to check for that in this transform, // so avoid inline assignments to ref locals for now. return false; } context.Step("Inline assignment to local variable", inst); var value = inst.Value; var var = nextInst.Variable; var stackVar = inst.Variable; block.Instructions.RemoveAt(pos); nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value))); return true; } internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) { return CheckImplicitTruncation(value, type, compilation, allowNullableValue) != ImplicitTruncationResult.ValuePreserved; } internal enum ImplicitTruncationResult : byte { /// /// The value is not implicitly truncated. /// ValuePreserved, /// /// The value is implicitly truncated. /// ValueChanged, /// /// The value is implicitly truncated, but the sign of the target type can be changed to remove the truncation. /// ValueChangedDueToSignMismatch } /// /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' /// due to implicit truncation. /// internal static ImplicitTruncationResult CheckImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) { if (!type.IsSmallIntegerType()) { // Implicit truncation in ILAst only happens for small integer types; // other types of implicit truncation in IL cause the ILReader to insert // conv instructions. return ImplicitTruncationResult.ValuePreserved; } // With small integer types, test whether the value might be changed by // truncation (based on type.GetSize()) followed by sign/zero extension (based on type.GetSign()). // (it's OK to have false-positives here if we're unsure) if (value.MatchLdcI4(out int val)) { bool valueFits = (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) switch { KnownTypeCode.Boolean => val == 0 || val == 1, KnownTypeCode.Byte => val >= byte.MinValue && val <= byte.MaxValue, KnownTypeCode.SByte => val >= sbyte.MinValue && val <= sbyte.MaxValue, KnownTypeCode.Int16 => val >= short.MinValue && val <= short.MaxValue, KnownTypeCode.UInt16 or KnownTypeCode.Char => val >= ushort.MinValue && val <= ushort.MaxValue, _ => false }; return valueFits ? ImplicitTruncationResult.ValuePreserved : ImplicitTruncationResult.ValueChanged; } else if (value is Conv conv) { PrimitiveType primitiveType = type.ToPrimitiveType(); PrimitiveType convTargetType = conv.TargetType; if (convTargetType == primitiveType) return ImplicitTruncationResult.ValuePreserved; if (primitiveType.GetSize() == convTargetType.GetSize() && primitiveType.GetSign() != convTargetType.GetSign() && primitiveType.HasOppositeSign()) return ImplicitTruncationResult.ValueChangedDueToSignMismatch; return ImplicitTruncationResult.ValueChanged; } else if (value is Comp) { return ImplicitTruncationResult.ValuePreserved; // comp returns 0 or 1, which always fits } else if (value is BinaryNumericInstruction bni) { switch (bni.Operator) { case BinaryNumericOperator.BitAnd: case BinaryNumericOperator.BitOr: case BinaryNumericOperator.BitXor: // If both input values fit into the type without truncation, // the result of a binary operator will also fit. var leftTruncation = CheckImplicitTruncation(bni.Left, type, compilation, allowNullableValue); // If the left side is truncating and a sign change is not possible we do not need to evaluate the right side if (leftTruncation == ImplicitTruncationResult.ValueChanged) return ImplicitTruncationResult.ValueChanged; var rightTruncation = CheckImplicitTruncation(bni.Right, type, compilation, allowNullableValue); return CommonImplicitTruncation(leftTruncation, rightTruncation); } } else if (value is IfInstruction ifInst) { var trueTruncation = CheckImplicitTruncation(ifInst.TrueInst, type, compilation, allowNullableValue); // If the true branch is truncating and a sign change is not possible we do not need to evaluate the false branch if (trueTruncation == ImplicitTruncationResult.ValueChanged) return ImplicitTruncationResult.ValueChanged; var falseTruncation = CheckImplicitTruncation(ifInst.FalseInst, type, compilation, allowNullableValue); return CommonImplicitTruncation(trueTruncation, falseTruncation); } else { IType inferredType = value.InferType(compilation); if (allowNullableValue) { inferredType = NullableType.GetUnderlyingType(inferredType); } if (inferredType.Kind != TypeKind.Unknown) { var inferredPrimitive = inferredType.ToPrimitiveType(); var primitiveType = type.ToPrimitiveType(); bool sameSign = inferredPrimitive.GetSign() == primitiveType.GetSign(); if (inferredPrimitive.GetSize() <= primitiveType.GetSize() && sameSign) return ImplicitTruncationResult.ValuePreserved; if (inferredPrimitive.GetSize() == primitiveType.GetSize() && !sameSign && primitiveType.HasOppositeSign()) return ImplicitTruncationResult.ValueChangedDueToSignMismatch; return ImplicitTruncationResult.ValueChanged; } } // In unknown cases, assume that the value might be changed by truncation. return ImplicitTruncationResult.ValueChanged; } private static ImplicitTruncationResult CommonImplicitTruncation(ImplicitTruncationResult left, ImplicitTruncationResult right) { if (left == right) return left; // Note: in all cases where left!=right, we return ValueChanged: // if only one side can be fixed by changing the sign, we don't want to change the sign of the other side. return ImplicitTruncationResult.ValueChanged; } /// /// Gets whether 'inst' is a possible store for use as a compound store. /// /// /// Output parameters: /// storeType: The type of the value being stored. /// value: The value being stored (will be analyzed further to detect compound assignments) /// /// Every IsCompoundStore() call should be followed by an IsMatchingCompoundLoad() call. /// static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value, ICompilation compilation) { value = null; storeType = null; if (inst is StObj stobj) { // stobj.Type may just be 'int' (due to stind.i4) when we're actually operating on a 'ref MyEnum'. // Try to determine the real type of the object we're modifying: storeType = stobj.Target.InferType(compilation); if (storeType is ByReferenceType refType) { if (TypeUtils.IsCompatibleTypeForMemoryAccess(refType.ElementType, stobj.Type)) { storeType = refType.ElementType; } else { storeType = stobj.Type; } } else if (storeType is PointerType pointerType) { if (TypeUtils.IsCompatibleTypeForMemoryAccess(pointerType.ElementType, stobj.Type)) { storeType = pointerType.ElementType; } else { storeType = stobj.Type; } } else { storeType = stobj.Type; } value = stobj.Value; return SemanticHelper.IsPure(stobj.Target.Flags); } else if (inst is CallInstruction call && (call.OpCode == OpCode.Call || call.OpCode == OpCode.CallVirt)) { if (call.Method.Parameters.Count == 0) { return false; } foreach (var arg in call.Arguments.SkipLast(1)) { if (arg.MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { continue; // OK, IsMatchingCompoundLoad can perform an adjustment in this special case } if (!SemanticHelper.IsPure(arg.Flags)) { return false; } } storeType = call.Method.Parameters.Last().Type; value = call.Arguments.Last(); return IsSameMember(call.Method, (call.Method.AccessorOwner as IProperty)?.Setter); } else if (inst is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.Parameter)) { storeType = stloc.Variable.Type; value = stloc.Value; return true; } else { return false; } } /// /// Checks whether 'load' and 'store' both access the same store, and can be combined to a compound assignment. /// /// The load instruction to test. /// The compound store to test against. Must have previously been tested via IsCompoundStore() /// The target to use for the compound assignment instruction. /// The target kind to use for the compound assignment instruction. /// If set to a non-null value, call this delegate to fix up minor mismatches between getter and setter. /// /// If given a non-null value, this function returns false if the forbiddenVariable is used in the load/store instructions. /// Some transforms effectively move a store around, /// which is only valid if the variable stored to does not occur in the compound load/store. /// /// /// Instruction preceding the load. /// static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, out ILInstruction target, out CompoundTargetKind targetKind, out Action finalizeMatch, ILVariable forbiddenVariable = null, ILInstruction previousInstruction = null) { target = null; targetKind = 0; finalizeMatch = null; if (load is LdObj ldobj && store is StObj stobj) { Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); if (!SemanticHelper.IsPure(ldobj.Target.Flags)) return false; if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(ldobj.Target)) return false; target = ldobj.Target; targetKind = CompoundTargetKind.Address; if (ldobj.Target.Match(stobj.Target).Success) { return true; } else if (IsDuplicatedAddressComputation(stobj.Target, ldobj.Target)) { // Use S_0 as target, so that S_0 can later be eliminated by inlining. // (we can't eliminate previousInstruction right now, because it's before the transform's starting instruction) target = stobj.Target; return true; } else { return false; } } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; target = load; targetKind = CompoundTargetKind.Property; return true; } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) { if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) return false; target = new LdLoca(ldloc.Variable).WithILRange(ldloc); targetKind = CompoundTargetKind.Address; finalizeMatch = context => context.Function.RecombineVariables(ldloc.Variable, stloc.Variable); return true; } else { return false; } bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loadTarget) { // Sometimes roslyn duplicates the address calculation: // stloc S_0(ldloc refParam) // stloc V_0(ldobj System.Int32(ldloc refParam)) // stobj System.Int32(ldloc S_0, binary.add.i4(ldloc V_0, ldc.i4 1)) while (storeTarget is LdFlda storeLdFlda && loadTarget is LdFlda loadLdFlda) { if (!storeLdFlda.Field.Equals(loadLdFlda.Field)) return false; storeTarget = storeLdFlda.Target; loadTarget = loadLdFlda.Target; } if (!storeTarget.MatchLdLoc(out var s)) return false; if (!(s.Kind == VariableKind.StackSlot && s.IsSingleDefinition && s != forbiddenVariable)) return false; if (s.StoreInstructions.SingleOrDefault() != previousInstruction) return false; return previousInstruction is StLoc addressStore && addressStore.Value.Match(loadTarget).Success; } } /// /// stloc l(stloc target(binary.add(ldloc target, ldc.i4 1))) /// bool TransformPreIncDecOperatorWithInlineStore(Block block, int pos) { var store = block.Instructions[pos]; if (!IsCompoundStore(store, out var targetType1, out var value1, context.TypeSystem)) { return false; } if (!IsCompoundStore(value1, out var targetType2, out var value2, context.TypeSystem)) { return false; } if (targetType1 != targetType2) return false; var targetType = targetType1; var stloc_outer = store as StLoc; var stloc_inner = value1 as StLoc; LdLoc ldloc; var binary = UnwrapSmallIntegerConv(value2, out var conv) as BinaryNumericInstruction; if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) { if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; if (conv is not null) { var primitiveType = targetType.ToPrimitiveType(); if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign()) targetType = SwapSign(targetType, context.TypeSystem); } if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) return false; ldloc = binary.Left as LdLoc; } else if (value2 is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) return false; if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations ldloc = operatorCall.Arguments[0] as LdLoc; } else { return false; } if (stloc_outer == null) return false; if (stloc_inner == null) return false; if (ldloc == null) return false; if (!(stloc_outer.Variable.Kind == VariableKind.Local || stloc_outer.Variable.Kind == VariableKind.StackSlot)) return false; if (!IsMatchingCompoundLoad(ldloc, stloc_inner, out var target, out var targetKind, out var finalizeMatch)) return false; if (IsImplicitTruncation(stloc_outer.Value, stloc_outer.Variable.Type, context.TypeSystem)) return false; context.Step(nameof(TransformPreIncDecOperatorWithInlineStore), store); finalizeMatch?.Invoke(context); if (binary != null) { block.Instructions[pos] = new StLoc(stloc_outer.Variable, new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue)); } else { Call operatorCall = (Call)value2; block.Instructions[pos] = new StLoc(stloc_outer.Variable, new UserDefinedCompoundAssign( operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, new LdcI4(1))); } return true; } /// /// stobj(target, binary.add(stloc l(ldobj(target)), ldc.i4 1)) /// where target is pure and does not use 'l', and the 'stloc l' does not truncate /// --> /// stloc l(compound.op.old(ldobj(target), ldc.i4 1)) /// /// -or- /// /// call set_Prop(args..., binary.add(stloc l(call get_Prop(args...)), ldc.i4 1)) /// where args.. are pure and do not use 'l', and the 'stloc l' does not truncate /// --> /// stloc l(compound.op.old(call get_Prop(target), ldc.i4 1)) /// /// /// This pattern is used for post-increment by legacy csc. /// /// Even though this transform operates only on a single expression, it's not an expression transform /// as the result value of the expression changes (this is OK only for statements in a block). /// bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) { var store = block.Instructions[pos]; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) { return false; } StLoc stloc; var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction; if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) { if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; if (conv is not null) { var primitiveType = targetType.ToPrimitiveType(); if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign()) targetType = SwapSign(targetType, context.TypeSystem); } if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) return false; stloc = binary.Left as StLoc; } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) return false; if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations stloc = operatorCall.Arguments[0] as StLoc; } else { return false; } if (stloc == null) return false; if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) return false; if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: stloc.Variable)) return false; if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); finalizeMatch?.Invoke(context); if (binary != null) { block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); } else { Call operatorCall = (Call)value; block.Instructions[pos] = new StLoc(stloc.Variable, new UserDefinedCompoundAssign( operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1))); } return true; } /// /// stloc tmp(ldobj(target)) /// stobj(target, binary.op(ldloc tmp, ldc.i4 1)) /// target is pure and does not use 'tmp', 'stloc does not truncate' /// --> /// stloc tmp(compound.op.old(ldobj(target), ldc.i4 1)) /// /// This is usually followed by inlining or eliminating 'tmp'. /// /// Local variables use a similar pattern, also detected by this function: /// /// stloc tmp(ldloc target) /// stloc target(binary.op(ldloc tmp, ldc.i4 1)) /// --> /// stloc tmp(compound.op.old(ldloca target, ldc.i4 1)) /// /// /// This pattern occurs with legacy csc for static fields, and with Roslyn for most post-increments. /// bool TransformPostIncDecOperator(Block block, int i) { var inst = block.Instructions[i] as StLoc; var store = block.Instructions.ElementAtOrDefault(i + 1); if (inst == null || store == null) return false; var tmpVar = inst.Variable; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; var truncation = CheckImplicitTruncation(inst.Value, targetType, context.TypeSystem); if (truncation == ImplicitTruncationResult.ValueChanged) { // 'stloc tmp' is implicitly truncating the value return false; } if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch) { if (!(store is StObj stObj && stObj.Type.Equals(targetType))) { // We cannot apply the sign change, so we can't fix the truncation return false; } } if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: inst.Variable, previousInstruction: block.Instructions.ElementAtOrDefault(i - 1))) { return false; } if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; if (!binary.Left.MatchLdLoc(tmpVar)) return false; if (targetType is PointerType ptrType) { var right = PointerArithmeticOffset.Detect(binary.Right, ptrType.ElementType, binary.CheckForOverflow); if (right is null || !right.MatchLdcI(1)) return false; } else if (!(binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) return false; if (truncation == ImplicitTruncationResult.ValueChangedDueToSignMismatch && store is StObj stObj) { // Change the sign of the type to skip implicit truncation stObj.Type = targetType = SwapSign(targetType, context.TypeSystem); } if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); finalizeMatch?.Invoke(context); inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { if (!operatorCall.Arguments[0].MatchLdLoc(tmpVar)) return false; if (!UserDefinedCompoundAssign.IsIncrementOrDecrement(operatorCall.Method, context.Settings)) return false; if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); Debug.Assert(truncation == ImplicitTruncationResult.ValuePreserved); finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); } else { return false; } block.Instructions.RemoveAt(i + 1); if (inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0) { // dead store -> it was a statement-level post-increment inst.ReplaceWith(inst.Value); } return true; } static bool IsSameMember(IMember a, IMember b) { if (a == null || b == null) return false; a = a.MemberDefinition; b = b.MemberDefinition; return a.Equals(b); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs ================================================ // Copyright (c) 2017 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transforms collection and object initialization patterns. /// public class TransformCollectionAndObjectInitializers : IStatementTransform { void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.ObjectOrCollectionInitializers) return; ILInstruction inst = block.Instructions[pos]; // Match stloc(v, newobj) if (!inst.MatchStLoc(out var v, out var initInst) || v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) return; IType instType; var blockKind = BlockKind.CollectionInitializer; var insertionPos = initInst.ChildIndex; var siblings = initInst.Parent!.Children; IMethod currentMethod = context.Function.Method!; // we allow a castclass instruction to wrap the init instruction: // this is needed, for example, for inherited record types used on .NET runtimes (e.g., .NET 4.x), // where covariant return types are not supported. if (initInst.MatchCastClass(out var arg, out var targetType)) { initInst = arg; } switch (initInst) { case NewObj newObjInst: if (newObjInst.ILStackWasEmpty && v.Kind == VariableKind.Local && !TypeContainsInitOnlyProperties(newObjInst.Method.DeclaringTypeDefinition) && !currentMethod.IsConstructor && !currentMethod.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { // on statement level (no other expressions on IL stack), // prefer to keep local variables (but not stack slots), // unless we are in a constructor (where inlining object initializers might be critical // for the base ctor call) or a compiler-generated delegate method, which might be used in a query expression. return; } // Do not try to transform delegate construction. // DelegateConstruction transform cannot deal with this. if (DelegateConstruction.MatchDelegateConstruction(newObjInst, out _, out _, out _) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst)) return; // Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression // anon = new { A = 5 } { 3,4,5 } is invalid syntax. if (newObjInst.Method.DeclaringType.ContainsAnonymousType()) return; // Tuples cannot have initializers if (TupleTransform.MatchTupleConstruction(newObjInst, out _)) return; instType = newObjInst.Method.DeclaringType; break; case DefaultValue defaultVal: instType = defaultVal.Type; break; case Call c when c.Method.FullNameIs("System.Activator", "CreateInstance") && c.Method.TypeArguments.Count == 1: if (!context.Settings.UseObjectCreationOfGenericTypeParameter) { return; } instType = c.Method.TypeArguments[0]; blockKind = BlockKind.ObjectInitializer; break; case CallInstruction ci when context.Settings.WithExpressions && IsRecordCloneMethodCall(ci): instType = ci.Method.DeclaringType; blockKind = BlockKind.WithInitializer; initInst = ci.Arguments.Single(); break; default: var typeDef = v.Type.GetDefinition(); if (context.Settings.WithExpressions && typeDef?.IsReferenceType == false && typeDef.IsRecord) { instType = v.Type; blockKind = BlockKind.WithInitializer; break; } return; } if (targetType != null) { instType = targetType; } // Copy-propagate stack slot holding an 'ldloca' of the variable if (pos < block.Instructions.Count && block.Instructions[pos + 1] is StLoc { Variable: { Kind: VariableKind.StackSlot, IsSingleDefinition: true }, Value: LdLoca ldLoca } stLocStack && ldLoca.Variable == v) { CopyPropagation.Propagate(stLocStack, context); } int initializerItemsCount = 0; bool initializerContainsInitOnlyItems = false; possibleIndexVariables.Clear(); currentPath.Clear(); isCollection = false; pathStack.Clear(); pathStack.Push(new HashSet()); // Detect initializer type by scanning the following statements // each must be a callvirt with ldloc v as first argument // 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 while (pos + initializerItemsCount + 1 < block.Instructions.Count && IsPartOfInitializer(block.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind, ref initializerContainsInitOnlyItems, context)) { initializerItemsCount++; } // Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable // directly after the possible initializer. if (!initializerContainsInitOnlyItems && IsMethodCallOnVariable(block.Instructions[pos + initializerItemsCount + 1], v)) return; // Calculate the correct number of statements inside the initializer: // All index variables that were used in the initializer have Index set to -1. // We fetch the first unused variable from the list and remove all instructions after its // first usage (i.e. the init store) from the initializer. var index = possibleIndexVariables.Where(info => info.Value.Index > -1).Min(info => (int?)info.Value.Index); if (index != null) { initializerItemsCount = index.Value - pos - 1; } // The initializer would be empty, there's nothing to do here. if (initializerItemsCount <= 0) return; context.Step("CollectionOrObjectInitializer", inst); // Create a new block and final slot (initializer target variable) var initializerBlock = new Block(blockKind); ILVariable finalSlot = context.Function.RegisterVariable(VariableKind.InitializerTarget, instType); initializerBlock.FinalInstruction = new LdLoc(finalSlot); initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst)); // Move all instructions to the initializer block. for (int i = 1; i <= initializerItemsCount; i++) { switch (block.Instructions[i + pos]) { case CallInstruction call: if (!(call is CallVirt || call is Call)) continue; var newCall = call; var newTarget = newCall.Arguments[0]; foreach (var load in newTarget.Descendants.OfType()) if ((load is LdLoc || load is LdLoca) && load.Variable == v) load.Variable = finalSlot; initializerBlock.Instructions.Add(newCall); break; case StObj stObj: var newStObj = stObj; foreach (var load in newStObj.Target.Descendants.OfType()) if ((load is LdLoc || load is LdLoca) && load.Variable == v) load.Variable = finalSlot; initializerBlock.Instructions.Add(newStObj); break; case StLoc stLoc: var newStLoc = stLoc; initializerBlock.Instructions.Add(newStLoc); break; } } block.Instructions.RemoveRange(pos + 1, initializerItemsCount); siblings[insertionPos] = initializerBlock; ILInlining.InlineIfPossible(block, pos, context); } private static bool TypeContainsInitOnlyProperties(ITypeDefinition? typeDefinition) { foreach (var property in typeDefinition?.Properties ?? []) { if (property.Setter?.IsInitOnly ?? false) { return true; } } return false; } internal static bool IsRecordCloneMethodCall(CallInstruction ci) { if (ci.Method.DeclaringTypeDefinition?.IsRecord != true) return false; if (ci.Method.Name != "$") return false; if (ci.Arguments.Count != 1) return false; return true; } bool IsMethodCallOnVariable(ILInstruction inst, ILVariable variable) { if (inst.MatchLdLocRef(variable)) return true; if (inst is CallInstruction call && call.Arguments.Count > 0 && !call.Method.IsStatic) return IsMethodCallOnVariable(call.Arguments[0], variable); if (inst.MatchLdFld(out var target, out _) || inst.MatchStFld(out target, out _, out _) || inst.MatchLdFlda(out target, out _)) return IsMethodCallOnVariable(target, variable); return false; } readonly Dictionary possibleIndexVariables = new Dictionary(); readonly List currentPath = new List(); bool isCollection; readonly Stack> pathStack = new Stack>(); bool IsPartOfInitializer(InstructionCollection instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind, ref bool initializerContainsInitOnlyItems, StatementTransformContext context) { // Include any stores to local variables that are single-assigned and do not reference the initializer-variable // in the list of possible index variables. // Index variables are used to implement dictionary initializers. if (instructions[pos] is StLoc stloc && stloc.Variable.Kind == VariableKind.Local && stloc.Variable.IsSingleDefinition) { if (!context.Settings.DictionaryInitializers) return false; if (stloc.Value.Descendants.OfType().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca))) return false; possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value)); return true; } (var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, context.CSharpResolver, possibleIndexVariables); if (kind == AccessPathKind.Invalid || target != targetVariable) return false; // Treat last element separately: // Can either be an Add method call or property setter. var lastElement = newPath.Last(); newPath.RemoveLast(); // Compare new path with current path: int minLen = Math.Min(currentPath.Count, newPath.Count); int firstDifferenceIndex = 0; while (firstDifferenceIndex < minLen && newPath[firstDifferenceIndex] == currentPath[firstDifferenceIndex]) firstDifferenceIndex++; while (currentPath.Count > firstDifferenceIndex) { isCollection = false; currentPath.RemoveAt(currentPath.Count - 1); pathStack.Pop(); } while (currentPath.Count < newPath.Count) { AccessPathElement newElement = newPath[currentPath.Count]; currentPath.Add(newElement); if (isCollection || !pathStack.Peek().Add(newElement)) return false; pathStack.Push(new HashSet()); } switch (kind) { case AccessPathKind.Adder: isCollection = true; if (pathStack.Peek().Count != 0) return false; return true; case AccessPathKind.Setter: if (isCollection || !pathStack.Peek().Add(lastElement)) return false; if (values?.Count != 1 || !IsValidObjectInitializerTarget(currentPath)) return false; if (blockKind != BlockKind.ObjectInitializer && blockKind != BlockKind.WithInitializer) blockKind = BlockKind.ObjectInitializer; initializerContainsInitOnlyItems |= lastElement.Member is IProperty { Setter.IsInitOnly: true }; return true; default: return false; } } bool IsValidObjectInitializerTarget(List path) { if (path.Count == 0) return true; var element = path.Last(); var previous = path.SkipLast(1).LastOrDefault(); if (element.Member is not IProperty p) return true; if (!p.IsIndexer) return true; if (previous != default) { return NormalizeTypeVisitor.IgnoreNullabilityAndTuples .EquivalentTypes(previous.Member.ReturnType, element.Member.DeclaringType); } return false; } } public enum AccessPathKind { Invalid, Setter, Adder } public struct AccessPathElement : IEquatable { public AccessPathElement(OpCode opCode, IMember member, ILInstruction[]? indices = null) { this.OpCode = opCode; this.Member = member; this.Indices = indices; } public readonly OpCode OpCode; public readonly IMember Member; public readonly ILInstruction[]? Indices; public override string ToString() => $"[{Member}, {Indices}]"; public static (AccessPathKind Kind, List Path, List? Values, ILVariable? Target) GetAccessPath( ILInstruction instruction, IType rootType, DecompilerSettings? settings = null, CSharpResolver? resolver = null, Dictionary? possibleIndexVariables = null) { List path = new List(); ILVariable? target = null; AccessPathKind kind = AccessPathKind.Invalid; List? values = null; IMethod method; ILInstruction? inst = instruction; while (inst != null) { switch (inst) { case CallInstruction call: if (!(call is CallVirt || call is Call)) goto default; method = call.Method; if (resolver != null && !IsMethodApplicable(method, call.Arguments, rootType, resolver, settings)) goto default; inst = call.Arguments[0]; if (inst is LdObjIfRef ldObjIfRef) { inst = ldObjIfRef.Target; } if (method.IsAccessor) { if (method.AccessorOwner is IProperty property && !CanBeUsedInInitializer(property, resolver, kind)) { goto default; } var isGetter = method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter; var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray(); if (indices.Length > 0 && settings?.DictionaryInitializers == false) goto default; if (possibleIndexVariables != null) { // Mark all index variables as used foreach (var index in indices.OfType()) { if (possibleIndexVariables.TryGetValue(index.Variable, out var info)) possibleIndexVariables[index.Variable] = (-1, info.Value); } } path.Insert(0, new AccessPathElement(call.OpCode, method.AccessorOwner, indices)); } else { path.Insert(0, new AccessPathElement(call.OpCode, method)); } if (values == null) { if (method.IsAccessor) { kind = AccessPathKind.Setter; values = new List { call.Arguments.Last() }; } else { kind = AccessPathKind.Adder; values = new List(call.Arguments.Skip(1)); if (values.Count == 0) goto default; } } break; case LdObj ldobj: { if (ldobj.Target is LdFlda ldflda && (kind != AccessPathKind.Setter || !ldflda.Field.IsReadOnly)) { path.Insert(0, new AccessPathElement(ldobj.OpCode, ldflda.Field)); inst = ldflda.Target; break; } goto default; } case LdObjIfRef ldobj: { if (ldobj.Target is LdFlda ldflda && (kind != AccessPathKind.Setter || !ldflda.Field.IsReadOnly)) { path.Insert(0, new AccessPathElement(ldobj.OpCode, ldflda.Field)); inst = ldflda.Target; break; } if (ldobj.Target is LdLoca ldloca) { target = ldloca.Variable; inst = null; break; } goto default; } case StObj stobj: { if (stobj.Target is LdFlda ldflda) { path.Insert(0, new AccessPathElement(stobj.OpCode, ldflda.Field)); inst = ldflda.Target; if (values == null) { values = new List(new[] { stobj.Value }); kind = AccessPathKind.Setter; } break; } goto default; } case LdLoc ldloc: target = ldloc.Variable; inst = null; break; case LdLoca ldloca: target = ldloca.Variable; inst = null; break; case LdFlda ldflda: path.Insert(0, new AccessPathElement(ldflda.OpCode, ldflda.Field)); inst = ldflda.Target; break; default: kind = AccessPathKind.Invalid; inst = null; break; } } if (kind != AccessPathKind.Invalid && values != null && values.SelectMany(v => v.Descendants).OfType().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca))) kind = AccessPathKind.Invalid; return (kind, path, values, target); } private static bool CanBeUsedInInitializer(IProperty property, ITypeResolveContext? resolveContext, AccessPathKind kind) { if (property.CanSet && (property.Accessibility == property.Setter.Accessibility || IsAccessorAccessible(property.Setter, resolveContext))) return true; return kind != AccessPathKind.Setter; } private static bool IsAccessorAccessible(IMethod setter, ITypeResolveContext? resolveContext) { if (resolveContext == null) return true; var lookup = new MemberLookup(resolveContext.CurrentTypeDefinition, resolveContext.CurrentModule); return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition); } static bool IsMethodApplicable(IMethod method, IReadOnlyList arguments, IType rootType, CSharpResolver resolver, DecompilerSettings? settings) { if (method.IsStatic && !method.IsExtensionMethod) return false; if (method.AccessorOwner is IProperty) return true; if (!"Add".Equals(method.Name, StringComparison.Ordinal) || arguments.Count == 0) return false; if (method.IsExtensionMethod) { if (settings?.ExtensionMethodsInCollectionInitializers == false) return false; if (!resolver.CanTransformToExtensionMethodCall(method, ignoreTypeArguments: true)) return false; } var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType; if (targetType == null) return false; if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT))) return false; return CanInferTypeArgumentsFromParameters(method); bool CanInferTypeArgumentsFromParameters(IMethod method) { if (method.TypeParameters.Count == 0) return true; // always use unspecialized member, otherwise type inference fails method = (IMethod)method.MemberDefinition; new TypeInference(resolver.Compilation) .InferTypeArguments( method.TypeParameters, // TODO : this is not entirely correct... we need argument type information to resolve Add methods properly method.Parameters.SelectReadOnlyArray(p => new ResolveResult(p.Type)), method.Parameters.SelectReadOnlyArray(p => p.Type), out bool success ); return success; } } static IType? GetReturnTypeFromInstruction(ILInstruction instruction) { switch (instruction) { case CallInstruction call: if (!(call is CallVirt || call is Call)) goto default; return call.Method.ReturnType; case LdObj ldobj: if (ldobj.Target is LdFlda ldflda) return ldflda.Field.ReturnType; goto default; case StObj stobj: if (stobj.Target is LdFlda ldflda2) return ldflda2.Field.ReturnType; goto default; default: return null; } } public override bool Equals(object? obj) { if (obj is AccessPathElement) return Equals((AccessPathElement)obj); return false; } public override int GetHashCode() { int hashCode = 0; unchecked { if (Member != null) hashCode += 1000000007 * Member.GetHashCode(); } return hashCode; } public bool Equals(AccessPathElement other) { return (other.Member == this.Member || this.Member.Equals(other.Member)) && (other.Indices == this.Indices || (other.Indices != null && this.Indices != null && this.Indices.SequenceEqual(other.Indices, ILInstructionMatchComparer.Instance))); } public static bool operator ==(AccessPathElement lhs, AccessPathElement rhs) { return lhs.Equals(rhs); } public static bool operator !=(AccessPathElement lhs, AccessPathElement rhs) { return !(lhs == rhs); } } class ILInstructionMatchComparer : IEqualityComparer { public static readonly ILInstructionMatchComparer Instance = new ILInstructionMatchComparer(); public bool Equals(ILInstruction? x, ILInstruction? y) { if (x == y) return true; if (x == null || y == null) return false; return SemanticHelper.IsPure(x.Flags) && SemanticHelper.IsPure(y.Flags) && x.Match(y).Success; } public int GetHashCode(ILInstruction obj) { return obj.GetHashCode(); } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs ================================================ // 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 // 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.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transforms closure fields to local variables. /// /// This is a post-processing step of , /// and . /// /// In general we can perform SROA (scalar replacement of aggregates) on any variable that /// satisfies the following conditions: /// 1) It is initialized by an empty/default constructor call. /// 2) The variable is never passed to another method. /// 3) The variable is never the target of an invocation. /// /// Note that 2) and 3) apply because declarations and uses of lambdas and local functions /// are already transformed by the time this transform is applied. /// public class TransformDisplayClassUsage : ILVisitor, IILTransform { class VariableToDeclare { private readonly DisplayClass container; private readonly IField field; private ILVariable declaredVariable; public string Name => field.Name; public bool CanPropagate { get; private set; } public bool UsesInitialValue { get; set; } public HashSet Initializers { get; } = new HashSet(); public VariableToDeclare(DisplayClass container, IField field, ILVariable declaredVariable = null) { this.container = container; this.field = field; this.declaredVariable = declaredVariable; Debug.Assert(declaredVariable == null || declaredVariable.StateMachineField == field); } public void Propagate(ILVariable variable) { this.declaredVariable = variable; this.CanPropagate = variable != null; } public ILVariable GetOrDeclare() { if (declaredVariable != null) return declaredVariable; declaredVariable = container.Variable.Function.RegisterVariable(VariableKind.Local, field.Type, field.Name); declaredVariable.InitialValueIsInitialized = true; declaredVariable.UsesInitialValue = UsesInitialValue; declaredVariable.CaptureScope = container.CaptureScope; return declaredVariable; } } [DebuggerDisplay("[DisplayClass {Variable} : {Type}]")] class DisplayClass { public readonly ILVariable Variable; public readonly ITypeDefinition Type; public readonly Dictionary VariablesToDeclare; public BlockContainer CaptureScope; public ILInstruction Initializer; public DisplayClass(ILVariable variable, ITypeDefinition type) { Variable = variable; Type = type; VariablesToDeclare = new Dictionary(); } } ILTransformContext context; ITypeResolveContext decompilationContext; readonly Dictionary displayClasses = new Dictionary(); readonly Dictionary displayClassCopyMap = new Dictionary(); void IILTransform.Run(ILFunction function, ILTransformContext context) { if (this.context != null) throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); try { this.context = context; this.decompilationContext = new SimpleTypeResolveContext(context.Function.Method); AnalyzeFunction(function); Transform(function); } finally { ClearState(); } } void ClearState() { displayClasses.Clear(); displayClassCopyMap.Clear(); this.decompilationContext = null; this.context = null; } void AnalyzeFunction(ILFunction function) { void VisitFunction(ILFunction f) { foreach (var v in f.Variables.ToArray()) { var result = AnalyzeVariable(v); if (result == null || displayClasses.ContainsKey(result.Variable)) continue; context.Step($"Detected display-class {result.Variable}", result.Initializer ?? f.Body); displayClasses.Add(result.Variable, result); } } void VisitChildren(ILInstruction inst) { foreach (var child in inst.Children) { Visit(child); } } void Visit(ILInstruction inst) { switch (inst) { case ILFunction f: VisitFunction(f); VisitChildren(inst); break; default: VisitChildren(inst); break; } } Visit(function); foreach (var (v, displayClass) in displayClasses.ToArray()) { if (!ValidateDisplayClassUses(v, displayClass)) displayClasses.Remove(v); } foreach (var displayClass in displayClasses.Values) { // handle uninitialized fields foreach (var f in displayClass.Type.Fields) { if (displayClass.VariablesToDeclare.ContainsKey(f)) continue; var variable = AddVariable(displayClass, null, f); variable.UsesInitialValue = true; displayClass.VariablesToDeclare[(IField)f.MemberDefinition] = variable; } foreach (var v in displayClass.VariablesToDeclare.Values) { if (v.CanPropagate) { var variableToPropagate = v.GetOrDeclare(); if (variableToPropagate.Kind != VariableKind.Parameter && !displayClasses.ContainsKey(variableToPropagate)) v.Propagate(null); } } } } bool ValidateDisplayClassUses(ILVariable v, DisplayClass displayClass) { foreach (var ldloc in v.LoadInstructions) { if (!ValidateUse(displayClass, ldloc)) return false; } foreach (var ldloca in v.AddressInstructions) { if (!ValidateUse(displayClass, ldloca)) return false; } return true; bool ValidateUse(DisplayClass container, ILInstruction use) { IField field; switch (use.Parent) { case LdFlda ldflda when ldflda.MatchLdFlda(out var target, out field) && target == use: var keyField = (IField)field.MemberDefinition; if (!container.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable) || variable == null) { variable = AddVariable(container, null, field); } container.VariablesToDeclare[keyField] = variable; return true; case StObj stobj when stobj.MatchStObj(out var target, out ILInstruction value, out _) && value == use: if (target.MatchLdFlda(out var load, out field) && load.MatchLdLocRef(out var otherVariable) && displayClasses.TryGetValue(otherVariable, out var otherDisplayClass)) { if (otherDisplayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out var declaredVar)) return declaredVar.CanPropagate; } return false; case StLoc stloc when stloc.Variable.IsSingleDefinition && stloc.Value == use: displayClassCopyMap[stloc.Variable] = v; return ValidateDisplayClassUses(stloc.Variable, displayClass); default: return false; } } } private DisplayClass AnalyzeVariable(ILVariable v) { switch (v.Kind) { case VariableKind.Parameter: if (context.Settings.YieldReturn && v.Function.StateMachineCompiledWithMono && v.IsThis()) return HandleMonoStateMachine(v.Function, v); return null; case VariableKind.StackSlot: case VariableKind.Local: case VariableKind.DisplayClassLocal: return DetectDisplayClass(v); case VariableKind.InitializerTarget: return DetectDisplayClassInitializer(v); default: return null; } } DisplayClass DetectDisplayClass(ILVariable v) { ITypeDefinition definition; if (v.Kind != VariableKind.StackSlot) { definition = v.Type.GetDefinition(); } else if (v.StoreInstructions.Count > 0 && v.StoreInstructions[0] is StLoc stloc) { definition = stloc.Value.InferType(context.TypeSystem).GetDefinition(); } else { definition = null; } if (!ValidateDisplayClassDefinition(definition)) return null; DisplayClass result; switch (definition.Kind) { case TypeKind.Class: if (!v.IsSingleDefinition) return null; if (!(v.StoreInstructions.SingleOrDefault() is StLoc stloc)) return null; if (stloc.Value is NewObj newObj && ValidateConstructor(context, newObj.Method)) { result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope, Initializer = stloc }; } else { return null; } HandleInitBlock(stloc.Parent as Block, stloc.ChildIndex + 1, result, result.Variable); break; case TypeKind.Struct: if (v.StoreInstructions.Count != 0) return null; Debug.Assert(v.StoreInstructions.Count == 0); result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope }; HandleInitBlock(FindDisplayStructInitBlock(v), 0, result, result.Variable); break; default: return null; } if (IsMonoNestedCaptureScope(definition)) { result.CaptureScope = null; } return result; } void HandleInitBlock(Block initBlock, int startIndex, DisplayClass result, ILVariable targetVariable) { if (initBlock == null) return; for (int i = startIndex; i < initBlock.Instructions.Count; i++) { var init = initBlock.Instructions[i]; if (!init.MatchStFld(out var target, out var field, out _)) break; if (!target.MatchLdLocRef(targetVariable)) break; if (result.VariablesToDeclare.ContainsKey((IField)field.MemberDefinition)) break; var variable = AddVariable(result, (StObj)init, field); result.VariablesToDeclare[(IField)field.MemberDefinition] = variable; } } private Block FindDisplayStructInitBlock(ILVariable v) { var root = v.Function.Body; return Visit(root)?.Ancestors.OfType().FirstOrDefault(); // Try to find a common ancestor of all uses of the variable v. ILInstruction Visit(ILInstruction inst) { switch (inst) { case LdLoc l when l.Variable == v: return l; case StLoc s when s.Variable == v: return s; case LdLoca la when la.Variable == v: return la; default: return VisitChildren(inst); } } ILInstruction VisitChildren(ILInstruction inst) { // Visit all children of the instruction ILInstruction result = null; foreach (var child in inst.Children) { var newResult = Visit(child); // As soon as there is a second use of v in this sub-tree, // we can skip all other children and just return this node. if (result == null) { result = newResult; } else if (newResult != null) { return inst; } } // returns null, if v is not used in this sub-tree; // returns a descendant of inst, if it is the only use of v in this sub-tree. return result; } } DisplayClass DetectDisplayClassInitializer(ILVariable v) { if (v.StoreInstructions.Count != 1 || !(v.StoreInstructions[0] is StLoc store && store.Parent is Block initializerBlock && initializerBlock.Kind == BlockKind.ObjectInitializer)) return null; if (!(store.Value is NewObj newObj)) return null; var definition = newObj.Method.DeclaringType.GetDefinition(); if (!ValidateDisplayClassDefinition(definition)) return null; if (!ValidateConstructor(context, newObj.Method)) return null; if (!initializerBlock.Parent.MatchStLoc(out var referenceVariable)) return null; if (!referenceVariable.IsSingleDefinition) return null; if (!(referenceVariable.StoreInstructions.SingleOrDefault() is StLoc)) return null; var result = new DisplayClass(referenceVariable, definition) { CaptureScope = referenceVariable.CaptureScope, Initializer = initializerBlock.Parent }; HandleInitBlock(initializerBlock, 1, result, v); return result; } private bool ValidateDisplayClassDefinition(ITypeDefinition definition) { if (definition == null) return false; if (definition.ParentModule.MetadataFile != context.PEFile) return false; // We do not want to accidentially transform state-machines and thus destroy them. var token = (TypeDefinitionHandle)definition.MetadataToken; var metadata = definition.ParentModule.MetadataFile.Metadata; if (YieldReturnDecompiler.IsCompilerGeneratorEnumerator(token, metadata)) return false; if (AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(token, metadata)) return false; if (!context.Settings.AggressiveScalarReplacementOfAggregates) { if (definition.DeclaringTypeDefinition == null) return false; if (!IsPotentialClosure(context, definition)) return false; } return true; } internal static bool ValidateConstructor(ILTransformContext context, IMethod method) { try { if (method.Parameters.Count != 0) return false; var handle = (MethodDefinitionHandle)method.MetadataToken; var module = (MetadataModule)method.ParentModule; var file = module.MetadataFile; if (handle.IsNil || file != context.PEFile) return false; var def = file.Metadata.GetMethodDefinition(handle); if (def.RelativeVirtualAddress == 0) return false; var body = file.GetMethodBody(def.RelativeVirtualAddress); // some compilers produce ctors with unused local variables // see https://github.com/icsharpcode/ILSpy/issues/2174 //if (!body.LocalSignature.IsNil) // return false; if (body.ExceptionRegions.Length != 0) return false; var reader = body.GetILReader(); if (reader.Length < 7) return false; // IL_0000: ldarg.0 // IL_0001: call instance void [mscorlib]System.Object::.ctor() // IL_0006: ret var opCode = DecodeOpCodeSkipNop(ref reader); switch (opCode) { case ILOpCode.Ldarg: case ILOpCode.Ldarg_s: if (reader.DecodeIndex(opCode) != 0) return false; break; case ILOpCode.Ldarg_0: // OK break; default: return false; } if (DecodeOpCodeSkipNop(ref reader) != ILOpCode.Call) return false; var baseCtorHandle = MetadataTokenHelpers.EntityHandleOrNil(reader.ReadInt32()); if (baseCtorHandle.IsNil) return false; var objectCtor = module.ResolveMethod(baseCtorHandle, new TypeSystem.GenericContext()); if (!objectCtor.DeclaringType.IsKnownType(KnownTypeCode.Object)) return false; if (!objectCtor.IsConstructor || objectCtor.Parameters.Count != 0) return false; return DecodeOpCodeSkipNop(ref reader) == ILOpCode.Ret; } catch (BadImageFormatException) { return false; } } static ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader) { ILOpCode code; do { code = reader.DecodeOpCode(); } while (code == ILOpCode.Nop); return code; } VariableToDeclare AddVariable(DisplayClass result, StObj statement, IField field) { VariableToDeclare variable = new VariableToDeclare(result, field); if (statement != null) { variable.Propagate(ResolveVariableToPropagate(statement.Value, field.Type)); variable.Initializers.Add(statement); } variable.UsesInitialValue = result.Type.IsReferenceType != false || result.Variable.UsesInitialValue; return variable; } /// /// Resolves references to variables that can be propagated. /// If a value does not match either LdLoc or a LdObj LdLdFlda* LdLoc chain, null is returned. /// The if any of the variables/fields in the chain cannot be propagated, null is returned. /// ILVariable ResolveVariableToPropagate(ILInstruction value, IType expectedType = null) { ILVariable v; switch (value) { case LdLoc load: v = load.Variable; if (v.Kind == VariableKind.Parameter) { if (v.LoadCount != 1 && !v.IsThis()) { // If the variable is a parameter and it is used elsewhere, we cannot propagate it. // "dc.field = v; dc.field.mutate(); use(v);" cannot turn to "v.mutate(); use(v)" return null; } } else { // Non-parameter propagation will later be checked, and will only be allowed for display classes if (v.Type.IsReferenceType != true) { // don't allow propagation for display structs (as used with local functions) return null; } } if (!v.IsSingleDefinition) { // "dc.field = v; v = 42; use(dc.field)" cannot turn to "v = 42; use(v);" return null; } if (!(expectedType == null || v.Kind == VariableKind.StackSlot || NormalizeTypeVisitor.IgnoreNullability.EquivalentTypes(v.Type, expectedType))) return null; return v; case LdObj ldfld: DisplayClass currentDisplayClass = null; foreach (var item in ldfld.Target.Descendants) { if (IsDisplayClassLoad(item, out v)) { if (!displayClasses.TryGetValue(v, out currentDisplayClass)) return null; } if (currentDisplayClass == null) return null; if (item is LdFlda ldf && currentDisplayClass.VariablesToDeclare.TryGetValue((IField)ldf.Field.MemberDefinition, out var vd)) { if (!vd.CanPropagate) return null; if (!displayClasses.TryGetValue(vd.GetOrDeclare(), out currentDisplayClass)) return null; } } return currentDisplayClass.Variable; default: return null; } } private void Transform(ILFunction function) { VisitILFunction(function); context.Step($"ResetHasInitialValueFlag", function); foreach (var f in function.Descendants.OfType()) { RemoveDeadVariableInit.ResetUsesInitialValueFlag(f, context); f.CapturedVariables.RemoveWhere(v => v.IsDead); } } internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) { closureType = null; initializer = null; if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) { initializer = inst; if (IsClosureInit(context, inst, out closureType)) { return true; } } closureType = variable.Type.GetDefinition(); if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.UsesInitialValue && IsPotentialClosure(context, closureType)) { initializer = Block.GetContainingStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); return true; } return false; } static bool IsClosureInit(ILTransformContext context, StLoc inst, out ITypeDefinition closureType) { if (inst.Value is NewObj newObj) { closureType = newObj.Method.DeclaringTypeDefinition; return closureType != null && IsPotentialClosure(context, newObj); } closureType = null; return false; } bool IsMonoNestedCaptureScope(ITypeDefinition closureType) { if (!closureType.Name.Contains("AnonStorey")) return false; var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition())); } /// /// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type, /// We construct a that spans the whole method body. /// DisplayClass HandleMonoStateMachine(ILFunction function, ILVariable thisVariable) { if (!(function.StateMachineCompiledWithMono && thisVariable.IsThis())) return null; // Special case for Mono-compiled yield state machines ITypeDefinition closureType = thisVariable.Type.GetDefinition(); if (!(closureType != decompilationContext.CurrentTypeDefinition && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true))) return null; var displayClass = new DisplayClass(thisVariable, thisVariable.Type.GetDefinition()); displayClass.CaptureScope = (BlockContainer)function.Body; foreach (var stateMachineVariable in function.Variables) { if (stateMachineVariable.StateMachineField == null || displayClass.VariablesToDeclare.ContainsKey(stateMachineVariable.StateMachineField)) continue; VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, stateMachineVariable.StateMachineField, stateMachineVariable); displayClass.VariablesToDeclare.Add(stateMachineVariable.StateMachineField, variableToDeclare); } if (!function.Method.IsStatic && FindThisField(out var thisField)) { var thisVar = function.Variables .FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition); if (thisVar == null) { thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this", StateMachineField = thisField }; function.Variables.Add(thisVar); } else if (thisVar.StateMachineField != null && displayClass.VariablesToDeclare.ContainsKey(thisVar.StateMachineField)) { // "this" was already added previously, no need to add it twice. return displayClass; } VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, thisField, thisVar); displayClass.VariablesToDeclare.Add(thisField, variableToDeclare); } return displayClass; bool FindThisField(out IField foundField) { foundField = null; foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.VariablesToDeclare.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) { thisField = field; return true; } return false; } } internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); } internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass) { var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass); } internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass, bool allowTypeImplementingInterfaces = false) { if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return false; switch (potentialDisplayClass.Kind) { case TypeKind.Struct: break; case TypeKind.Class: if (!allowTypeImplementingInterfaces) { if (!potentialDisplayClass.DirectBaseTypes.All(t => t.IsKnownType(KnownTypeCode.Object))) return false; } break; default: return false; } // Make sure that potentialDisplayCLass and decompiledTypeDefinition are part of the same type tree // Either decompiledTypeDefinition is an ancestor type of potentialDisplayClass or both have // at least one common ancestor. var potentialDisplayClassAncestors = new HashSet(); var potentialDisplayClassParent = potentialDisplayClass.DeclaringTypeDefinition; while (potentialDisplayClassParent != null) { potentialDisplayClassAncestors.Add(potentialDisplayClassParent); potentialDisplayClassParent = potentialDisplayClassParent.DeclaringTypeDefinition; } var decompiledTypeDefinitionOrAncestor = decompiledTypeDefinition; while (decompiledTypeDefinitionOrAncestor != null) { if (potentialDisplayClassAncestors.Contains(decompiledTypeDefinitionOrAncestor)) return true; decompiledTypeDefinitionOrAncestor = decompiledTypeDefinitionOrAncestor.DeclaringTypeDefinition; } return false; } readonly Stack currentFunctions = new Stack(); protected internal override void VisitILFunction(ILFunction function) { context.StepStartGroup("Visit " + function.Name); try { this.currentFunctions.Push(function); base.VisitILFunction(function); } finally { this.currentFunctions.Pop(); context.StepEndGroup(keepIfEmpty: true); } } protected override void Default(ILInstruction inst) { ILInstruction next; for (var child = inst.Children.FirstOrDefault(); child != null; child = next) { next = child.GetNextSibling(); child.AcceptVisitor(this); } } protected internal override void VisitStLoc(StLoc inst) { DisplayClass displayClass; if (inst.Parent is Block parentBlock && inst.Variable.IsSingleDefinition) { if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0) { // traverse pre-order, so that we do not have to deal with more special cases afterwards base.VisitStLoc(inst); if (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction) { context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); inst.ReplaceWith(inst.Value); } return; } if (displayClasses.TryGetValue(inst.Variable, out displayClass) && displayClass.Initializer == inst) { // inline contents of object initializer block if (inst.Value is Block initBlock && initBlock.Kind == BlockKind.ObjectInitializer) { context.Step($"Remove initializer of {inst.Variable.Name}", inst); for (int i = 1; i < initBlock.Instructions.Count; i++) { var stobj = (StObj)initBlock.Instructions[i]; var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition]; parentBlock.Instructions.Insert(inst.ChildIndex + i, new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj)); } } context.Step($"Remove initializer of {inst.Variable.Name}", inst); parentBlock.Instructions.Remove(inst); return; } if (inst.Value is LdLoc || inst.Value is LdObj) { // in some cases (e.g. if inlining fails), there can be a reference to a display class in a stack slot, // in that case it is necessary to resolve the reference and iff it can be propagated, replace all loads // of the single-definition. if (!displayClassCopyMap.TryGetValue(inst.Variable, out var referencedDisplayClass)) { referencedDisplayClass = ResolveVariableToPropagate(inst.Value); } if (referencedDisplayClass != null && displayClasses.TryGetValue(referencedDisplayClass, out _)) { context.Step($"Propagate reference to {referencedDisplayClass.Name} in {inst.Variable}", inst); foreach (var ld in inst.Variable.LoadInstructions.ToArray()) { ld.ReplaceWith(new LdLoc(referencedDisplayClass).WithILRange(ld)); } parentBlock.Instructions.Remove(inst); return; } } } base.VisitStLoc(inst); } protected internal override void VisitStObj(StObj inst) { if (IsDisplayClassFieldAccess(inst.Target, out var v, out var displayClass, out var field)) { VariableToDeclare vd = displayClass.VariablesToDeclare[(IField)field.MemberDefinition]; if (vd.CanPropagate && vd.Initializers.Contains(inst)) { if (inst.Parent is Block containingBlock) { context.Step($"Remove initializer of {v.Name}.{vd.Name} due to propagation", inst); containingBlock.Instructions.Remove(inst); return; } } if (inst.Value is LdLoc ldLoc && ldLoc.Variable is { IsSingleDefinition: true, CaptureScope: null } && ldLoc.Variable.StoreInstructions.FirstOrDefault() is StLoc stloc && stloc.Parent is Block block) { ILInlining.InlineOneIfPossible(block, stloc.ChildIndex, InliningOptions.None, context); } } base.VisitStObj(inst); EarlyExpressionTransforms.StObjToStLoc(inst, context); } protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); EarlyExpressionTransforms.LdObjToLdLoc(inst, context); } private bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable) { // We cannot use MatchLdLocRef here because local functions use ref parameters if (!target.MatchLdLoc(out variable) && !target.MatchLdLoca(out variable)) return false; if (displayClassCopyMap.TryGetValue(variable, out ILVariable other)) variable = other; return true; } private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field) { displayClass = null; displayClassVar = null; field = null; if (inst is not LdFlda ldflda) return false; field = ldflda.Field; return IsDisplayClassLoad(ldflda.Target, out displayClassVar) && displayClasses.TryGetValue(displayClassVar, out displayClass); } protected internal override void VisitLdFlda(LdFlda inst) { base.VisitLdFlda(inst); // Get display class info if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field)) return; var keyField = (IField)field.MemberDefinition; var v = displayClass.VariablesToDeclare[keyField]; context.Step($"Replace {field.Name} with captured variable {v.Name}", inst); ILVariable variable = v.GetOrDeclare(); inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); // add captured variable to all descendant functions from the declaring function to this use-site function foreach (var f in currentFunctions) { if (f == variable.Function) break; f.CapturedVariables.Add(variable); } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs ================================================ // Copyright (c) 2017 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 ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Converts LINQ Expression Trees to ILFunctions/ILAst instructions. /// /// We build a tree of Func{ILInstruction}s, which are only executed, if the whole transform succeeds. /// public class TransformExpressionTrees : IStatementTransform { /// /// Returns true if the instruction matches the pattern for Expression.Lambda calls. /// static bool MightBeExpressionTree(ILInstruction inst, ILInstruction stmt) { if (!(inst is CallInstruction call && call.Method.FullNameIs("System.Linq.Expressions.Expression", "Lambda") && call.Arguments.Count == 2)) return false; if (!(IsEmptyParameterList(call.Arguments[1]) || (call.Arguments[1] is Block block && block.Kind == BlockKind.ArrayInitializer))) return false; //if (!ILInlining.CanUninline(call, stmt)) // return false; return true; } static bool IsEmptyParameterList(ILInstruction inst) { if (inst is CallInstruction emptyCall && emptyCall.Method.FullNameIs("System.Array", "Empty") && emptyCall.Arguments.Count == 0) return true; if (inst.MatchNewArr(out var type) && type.FullName == "System.Linq.Expressions.ParameterExpression") return true; if (inst.MatchNewArr(out type) && type.FullName == "System.Linq.Expressions.Expression") return true; return false; } bool MatchParameterVariableAssignment(ILInstruction expr, out ILVariable parameterReferenceVar, out IType type, out string name) { // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) type = null; name = null; if (!expr.MatchStLoc(out parameterReferenceVar, out var init)) return false; if (!parameterReferenceVar.IsSingleDefinition) return false; if (!(parameterReferenceVar.Kind == VariableKind.Local || parameterReferenceVar.Kind == VariableKind.StackSlot)) return false; if (parameterReferenceVar.Type == null || parameterReferenceVar.Type.FullName != "System.Linq.Expressions.ParameterExpression") return false; if (!(init is CallInstruction initCall && initCall.Arguments.Count == 2)) return false; if (!(initCall.Method.FullNameIs("System.Linq.Expressions.Expression", "Parameter"))) return false; CallInstruction typeArg = initCall.Arguments[0] as CallInstruction; if (typeArg == null || typeArg.Arguments.Count != 1) return false; if (!typeArg.Method.FullNameIs("System.Type", "GetTypeFromHandle")) return false; return typeArg.Arguments[0].MatchLdTypeToken(out type) && initCall.Arguments[1].MatchLdStr(out name); } StatementTransformContext context; Dictionary parameters; Dictionary parameterMapping; List instructionsToRemove; Stack lambdaStack; CSharpConversions conversions; CSharpResolver resolver; public void Run(Block block, int pos, StatementTransformContext context) { if (!context.Settings.ExpressionTrees) return; this.context = context; this.conversions = CSharpConversions.Get(context.TypeSystem); this.resolver = new CSharpResolver(context.TypeSystem); this.parameters = new Dictionary(); this.parameterMapping = new Dictionary(); this.instructionsToRemove = new List(); this.lambdaStack = new Stack(); for (int i = pos; i < block.Instructions.Count; i++) { if (MatchParameterVariableAssignment(block.Instructions[i], out var v, out var type, out var name)) { parameters.Add(v, (type, name)); continue; } if (TryConvertExpressionTree(block.Instructions[i], block.Instructions[i])) { foreach (var inst in instructionsToRemove) block.Instructions.Remove(inst); instructionsToRemove.Clear(); } break; } } bool TryConvertExpressionTree(ILInstruction instruction, ILInstruction statement) { if (MightBeExpressionTree(instruction, statement)) { var (lambda, type) = ConvertLambda((CallInstruction)instruction); if (lambda != null) { context.Step("Convert Expression Tree", instruction); var newLambda = (ILFunction)lambda(); SetExpressionTreeFlag(newLambda, (CallInstruction)instruction); instruction.ReplaceWith(newLambda); return true; } return false; } if (instruction is Block block && block.Kind == BlockKind.ControlFlow) return false; // don't look into nested blocks foreach (var child in instruction.Children) { if (TryConvertExpressionTree(child, statement)) return true; } return false; } /// /// Converts a Expression.Lambda call into an ILFunction. /// If the conversion fails, null is returned. /// (Func, IType) ConvertLambda(CallInstruction instruction) { if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1) return (null, SpecialType.UnknownType); var parameterList = new List(); var parameterVariablesList = new List(); if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method))) return (null, SpecialType.UnknownType); var container = new BlockContainer(); container.AddILRange(instruction); var functionType = instruction.Method.ReturnType.TypeArguments[0]; var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType ?? SpecialType.UnknownType; var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container, ILFunctionKind.ExpressionTree); function.DelegateType = functionType; function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; function.Variables.AddRange(parameterVariablesList); function.AddILRange(instruction); lambdaStack.Push(function); var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]); lambdaStack.Pop(); if (bodyInstruction == null) return (null, SpecialType.UnknownType); return (BuildFunction, function.DelegateType); ILFunction BuildFunction() { lambdaStack.Push(function); var convertedBody = bodyInstruction(); lambdaStack.Pop(); container.ExpectedResultType = convertedBody.ResultType; container.Blocks.Add(new Block() { Instructions = { new Leave(container, convertedBody) } }); // Replace all other usages of the parameter variable foreach (var mapping in parameterMapping) { foreach (var load in mapping.Key.LoadInstructions.ToArray()) { if (load.IsDescendantOf(instruction)) continue; load.ReplaceWith(new LdLoc(mapping.Value)); } } return function; } } (Func, IType) ConvertQuote(CallInstruction invocation) { if (invocation.Arguments.Count != 1) return (null, SpecialType.UnknownType); var argument = invocation.Arguments.Single(); if (argument is ILFunction function) { return (() => function, function.DelegateType); } else { var (converted, type) = ConvertInstruction(argument); if (converted == null) return (converted, type); return (BuildQuote, type); ILInstruction BuildQuote() { var f = converted(); if (f is ILFunction lambda && argument is CallInstruction call) { SetExpressionTreeFlag(lambda, call); } return f; } } } void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call) { lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; lambda.DelegateType = call.Method.ReturnType; } bool ReadParameters(ILInstruction initializer, IList parameters, IList parameterVariables, ITypeResolveContext resolveContext) { switch (initializer) { case Block initializerBlock: if (initializerBlock.Kind != BlockKind.ArrayInitializer) return false; int i = 0; foreach (var inst in initializerBlock.Instructions.OfType()) { if (i >= this.parameters.Count) return false; if (!inst.Value.MatchLdLoc(out var v)) return false; if (!this.parameters.TryGetValue(v, out var value)) return false; // Add parameter variable only once to mapping. if (!this.parameterMapping.ContainsKey(v)) { var param = new ILVariable(VariableKind.Parameter, value.Item1, i) { Name = value.Item2 }; parameterMapping.Add(v, param); parameterVariables.Add(param); parameters.Add(new DefaultParameter(value.Item1, value.Item2)); instructionsToRemove.Add((ILInstruction)v.StoreInstructions[0]); } i++; } return true; default: return IsEmptyParameterList(initializer); } } (Func, IType) ConvertInstruction(ILInstruction instruction, IType typeHint = null) { var (inst, type) = Convert(); if (inst == null) return (null, type); ILInstruction DoConvert() { var result = inst(); Debug.Assert(type != null, "IType must be non-null!"); Debug.Assert(result.ResultType == type.GetStackType(), "StackTypes must match!"); if (typeHint != null) { if (result.ResultType != typeHint.GetStackType()) { return new Conv(result, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign()); } } return result; } return (DoConvert, typeHint ?? type); (Func, IType) Convert() { switch (instruction) { case CallInstruction invocation: if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression") return (null, SpecialType.UnknownType); switch (invocation.Method.Name) { case "Add": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false); case "AddChecked": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true); case "And": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd); case "AndAlso": return ConvertLogicOperator(invocation, true); case "ArrayAccess": case "ArrayIndex": return ConvertArrayIndex(invocation); case "ArrayLength": return ConvertArrayLength(invocation); case "Call": return ConvertCall(invocation); case "Coalesce": return ConvertCoalesce(invocation); case "Condition": return ConvertCondition(invocation); case "Constant": return ConvertConstant(invocation); case "Convert": return ConvertCast(invocation, false); case "ConvertChecked": return ConvertCast(invocation, true); case "Divide": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div); case "Equal": return ConvertComparison(invocation, ComparisonKind.Equality); case "ExclusiveOr": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor); case "Field": return ConvertField(invocation, typeHint); case "GreaterThan": return ConvertComparison(invocation, ComparisonKind.GreaterThan); case "GreaterThanOrEqual": return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual); case "Invoke": return ConvertInvoke(invocation); case "Lambda": return ConvertLambda(invocation); case "LeftShift": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft); case "LessThan": return ConvertComparison(invocation, ComparisonKind.LessThan); case "LessThanOrEqual": return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual); case "ListInit": return ConvertListInit(invocation); case "MemberInit": return ConvertMemberInit(invocation); case "Modulo": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem); case "Multiply": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false); case "MultiplyChecked": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true); case "Negate": return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); case "NegateChecked": return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); case "New": return ConvertNewObject(invocation); case "NewArrayBounds": return ConvertNewArrayBounds(invocation); case "NewArrayInit": return ConvertNewArrayInit(invocation); case "Not": return ConvertNotOperator(invocation); case "NotEqual": return ConvertComparison(invocation, ComparisonKind.Inequality); case "OnesComplement": return ConvertNotOperator(invocation); case "Or": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr); case "OrElse": return ConvertLogicOperator(invocation, false); case "Property": return ConvertProperty(invocation); case "Quote": return ConvertQuote(invocation); case "RightShift": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight); case "Subtract": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); case "SubtractChecked": return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); case "TypeAs": return ConvertTypeAs(invocation); case "TypeIs": return ConvertTypeIs(invocation); } return (null, SpecialType.UnknownType); case ILFunction function: ILFunction ApplyChangesToILFunction() { if (function.Kind == ILFunctionKind.ExpressionTree) { function.DelegateType = UnwrapExpressionTree(function.DelegateType); function.Kind = ILFunctionKind.Delegate; } return function; } return (ApplyChangesToILFunction, function.DelegateType); case LdLoc ldloc: if (IsExpressionTreeParameter(ldloc.Variable)) { // Replace an already mapped parameter with the actual ILVariable, // we generated earlier. if (parameterMapping.TryGetValue(ldloc.Variable, out var v)) { if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike) return (() => new LdLoca(v), typeHint); return (() => new LdLoc(v), v.Type); } // This is a parameter variable from an outer scope. // We can't replace these variables just yet, because the transform works backwards. // We simply return the same instruction again, but return the actual expected type, // so our transform can continue normally. // Later, we will replace all references to unmapped variables, // with references to mapped parameters. if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction instr) { if (MatchParameterVariableAssignment(instr, out _, out var t, out _)) return (() => new ExpressionTreeCast(t, ldloc, false), t); } } return (null, SpecialType.UnknownType); default: return (null, SpecialType.UnknownType); } } } bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1; IType UnwrapExpressionTree(IType delegateType) { if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) { return pt.TypeArguments[0]; } return delegateType; } (Func, IType) ConvertArrayIndex(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var (array, arrayType) = ConvertInstruction(invocation.Arguments[0]); if (array == null) return (null, SpecialType.UnknownType); if (!(arrayType is ArrayType type)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) arguments = new[] { invocation.Arguments[1] }; ILInstruction Convert() { Func[] toBeConverted = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { var (converted, indexType) = ConvertInstruction(arguments[i]); if (converted == null) return null; toBeConverted[i] = converted; } return new LdObj(new LdElema(type.ElementType, array(), toBeConverted.SelectArray(f => f())) { DelayExceptions = true }, type.ElementType); } return (Convert, type.ElementType); } (Func, IType) ConvertArrayLength(CallInstruction invocation) { if (invocation.Arguments.Count != 1) return (null, SpecialType.UnknownType); var (converted, _) = ConvertInstruction(invocation.Arguments[0]); if (converted == null) return (null, SpecialType.UnknownType); return (() => new LdLen(StackType.I4, converted()), context.TypeSystem.FindType(KnownTypeCode.Int32)); } (Func, IType) ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]); if (left == null) return (null, SpecialType.UnknownType); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]); if (right == null) return (null, SpecialType.UnknownType); IMember method; switch (invocation.Arguments.Count) { case 2: if (op == BinaryNumericOperator.ShiftLeft || op == BinaryNumericOperator.ShiftRight) { if (!NullableType.GetUnderlyingType(rightType).IsKnownType(KnownTypeCode.Int32)) return (null, SpecialType.UnknownType); } else { if (!rightType.Equals(leftType)) return (null, SpecialType.UnknownType); } return (() => new BinaryNumericInstruction(op, left(), right(), NullableType.GetUnderlyingType(leftType).GetStackType(), NullableType.GetUnderlyingType(rightType).GetStackType(), isChecked == true, leftType.GetSign(), isLifted: NullableType.IsNullable(leftType) || NullableType.IsNullable(rightType)), leftType); case 3: if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) return (null, SpecialType.UnknownType); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, method.ReturnType); case 4: if (!invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull)) return (null, SpecialType.UnknownType); if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) return (null, SpecialType.UnknownType); bool isLifted = NullableType.IsNullable(leftType); if (isLifted) method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType); default: return (null, SpecialType.UnknownType); } } (Func, IType) ConvertBind(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var (value, typeValue) = ConvertInstruction(invocation.Arguments[1]); if (value == null) return (null, SpecialType.UnknownType); if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { var method = (IMethod)member; // It is possible to use Expression.Bind with a get-accessor, // however, it would be an invalid expression tree if the property is readonly. // As this is an assignment, the ILAst expects a set-accessor. To avoid any problems // constructing property assignments, we explicitly use the set-accessor instead. if (method.AccessorOwner is IProperty { CanSet: true } property && method != property.Setter) { member = property.Setter; } } else if (MatchGetFieldFromHandle(invocation.Arguments[0], out member)) { } else { return (null, SpecialType.UnknownType); } switch (member) { case IMethod method: if (method.IsStatic) return (targetVariable => new Call(method) { Arguments = { new LdLoc(targetVariable), value() } }, method.ReturnType); else return (targetVariable => new CallVirt(method) { Arguments = { new LdLoc(targetVariable), value() } }, method.ReturnType); case IField field: return (targetVariable => new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member) { DelayExceptions = true }, value(), member.ReturnType), field.ReturnType); } return (null, SpecialType.UnknownType); } (Func, IType) ConvertCall(CallInstruction invocation) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); IList arguments = null; Func targetConverter = null; IType targetType = null; if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { // static method if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments)) { arguments = new List(invocation.Arguments.Skip(1)); } } else if (MatchGetMethodFromHandle(invocation.Arguments[1], out member)) { if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { arguments = new List(invocation.Arguments.Skip(2)); } if (!invocation.Arguments[0].MatchLdNull()) { (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]); if (targetConverter == null) return (null, SpecialType.UnknownType); } } if (arguments == null) return (null, SpecialType.UnknownType); IMethod method = (IMethod)member; var convertedArguments = ConvertCallArguments(arguments, method); if (convertedArguments == null) return (null, SpecialType.UnknownType); if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { if (!MatchGetMethodFromHandle(UnpackConstant(invocation.Arguments[0]), out var targetMethod)) return (null, SpecialType.UnknownType); if (!MatchGetTypeFromHandle(UnpackConstant(arguments[0]), out var delegateType)) return (null, SpecialType.UnknownType); return (() => new NewObj(delegateType.GetConstructors().Single()) { Arguments = { convertedArguments[1](), new LdFtn((IMethod)targetMethod) } }, delegateType); } CallInstruction BuildCall() { CallInstruction call; if (method.IsStatic) { call = new Call(method); } else { call = new CallVirt(method); } if (targetConverter != null) { call.Arguments.Add(PrepareCallTarget(method.DeclaringType, targetConverter(), targetType)); } call.Arguments.AddRange(convertedArguments.Select(f => f())); return call; } return (BuildCall, method.ReturnType); } ILInstruction PrepareCallTarget(IType expectedType, ILInstruction target, IType targetType) { ILInstruction result; switch (CallInstruction.ExpectedTypeForThisPointer(expectedType, null)) { case StackType.Ref: if (target.ResultType == StackType.Ref) { result = target; } else if (target is LdLoc ldloc) { result = new LdLoca(ldloc.Variable).WithILRange(ldloc); } else { result = new AddressOf(target, expectedType); } break; case StackType.O: if (targetType.IsReferenceType == false) { result = new Box(target, targetType); } else { result = target; } break; default: result = target; break; } if (expectedType.Kind == TypeKind.Unknown && result.ResultType != StackType.Unknown) { result = new Conv(target, PrimitiveType.Unknown, false, Sign.None); } else if (expectedType.Kind != TypeKind.Unknown && result.ResultType == StackType.Unknown) { // if references are missing, we need to coerce the unknown type to the expected type. // Otherwise we will get loads of assertions and expression trees // are usually explicit about any conversions. result = new Conv(result, expectedType.ToPrimitiveType(), false, Sign.None); } return result; } ILInstruction UnpackConstant(ILInstruction inst) { if (!(inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant" && call.Arguments.Count == 2)) return inst; return call.Arguments[0]; } Func[] ConvertCallArguments(IList arguments, IMethod method) { var converted = new Func[arguments.Count]; Debug.Assert(arguments.Count == method.Parameters.Count); for (int i = 0; i < arguments.Count; i++) { var expectedType = method.Parameters[i].Type; var argument = ConvertInstruction(arguments[i], expectedType).Item1; if (argument == null) return null; converted[i] = argument; } return converted; } (Func, IType) ConvertCast(CallInstruction invocation, bool isChecked) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var targetType)) return (null, SpecialType.UnknownType); var (expr, exprType) = ConvertInstruction(invocation.Arguments[0]); if (expr == null) return (null, SpecialType.UnknownType); if (exprType.IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32)) return (expr, targetType); return (() => new ExpressionTreeCast(targetType, expr(), isChecked), targetType); } (Func, IType) ConvertCoalesce(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[0]); if (trueInst == null) return (null, SpecialType.UnknownType); var (fallbackInst, fallbackInstType) = ConvertInstruction(invocation.Arguments[1]); if (fallbackInst == null) return (null, SpecialType.UnknownType); var kind = NullCoalescingKind.Ref; var trueInstTypeNonNullable = NullableType.GetUnderlyingType(trueInstType); IType targetType; if (NullableType.IsNullable(trueInstType) && conversions.ImplicitConversion(fallbackInstType, trueInstTypeNonNullable).IsValid) { targetType = trueInstTypeNonNullable; kind = NullableType.IsNullable(fallbackInstType) ? NullCoalescingKind.Nullable : NullCoalescingKind.NullableWithValueFallback; } else if (conversions.ImplicitConversion(fallbackInstType, trueInstType).IsValid) { targetType = trueInstType; } else { targetType = fallbackInstType; } return (() => new NullCoalescingInstruction(kind, trueInst(), fallbackInst()) { UnderlyingResultType = trueInstTypeNonNullable.GetStackType() }, targetType); } (Func, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]); if (left == null) return (null, SpecialType.UnknownType); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]); if (right == null) return (null, SpecialType.UnknownType); if (invocation.Arguments.Count == 4 && invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull) && MatchGetMethodFromHandle(invocation.Arguments[3], out var method)) { bool isLifted = NullableType.IsNullable(leftType); if (isLifted) method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType); } var rr = resolver.ResolveBinaryOperator(kind.ToBinaryOperatorType(), new ResolveResult(leftType), new ResolveResult(rightType)) as OperatorResolveResult; if (rr != null && !rr.IsError && rr.UserDefinedOperatorMethod != null) { return (() => new Call(rr.UserDefinedOperatorMethod) { Arguments = { left(), right() } }, rr.UserDefinedOperatorMethod.ReturnType); } if (leftType.IsKnownType(KnownTypeCode.String) && rightType.IsKnownType(KnownTypeCode.String)) { IMethod operatorMethod; switch (kind) { case ComparisonKind.Equality: operatorMethod = leftType.GetMethods(m => m.IsOperator && m.Name == "op_Equality" && m.Parameters.Count == 2).FirstOrDefault(m => m.Parameters[0].Type.IsKnownType(KnownTypeCode.String) && m.Parameters[1].Type.IsKnownType(KnownTypeCode.String)); if (operatorMethod == null) return (null, SpecialType.UnknownType); break; case ComparisonKind.Inequality: operatorMethod = leftType.GetMethods(m => m.IsOperator && m.Name == "op_Inequality" && m.Parameters.Count == 2).FirstOrDefault(m => m.Parameters[0].Type.IsKnownType(KnownTypeCode.String) && m.Parameters[1].Type.IsKnownType(KnownTypeCode.String)); if (operatorMethod == null) return (null, SpecialType.UnknownType); break; default: return (null, SpecialType.UnknownType); } return (() => new Call(operatorMethod) { Arguments = { left(), right() } }, operatorMethod.ReturnType); } var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); var lifting = NullableType.IsNullable(leftType) ? ComparisonLiftingKind.CSharp : ComparisonLiftingKind.None; var utype = NullableType.GetUnderlyingType(leftType); return (() => new Comp(kind, lifting, utype.GetStackType(), utype.GetSign(), left(), right()), resultType); } (Func, IType) ConvertCondition(CallInstruction invocation) { if (invocation.Arguments.Count != 3) return (null, SpecialType.UnknownType); var (condition, conditionType) = ConvertInstruction(invocation.Arguments[0]); if (condition == null || !conditionType.IsKnownType(KnownTypeCode.Boolean)) return (null, SpecialType.UnknownType); var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[1]); if (trueInst == null) return (null, SpecialType.UnknownType); var (falseInst, falseInstType) = ConvertInstruction(invocation.Arguments[2]); if (falseInst == null) return (null, SpecialType.UnknownType); if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(trueInstType, falseInstType)) return (null, SpecialType.UnknownType); return (() => new IfInstruction(condition(), trueInst(), falseInst()), trueInstType); } (Func, IType) ConvertConstant(CallInstruction invocation) { if (!MatchConstantCall(invocation, out var value, out var type)) return (null, SpecialType.UnknownType); if (value.MatchBox(out var arg, out var boxType)) { if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean)) return (() => new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType); return (() => ConvertValue(arg, invocation), type); } return (() => ConvertValue(value, invocation), type); } (Func, IType) ConvertElementInit(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); if (!MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); var args = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { var arg = ConvertInstruction(arguments[i]).Item1; if (arg == null) return (null, SpecialType.UnknownType); args[i] = arg; } ILInstruction BuildCall() { CallInstruction call = member.IsStatic ? (CallInstruction)new Call((IMethod)member) : new CallVirt((IMethod)member); call.Arguments.AddRange(args.Select(f => f())); return call; } return (BuildCall, member.ReturnType); } (Func, IType) ConvertField(CallInstruction invocation, IType typeHint) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); Func targetConverter = null; if (!invocation.Arguments[0].MatchLdNull()) { targetConverter = ConvertInstruction(invocation.Arguments[0]).Item1; if (targetConverter == null) return (null, SpecialType.UnknownType); } if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member)) return (null, SpecialType.UnknownType); IType type = member.ReturnType; if (typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike) { type = typeHint; } return (BuildField, type); ILInstruction BuildField() { ILInstruction inst; if (targetConverter == null) { inst = new LdsFlda((IField)member); } else { var target = targetConverter(); if (member.DeclaringType.IsReferenceType == true) { inst = new LdFlda(target, (IField)member) { DelayExceptions = true }; } else { inst = new LdFlda(new AddressOf(target, member.DeclaringType), (IField)member) { DelayExceptions = true }; } } if (!(typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike)) { inst = new LdObj(inst, member.ReturnType); } return inst; } } (Func, IType) ConvertInvoke(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]); if (targetConverter == null) return (null, SpecialType.UnknownType); var invokeMethod = targetType.GetDelegateInvokeMethod(); if (invokeMethod == null) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); var convertedArguments = ConvertCallArguments(arguments, invokeMethod); if (convertedArguments == null) return (null, SpecialType.UnknownType); ILInstruction BuildCall() { var call = new CallVirt(invokeMethod); call.Arguments.Add(targetConverter()); call.Arguments.AddRange(convertedArguments.Select(f => f())); return call; } return (BuildCall, invokeMethod.ReturnType); } (Func, IType) ConvertListInit(CallInstruction invocation) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); var newObj = ConvertInstruction(invocation.Arguments[0]).Item1; if (newObj == null) return (null, SpecialType.UnknownType); if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor)) return (null, SpecialType.UnknownType); IList arguments; if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) { if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); } else { if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) return (null, SpecialType.UnknownType); } if (arguments == null || arguments.Count == 0) return (null, SpecialType.UnknownType); Func[] convertedArguments = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") { var arg = ConvertElementInit(elementInit).Item1; if (arg == null) return (null, SpecialType.UnknownType); convertedArguments[i] = v => { var a = arg(); ((CallInstruction)a).Arguments.Insert(0, new LdLoc(v)); return a; }; } else { var arg = ConvertInstruction(arguments[i]).Item1; if (arg == null) return (null, SpecialType.UnknownType); convertedArguments[i] = v => arg(); } } Block BuildBlock() { var initializerBlock = new Block(BlockKind.CollectionInitializer); ILFunction function = lambdaStack.Peek(); var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType); initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.Instructions.Add(new StLoc(initializer, newObj())); initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer))); return initializerBlock; } return (BuildBlock, ctor.DeclaringType); } (Func, IType) ConvertLogicOperator(CallInstruction invocation, bool and) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); var (left, leftType) = ConvertInstruction(invocation.Arguments[0]); if (left == null) return (null, SpecialType.UnknownType); var (right, rightType) = ConvertInstruction(invocation.Arguments[1]); if (right == null) return (null, SpecialType.UnknownType); IMember method; switch (invocation.Arguments.Count) { case 2: var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); return (() => and ? IfInstruction.LogicAnd(left(), right()) : IfInstruction.LogicOr(left(), right()), resultType); case 3: if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) return (null, SpecialType.UnknownType); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, method.ReturnType); case 4: if (!invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull)) return (null, SpecialType.UnknownType); if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) return (null, SpecialType.UnknownType); bool isLifted = NullableType.IsNullable(leftType); if (isLifted) method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType); default: return (null, SpecialType.UnknownType); } } (Func, IType) ConvertMemberInit(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var newObj = ConvertInstruction(invocation.Arguments[0]).Item1; if (newObj == null) return (null, SpecialType.UnknownType); if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); if (arguments == null || arguments.Count == 0) return (null, SpecialType.UnknownType); Func[] convertedArguments = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { Func arg; if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind") { arg = ConvertBind(bind).Item1; if (arg == null) return (null, SpecialType.UnknownType); } else { return (null, SpecialType.UnknownType); } convertedArguments[i] = arg; } ILInstruction BuildBlock() { var function = lambdaStack.Peek(); var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType); var initializerBlock = new Block(BlockKind.CollectionInitializer); initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.Instructions.Add(new StLoc(initializer, newObj())); initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer))); return initializerBlock; } return (BuildBlock, ctor.DeclaringType); } (Func, IType) ConvertNewArrayBounds(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); if (arguments.Count == 0) return (null, SpecialType.UnknownType); var indices = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { var index = ConvertInstruction(arguments[i]).Item1; if (index == null) return (null, SpecialType.UnknownType); indices[i] = index; } return (() => new NewArr(type, indices.SelectArray(f => f())), new ArrayType(context.TypeSystem, type, arguments.Count)); } (Func, IType) ConvertNewArrayInit(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); ArrayType arrayType = new ArrayType(context.BlockContext.TypeSystem, type); if (arguments.Count == 0) return (() => new NewArr(type, new LdcI4(0)), arrayType); var convertedArguments = new Func[arguments.Count]; for (int i = 0; i < arguments.Count; i++) { ILInstruction item = arguments[i]; var value = ConvertInstruction(item).Item1; if (value == null) return (null, SpecialType.UnknownType); convertedArguments[i] = value; } ILInstruction BuildInitializer() { var block = (Block)invocation.Arguments[1]; var function = lambdaStack.Peek(); var variable = function.RegisterVariable(VariableKind.InitializerTarget, arrayType); Block initializer = new Block(BlockKind.ArrayInitializer); initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(convertedArguments.Length)))); for (int i = 0; i < convertedArguments.Length; i++) { initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)) { DelayExceptions = true }, convertedArguments[i](), type)); } initializer.FinalInstruction = new LdLoc(variable); return initializer; } return (BuildInitializer, arrayType); } bool MatchNew(CallInstruction invocation, out IMethod ctor) { ctor = null; if (invocation.Method.Name != "New") return false; switch (invocation.Arguments.Count) { case 1: if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault(); return ctor != null; } if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member)) { ctor = (IMethod)member; return true; } return false; case 2: case 3: if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) return false; ctor = (IMethod)member; return true; default: return false; } } (Func, IType) ConvertNewObject(CallInstruction invocation) { switch (invocation.Arguments.Count) { case 1: if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault(); if (ctor == null) return (null, SpecialType.UnknownType); return (() => new NewObj(ctor), type); } if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member)) { return (() => new NewObj((IMethod)member), member.DeclaringType); } return (null, SpecialType.UnknownType); case 2: if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); IMethod method = (IMethod)member; Func[] convertedArguments = ConvertCallArguments(arguments, method); if (convertedArguments == null) return (null, SpecialType.UnknownType); return (() => BuildNewObj(method, convertedArguments), member.DeclaringType); case 3: if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); method = (IMethod)member; convertedArguments = ConvertCallArguments(arguments, method); if (convertedArguments == null) return (null, SpecialType.UnknownType); return (() => BuildNewObj(method, convertedArguments), member.DeclaringType); } ILInstruction BuildNewObj(IMethod method, Func[] args) { var newObj = new NewObj(method); newObj.Arguments.AddRange(args.Select(f => f())); return newObj; } return (null, SpecialType.UnknownType); } (Func, IType) ConvertNotOperator(CallInstruction invocation) { if (invocation.Arguments.Count < 1) return (null, SpecialType.UnknownType); var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]); if (argument == null) return (null, SpecialType.UnknownType); var underlyingType = NullableType.GetUnderlyingType(argumentType); switch (invocation.Arguments.Count) { case 1: bool isLifted = NullableType.IsNullable(argumentType); return (() => underlyingType.IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument(), isLifted) : (ILInstruction)new BitNot(argument(), isLifted, underlyingType.GetStackType()), argumentType); case 2: if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) return (null, SpecialType.UnknownType); return (() => new Call((IMethod)method) { Arguments = { argument() } }, method.ReturnType); default: return (null, SpecialType.UnknownType); } } (Func, IType) ConvertProperty(CallInstruction invocation) { if (invocation.Arguments.Count < 2) return (null, SpecialType.UnknownType); Func targetConverter = null; IType targetType = null; if (!invocation.Arguments[0].MatchLdNull()) { (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]); if (targetConverter == null) return (null, SpecialType.UnknownType); } if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) return (null, SpecialType.UnknownType); IList arguments; if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { arguments = new List(); } var convertedArguments = ConvertCallArguments(arguments, (IMethod)member); if (convertedArguments == null) return (null, SpecialType.UnknownType); ILInstruction BuildProperty() { CallInstruction call; if (member.IsStatic) { call = new Call((IMethod)member); } else { call = new CallVirt((IMethod)member); } if (targetConverter != null) { call.Arguments.Add(PrepareCallTarget(member.DeclaringType, targetConverter(), targetType)); } call.Arguments.AddRange(convertedArguments.Select(f => f())); return call; } return (BuildProperty, member.ReturnType); } (Func, IType) ConvertTypeAs(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var converted = ConvertInstruction(invocation.Arguments[0]).Item1; if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) return (null, SpecialType.UnknownType); if (converted == null) return (null, SpecialType.UnknownType); ILInstruction BuildTypeAs() { ILInstruction inst = new IsInst(converted(), type); // We must follow ECMA-335, III.4.6: // If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T. if (type.IsKnownType(KnownTypeCode.NullableOfT)) inst = new UnboxAny(inst, type); return inst; } return (BuildTypeAs, type); } (Func, IType) ConvertTypeIs(CallInstruction invocation) { if (invocation.Arguments.Count != 2) return (null, SpecialType.UnknownType); var converted = ConvertInstruction(invocation.Arguments[0]).Item1; if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) return (null, SpecialType.UnknownType); var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); if (converted != null) return (() => new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted(), type), new LdNull()), resultType); return (null, SpecialType.UnknownType); } (Func, IType) ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) { if (invocation.Arguments.Count < 1) return (null, SpecialType.UnknownType); var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]); if (argument == null) return (null, SpecialType.UnknownType); switch (invocation.Arguments.Count) { case 1: ILInstruction left; var underlyingType = NullableType.GetUnderlyingType(argumentType); switch (underlyingType.GetStackType()) { case StackType.I4: left = new LdcI4(0); break; case StackType.I8: left = new LdcI8(0); break; case StackType.I: left = new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None); break; case StackType.F4: left = new LdcF4(0); break; case StackType.F8: left = new LdcF8(0); break; case StackType.O when underlyingType.IsKnownType(KnownTypeCode.Decimal): left = new LdcDecimal(0); break; default: return (null, SpecialType.UnknownType); } return (() => new BinaryNumericInstruction(op, left, argument(), underlyingType.GetStackType(), underlyingType.GetStackType(), isChecked == true, argumentType.GetSign(), isLifted: NullableType.IsNullable(argumentType)), argumentType); case 2: if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) return (null, SpecialType.UnknownType); return (() => new Call((IMethod)method) { Arguments = { argument() } }, method.ReturnType); } return (null, SpecialType.UnknownType); } ILInstruction ConvertValue(ILInstruction value, ILInstruction context) { switch (value) { case LdLoc ldloc: if (IsExpressionTreeParameter(ldloc.Variable)) { if (!parameterMapping.TryGetValue(ldloc.Variable, out var v)) return ldloc.Clone(); if (context is CallInstruction parentCall && parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call" && v.StackType.IsIntegerType()) return new LdLoca(v).WithILRange(ldloc); return null; } else if (IsClosureReference(ldloc.Variable)) { if (ldloc.Variable.Kind == VariableKind.Local) { ldloc.Variable.Kind = VariableKind.DisplayClassLocal; } if (ldloc.Variable.CaptureScope == null) { ldloc.Variable.CaptureScope = BlockContainer.FindClosestContainer(context); var f = ldloc.Variable.CaptureScope.Ancestors.OfType().FirstOrDefault(); if (f != null) { f.CapturedVariables.Add(ldloc.Variable); } } return ldloc; } else { return ldloc; } default: return value.Clone(); } } bool IsClosureReference(ILVariable variable) { if (!variable.IsSingleDefinition || !(variable.StoreInstructions.SingleOrDefault() is StLoc store)) return false; if (!(store.Value is NewObj newObj)) return false; return TransformDisplayClassUsage.IsPotentialClosure(this.context, newObj); } bool IsExpressionTreeParameter(ILVariable variable) { return variable.Type.FullName == "System.Linq.Expressions.ParameterExpression"; } bool MatchConstantCall(ILInstruction inst, out ILInstruction value, out IType type) { value = null; type = null; if (inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant") { value = call.Arguments[0]; if (call.Arguments.Count == 2) return MatchGetTypeFromHandle(call.Arguments[1], out type); type = value switch { LdNull => SpecialType.NullType, LdStr => context.TypeSystem.FindType(KnownTypeCode.String), LdcF4 => context.TypeSystem.FindType(KnownTypeCode.Single), LdcF8 => context.TypeSystem.FindType(KnownTypeCode.Double), LdcI4 => context.TypeSystem.FindType(KnownTypeCode.Int32), LdcI8 => context.TypeSystem.FindType(KnownTypeCode.Int64), _ => value.InferType(context.TypeSystem), }; return true; } return false; } internal static bool MatchGetTypeFromHandle(ILInstruction inst, out IType type) { type = null; return inst is CallInstruction getTypeCall && getTypeCall.Method.FullName == "System.Type.GetTypeFromHandle" && getTypeCall.Arguments.Count == 1 && getTypeCall.Arguments[0].MatchLdTypeToken(out type); } bool MatchGetMethodFromHandle(ILInstruction inst, out IMember member) { member = null; //castclass System.Reflection.MethodInfo(call GetMethodFromHandle(ldmembertoken op_Addition)) if (!inst.MatchCastClass(out var arg, out var type)) return false; if (type.FullName != "System.Reflection.MethodInfo") return false; if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle")) return false; return MatchFromHandleParameterList(call, out member); } bool MatchGetConstructorFromHandle(ILInstruction inst, out IMember member) { member = null; //castclass System.Reflection.ConstructorInfo(call GetMethodFromHandle(ldmembertoken op_Addition)) if (!inst.MatchCastClass(out var arg, out var type)) return false; if (type.FullName != "System.Reflection.ConstructorInfo") return false; if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle")) return false; return MatchFromHandleParameterList(call, out member); } bool MatchGetFieldFromHandle(ILInstruction inst, out IMember member) { member = null; if (!(inst is CallInstruction call && call.Method.FullName == "System.Reflection.FieldInfo.GetFieldFromHandle")) return false; return MatchFromHandleParameterList(call, out member); } static bool MatchFromHandleParameterList(CallInstruction call, out IMember member) { member = null; switch (call.Arguments.Count) { case 1: if (!call.Arguments[0].MatchLdMemberToken(out member)) return false; break; case 2: if (!call.Arguments[0].MatchLdMemberToken(out member)) return false; if (!call.Arguments[1].MatchLdTypeToken(out _)) return false; break; default: return false; } return true; } bool MatchArgumentList(ILInstruction inst, out IList arguments) { arguments = null; if (!(inst is Block block && block.Kind == BlockKind.ArrayInitializer)) { if (IsEmptyParameterList(inst)) { arguments = new List(); return true; } return false; } int i = 0; arguments = new List(); foreach (var item in block.Instructions.OfType()) { if (!(item.Target is LdElema ldelem && ldelem.Indices.Single().MatchLdcI4(i))) return false; arguments.Add(item.Value); i++; } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Text; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { class TupleTransform { /// /// Matches an 'ldflda' instruction accessing a tuple element. /// /// E.g. matches: /// ldflda Item1(ldflda Rest(target)) /// public static bool MatchTupleFieldAccess(LdFlda inst, out IType tupleType, out ILInstruction target, out int position) { tupleType = inst.Field.DeclaringType; target = inst.Target; if (!inst.Field.Name.StartsWith("Item", StringComparison.Ordinal)) { position = 0; return false; } if (!int.TryParse(inst.Field.Name.Substring(4), out position)) return false; if (!TupleType.IsTupleCompatible(tupleType, out _)) return false; while (target is LdFlda ldflda && ldflda.Field.Name == "Rest" && TupleType.IsTupleCompatible(ldflda.Field.DeclaringType, out _)) { tupleType = ldflda.Field.DeclaringType; target = ldflda.Target; position += TupleType.RestPosition - 1; } return true; } /// /// Matches 'newobj TupleType(...)'. /// Takes care of flattening long tuples. /// public static bool MatchTupleConstruction(NewObj newobj, out ILInstruction[] arguments) { arguments = null; if (newobj == null) return false; if (!TupleType.IsTupleCompatible(newobj.Method.DeclaringType, out int elementCount)) return false; arguments = new ILInstruction[elementCount]; int outIndex = 0; while (elementCount >= TupleType.RestPosition) { if (newobj.Arguments.Count != TupleType.RestPosition) return false; for (int pos = 1; pos < TupleType.RestPosition; pos++) { arguments[outIndex++] = newobj.Arguments[pos - 1]; } elementCount -= TupleType.RestPosition - 1; Debug.Assert(outIndex + elementCount == arguments.Length); newobj = newobj.Arguments.Last() as NewObj; if (newobj == null) return false; if (!TupleType.IsTupleCompatible(newobj.Method.DeclaringType, out int restElementCount)) return false; if (restElementCount != elementCount) return false; } Debug.Assert(outIndex + elementCount == arguments.Length); if (newobj.Arguments.Count != elementCount) return false; for (int i = 0; i < elementCount; i++) { arguments[outIndex++] = newobj.Arguments[i]; } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { public class UserDefinedLogicTransform : IStatementTransform { void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { if (LegacyPattern(block, pos, context)) return; if (RoslynOptimized(block, pos, context)) return; } bool RoslynOptimized(Block block, int pos, StatementTransformContext context) { // Roslyn, optimized pattern in combination with return statement: // if (logic.not(call op_False(ldloc lhsVar))) leave IL_0000 (call op_BitwiseAnd(ldloc lhsVar, rhsInst)) // leave IL_0000(ldloc lhsVar) // -> // user.logic op_BitwiseAnd(ldloc lhsVar, rhsInst) if (!block.Instructions[pos].MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) return false; if (trueInst.OpCode == OpCode.Nop) { trueInst = block.Instructions[pos + 1]; } else if (falseInst.OpCode == OpCode.Nop) { falseInst = block.Instructions[pos + 1]; } else { return false; } if (trueInst.MatchReturn(out var trueValue) && falseInst.MatchReturn(out var falseValue)) { var transformed = Transform(condition, trueValue, falseValue); if (transformed == null) { transformed = TransformDynamic(condition, trueValue, falseValue); } if (transformed != null) { context.Step("User-defined short-circuiting logic operator (optimized return)", condition); ((Leave)block.Instructions[pos + 1]).Value = transformed; block.Instructions.RemoveAt(pos); return true; } } return false; } bool LegacyPattern(Block block, int pos, StatementTransformContext context) { // Legacy csc pattern: // stloc s(lhsInst) // if (logic.not(call op_False(ldloc s))) Block { // stloc s(call op_BitwiseAnd(ldloc s, rhsInst)) // } // -> // stloc s(user.logic op_BitwiseAnd(lhsInst, rhsInst)) if (!block.Instructions[pos].MatchStLoc(out var s, out var lhsInst)) return false; if (!(s.Kind == VariableKind.StackSlot)) return false; if (!(block.Instructions[pos + 1] is IfInstruction ifInst)) return false; if (!ifInst.Condition.MatchLogicNot(out var condition)) return false; if (!(MatchCondition(condition, out var s2, out string conditionMethodName) && s2 == s)) return false; if (ifInst.FalseInst.OpCode != OpCode.Nop) return false; var trueInst = Block.Unwrap(ifInst.TrueInst); if (!trueInst.MatchStLoc(s, out var storeValue)) return false; if (storeValue is Call call) { if (!MatchBitwiseCall(call, s, conditionMethodName)) return false; if (s.IsUsedWithin(call.Arguments[1])) return false; context.Step("User-defined short-circuiting logic operator (legacy pattern)", condition); ((StLoc)block.Instructions[pos]).Value = new UserDefinedLogicOperator(call.Method, lhsInst, call.Arguments[1]) .WithILRange(call); block.Instructions.RemoveAt(pos + 1); context.RequestRerun(); // the 'stloc s' may now be eligible for inlining return true; } return false; } static bool MatchCondition(ILInstruction condition, out ILVariable v, out string name) { v = null; name = null; if (!(condition is Call call && call.Method.IsOperator && call.Arguments.Count == 1 && !call.IsLifted)) return false; name = call.Method.Name; if (!(name == "op_True" || name == "op_False")) return false; return call.Arguments[0].MatchLdLoc(out v); } static bool MatchBitwiseCall(Call call, ILVariable v, string conditionMethodName) { if (!(call != null && call.Method.IsOperator && call.Arguments.Count == 2 && !call.IsLifted)) return false; if (!call.Arguments[0].MatchLdLoc(v)) return false; return conditionMethodName == "op_False" && call.Method.Name == "op_BitwiseAnd" || conditionMethodName == "op_True" && call.Method.Name == "op_BitwiseOr"; } /// /// if (call op_False(ldloc lhsVar)) ldloc lhsVar else call op_BitwiseAnd(ldloc lhsVar, rhsInst) /// -> user.logic op_BitwiseAnd(ldloc lhsVar, rhsInst) /// or /// if (call op_True(ldloc lhsVar)) ldloc lhsVar else call op_BitwiseOr(ldloc lhsVar, rhsInst) /// -> user.logic op_BitwiseOr(ldloc lhsVar, rhsInst) /// public static ILInstruction Transform(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst) { if (!MatchCondition(condition, out var lhsVar, out var conditionMethodName)) return null; if (!trueInst.MatchLdLoc(lhsVar)) return null; var call = falseInst as Call; if (!MatchBitwiseCall(call, lhsVar, conditionMethodName)) return null; var result = new UserDefinedLogicOperator(call.Method, call.Arguments[0], call.Arguments[1]); result.AddILRange(condition); result.AddILRange(trueInst); result.AddILRange(call); return result; } public static ILInstruction TransformDynamic(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst) { // Check condition: System.Linq.Expressions.ExpressionType unaryOp; if (condition.MatchLdLoc(out var lhsVar)) { // if (ldloc lhsVar) box bool(ldloc lhsVar) else dynamic.binary.operator.logic Or(ldloc lhsVar, rhsInst) // -> dynamic.logic.operator OrElse(ldloc lhsVar, rhsInst) if (trueInst is Box box && box.Type.IsKnownType(KnownTypeCode.Boolean)) { unaryOp = System.Linq.Expressions.ExpressionType.IsTrue; trueInst = box.Argument; } else if (falseInst is Box box2 && box2.Type.IsKnownType(KnownTypeCode.Boolean)) { // negate condition and swap true/false unaryOp = System.Linq.Expressions.ExpressionType.IsFalse; falseInst = trueInst; trueInst = box2.Argument; } else { return null; } } else if (condition is DynamicUnaryOperatorInstruction unary) { // if (dynamic.unary.operator IsFalse(ldloc lhsVar)) ldloc lhsVar else dynamic.binary.operator.logic And(ldloc lhsVar, rhsInst) // -> dynamic.logic.operator AndAlso(ldloc lhsVar, rhsInst) unaryOp = unary.Operation; if (!unary.Operand.MatchLdLoc(out lhsVar)) return null; } else if (MatchCondition(condition, out lhsVar, out string operatorMethodName)) { // if (call op_False(ldloc s)) box S(ldloc s) else dynamic.binary.operator.logic And(ldloc s, rhsInst)) if (operatorMethodName == "op_True") { unaryOp = System.Linq.Expressions.ExpressionType.IsTrue; } else { Debug.Assert(operatorMethodName == "op_False"); unaryOp = System.Linq.Expressions.ExpressionType.IsFalse; } var callParamType = ((Call)condition).Method.Parameters.Single().Type.SkipModifiers(); if (callParamType.IsReferenceType == false) { // If lhs is a value type, eliminate the boxing instruction. if (trueInst is Box box && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(box.Type, callParamType)) { trueInst = box.Argument; } else if (trueInst.OpCode == OpCode.LdcI4) { // special case, handled below in 'check trueInst' } else { return null; } } } else { return null; } // Check trueInst: DynamicUnaryOperatorInstruction rhsUnary; if (trueInst.MatchLdLoc(lhsVar)) { // OK, typical pattern where the expression evaluates to 'dynamic' rhsUnary = null; } else if (trueInst.MatchLdcI4(1) && unaryOp == System.Linq.Expressions.ExpressionType.IsTrue) { // logic.or(IsTrue(lhsVar), IsTrue(lhsVar | rhsInst)) // => IsTrue(lhsVar || rhsInst) rhsUnary = falseInst as DynamicUnaryOperatorInstruction; if (rhsUnary != null) { if (rhsUnary.Operation != System.Linq.Expressions.ExpressionType.IsTrue) return null; falseInst = rhsUnary.Operand; } else { return null; } } else { return null; } System.Linq.Expressions.ExpressionType expectedBitop; System.Linq.Expressions.ExpressionType logicOp; if (unaryOp == System.Linq.Expressions.ExpressionType.IsFalse) { expectedBitop = System.Linq.Expressions.ExpressionType.And; logicOp = System.Linq.Expressions.ExpressionType.AndAlso; } else if (unaryOp == System.Linq.Expressions.ExpressionType.IsTrue) { expectedBitop = System.Linq.Expressions.ExpressionType.Or; logicOp = System.Linq.Expressions.ExpressionType.OrElse; } else { return null; } // Check falseInst: if (!(falseInst is DynamicBinaryOperatorInstruction binary)) return null; if (binary.Operation != expectedBitop) return null; if (!binary.Left.MatchLdLoc(lhsVar)) return null; var logicInst = new DynamicLogicOperatorInstruction(binary.BinderFlags, logicOp, binary.CallingContext, binary.LeftArgumentInfo, binary.Left, binary.RightArgumentInfo, binary.Right) .WithILRange(binary); if (rhsUnary != null) { rhsUnary.Operand = logicInst; return rhsUnary; } else { return logicInst; } } } } ================================================ FILE: ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs ================================================ // Copyright (c) 2017 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.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { public class UsingTransform : IBlockTransform { BlockTransformContext context; void IBlockTransform.Run(Block block, BlockTransformContext context) { if (!context.Settings.UsingStatement) return; this.context = context; for (int i = context.IndexOfFirstAlreadyTransformedInstruction - 1; i >= 0; i--) { if (TransformUsing(block, i)) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (TransformUsingVB(block, i)) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } if (TransformAsyncUsing(block, i)) { context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; continue; } } } /// /// stloc obj(resourceExpression) /// .try BlockContainer { /// Block IL_0003(incoming: 1) { /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } finally BlockContainer { /// Block IL_0012(incoming: 1) { /// if (comp(ldloc obj != ldnull)) Block IL_001a { /// callvirt Dispose(ldnull) /// } /// leave IL_0012(nop) /// } /// } /// leave IL_0000(nop) /// => /// using (resourceExpression) { /// BlockContainer { /// Block IL_0003(incoming: 1) { /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } /// } /// bool TransformUsing(Block block, int i) { if (i + 1 >= block.Instructions.Count) return false; if (!(block.Instructions[i + 1] is TryFinally tryFinally) || !(block.Instructions[i] is StLoc storeInst)) return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) return false; if (storeInst.Variable.Kind != VariableKind.Local) return false; if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) return false; if (!storeInst.Variable.AddressInstructions.All(ValidateAddressUse)) return false; if (storeInst.Variable.StoreInstructions.Count > 1) return false; if (!(tryFinally.FinallyBlock is BlockContainer container)) return false; if (!MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull())) return false; context.Step("UsingTransform", tryFinally); storeInst.Variable.Kind = VariableKind.UsingLocal; block.Instructions.RemoveAt(i + 1); block.Instructions[i] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock) { IsRefStruct = context.Settings.IntroduceRefModifiersOnStructs && storeInst.Variable.Type.Kind == TypeKind.Struct && storeInst.Variable.Type.IsByRefLike }.WithILRange(storeInst); return true; bool ValidateAddressUse(LdLoca la) { if (!la.IsDescendantOf(tryFinally)) return false; if (la.IsDescendantOf(tryFinally.TryBlock)) { if (!(ILInlining.IsUsedAsThisPointerInCall(la) || ILInlining.IsPassedToInParameter(la))) return false; } return true; } } /// /// .try BlockContainer { /// Block IL_0003(incoming: 1) { /// stloc obj(resourceExpression) /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } finally BlockContainer { /// Block IL_0012(incoming: 1) { /// if (comp(ldloc obj != ldnull)) Block IL_001a { /// callvirt Dispose(ldnull) /// } /// leave IL_0012(nop) /// } /// } /// leave IL_0000(nop) /// => /// using (resourceExpression) { /// BlockContainer { /// Block IL_0003(incoming: 1) { /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } /// } /// bool TransformUsingVB(Block block, int i) { if (i >= block.Instructions.Count) return false; if (!(block.Instructions[i] is TryFinally tryFinally)) return false; if (!(tryFinally.TryBlock is BlockContainer tryContainer && tryContainer.EntryPoint.Instructions.FirstOrDefault() is StLoc storeInst)) return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) return false; if (storeInst.Variable.Kind != VariableKind.Local) return false; if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) return false; if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) return false; if (storeInst.Variable.StoreInstructions.Count > 1) return false; if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull())) return false; context.Step("UsingTransformVB", tryFinally); storeInst.Variable.Kind = VariableKind.UsingLocal; tryContainer.EntryPoint.Instructions.RemoveAt(0); block.Instructions[i] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock); return true; } bool CheckResourceType(IType type) { // non-generic IEnumerator does not implement IDisposable. // This is a workaround for non-generic foreach. if (type.IsKnownType(KnownTypeCode.IEnumerator) || type.GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IEnumerator))) return true; if (NullableType.GetUnderlyingType(type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) return true; if (context.Settings.IntroduceRefModifiersOnStructs && type.Kind == TypeKind.Struct && type.IsByRefLike) return true; // General GetEnumerator-pattern? if (!type.GetMethods(m => m.Name == "GetEnumerator" && m.TypeParameters.Count == 0 && m.Parameters.Count == 0).Any(m => ImplementsForeachPattern(m.ReturnType))) return false; return true; } bool ImplementsForeachPattern(IType type) { if (!type.GetMethods(m => m.Name == "MoveNext" && m.TypeParameters.Count == 0 && m.Parameters.Count == 0).Any(m => m.ReturnType.IsKnownType(KnownTypeCode.Boolean))) return false; if (!type.GetProperties(p => p.Name == "Current" && p.CanGet && !p.IsIndexer).Any()) return false; return true; } /// finally BlockContainer { /// Block IL_0012(incoming: 1) { /// if (comp(ldloc obj != ldnull)) Block IL_001a { /// callvirt Dispose(obj) /// } /// leave IL_0012(nop) /// } /// } bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull, in string disposeMethodFullName = "System.IDisposable.Dispose", KnownTypeCode disposeTypeCode = KnownTypeCode.IDisposable) { var entryPoint = container.EntryPoint; if (entryPoint.IncomingEdgeCount != 1) return false; int pos = 0; bool isReference = objVar.Type.IsReferenceType != false; // optional: // stloc temp(isinst TDisposable(ldloc obj)) if (entryPoint.Instructions.ElementAtOrDefault(pos).MatchStLoc(out var tempVar, out var isinst)) { if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(disposeTypeCode)) { return false; } if (!tempVar.IsSingleDefinition) return false; isReference = true; pos++; objVar = tempVar; } // if (comp(ldloc obj != ldnull)) Block IL_001a { // callvirt Dispose(obj) // } var checkInst = entryPoint.Instructions.ElementAtOrDefault(pos); if (checkInst == null) return false; if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck, disposeMethodFullName, disposeTypeCode)) { return false; } // make sure the (optional) temporary is used only in the dispose check if (pos > 0 && objVar.LoadCount != numObjVarLoadsInCheck) return false; pos++; // make sure, the finally ends in a leave(nop) instruction. if (!entryPoint.Instructions.ElementAtOrDefault(pos).MatchLeave(container, out var returnValue)) return false; if (!returnValue.MatchNop()) return false; // leave is the last instruction return (pos + 1) == entryPoint.Instructions.Count; } bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck, string disposeMethodFullName, KnownTypeCode disposeTypeCode) { numObjVarLoadsInCheck = 2; ILInstruction disposeInvocation; CallInstruction disposeCall; if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) { if (checkInst.MatchIfInstruction(out var condition, out var disposeInst)) { if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar)) return false; if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) return false; disposeInvocation = disposeBlock.Instructions[0]; } else if (checkInst.MatchNullableRewrap(out disposeInst)) { disposeInvocation = disposeInst; } else { return false; } if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { if (!UnwrapAwait(ref disposeInvocation)) return false; } disposeCall = disposeInvocation as CallVirt; if (disposeCall == null) return false; if (disposeCall.Method.FullName != disposeMethodFullName) return false; if (disposeCall.Method.Parameters.Count > 0) return false; if (disposeCall.Arguments.Count != 1) return false; var firstArg = disposeCall.Arguments.FirstOrDefault(); if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(disposeTypeCode))) { if (!firstArg.MatchAddressOf(out var innerArg2, out _)) return false; return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) || (innerArg2 is NullableUnwrap unwrap && unwrap.Argument.MatchLdLoc(objVar)); } else { if (!(innerArg1.MatchBox(out firstArg, out var boxType) && boxType.IsKnownType(KnownTypeCode.NullableOfT) && NullableType.GetUnderlyingType(boxType).Equals(NullableType.GetUnderlyingType(objVar.Type)))) return false; return firstArg.MatchLdLoc(objVar); } } else { ILInstruction target; bool boxedValue = false; if (isReference && checkInst is NullableRewrap rewrap) { // the null check of reference types might have been transformed into "objVar?.Dispose();" if (!(rewrap.Argument is CallVirt cv)) return false; target = cv.Arguments.FirstOrDefault(); if (target is LdObjIfRef ldObjIfRef) target = ldObjIfRef.Target; if (!(target is NullableUnwrap unwrap)) return false; numObjVarLoadsInCheck = 1; disposeCall = cv; target = unwrap.Argument; } else if (isReference) { // reference types have a null check. if (!checkInst.MatchIfInstruction(out var condition, out var disposeInst)) return false; if (!MatchNullCheckOrTypeCheck(condition, ref objVar, disposeTypeCode, out var isInlinedIsInst)) return false; if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) return false; disposeInvocation = disposeBlock.Instructions[0]; if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { if (!UnwrapAwait(ref disposeInvocation)) return false; } if (!(disposeInvocation is CallVirt cv)) return false; target = cv.Arguments.FirstOrDefault(); if (target == null) return false; if (target is LdObjIfRef ldObjIfRef) target = ldObjIfRef.Target; if (target.MatchBox(out var newTarget, out var type) && type.Equals(objVar.Type)) target = newTarget; else if (isInlinedIsInst && target.MatchIsInst(out newTarget, out type) && type.IsKnownType(disposeTypeCode)) target = newTarget; disposeCall = cv; } else if (objVar.Type.Kind == TypeKind.Struct && objVar.Type.IsByRefLike) { if (!(checkInst is Call call && call.Method.DeclaringType == objVar.Type)) return false; target = call.Arguments.FirstOrDefault(); if (target == null) return false; if (call.Method.Name != "Dispose") return false; disposeMethodFullName = call.Method.FullName; disposeCall = call; } else { if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { if (!UnwrapAwait(ref checkInst)) return false; } if (!(checkInst is CallInstruction cv)) return false; target = cv.Arguments.FirstOrDefault(); if (target == null) return false; if (target.MatchBox(out var newTarget, out var type) && type.Equals(objVar.Type)) { boxedValue = type.IsReferenceType != true; target = newTarget; } disposeCall = cv; } if (disposeCall.Method.IsStatic) return false; if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { if (disposeCall.Method.Name != "DisposeAsync") return false; } else { if (disposeCall.Method.FullName != disposeMethodFullName) return false; } if (disposeCall.Method.Parameters.Count > 0) return false; if (disposeCall.Arguments.Count != 1) return false; return target.MatchLdLocRef(objVar) || (boxedValue && target.MatchLdLoc(objVar)) || (usingNull && disposeCall.Arguments[0].MatchLdNull()) || (isReference && checkInst is NullableRewrap && target.MatchIsInst(out var arg, out var type2) && arg.MatchLdLoc(objVar) && type2.IsKnownType(disposeTypeCode)); } } bool MatchNullCheckOrTypeCheck(ILInstruction condition, ref ILVariable objVar, KnownTypeCode disposeType, out bool isInlinedIsInst) { isInlinedIsInst = false; if (condition.MatchCompNotEquals(out var left, out var right)) { if (left.MatchStLoc(out var inlineAssignVar, out var inlineAssignVal)) { if (!inlineAssignVal.MatchIsInst(out var arg, out var type) || !type.IsKnownType(disposeType)) return false; if (!inlineAssignVar.IsSingleDefinition || inlineAssignVar.LoadCount != 1) return false; if (!inlineAssignVar.Type.IsKnownType(disposeType)) return false; isInlinedIsInst = true; left = arg; if (!left.MatchLdLoc(objVar) || !right.MatchLdNull()) return false; objVar = inlineAssignVar; return true; } else if (left.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType)) { isInlinedIsInst = true; left = arg; } if (!left.MatchLdLoc(objVar) || !right.MatchLdNull()) return false; return true; } if (condition is MatchInstruction { CheckNotNull: true, CheckType: true, TestedOperand: LdLoc { Variable: var v }, Variable: var newObjVar }) { if (v != objVar) return false; if (!newObjVar.Type.IsKnownType(disposeType)) return false; objVar = newObjVar; return true; } return false; } /// /// stloc test(resourceExpression) /// .try BlockContainer { /// Block IL_002b (incoming: 1) { /// call Use(ldloc test) /// leave IL_002b (nop) /// } /// /// } finally BlockContainer { /// Block IL_0045 (incoming: 1) { /// if (comp.o(ldloc test == ldnull)) leave IL_0045 (nop) /// br IL_00ae /// } /// /// Block IL_00ae (incoming: 1) { /// await(addressof System.Threading.Tasks.ValueTask(callvirt DisposeAsync(ldloc test))) /// leave IL_0045 (nop) /// } /// /// } /// private bool TransformAsyncUsing(Block block, int i) { if (!context.Settings.AsyncUsingAndForEachStatement) return false; if (i < 1 || i >= block.Instructions.Count) return false; if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst)) return false; if (!CheckAsyncResourceType(storeInst.Variable.Type, out string disposeMethodFullName)) return false; if (storeInst.Variable.Kind != VariableKind.Local) return false; if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) return false; if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) return false; if (storeInst.Variable.StoreInstructions.Count > 1) return false; if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, usingNull: false, disposeMethodFullName, KnownTypeCode.IAsyncDisposable)) return false; context.Step("AsyncUsingTransform", tryFinally); storeInst.Variable.Kind = VariableKind.UsingLocal; block.Instructions.RemoveAt(i); block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock) { IsAsync = true } .WithILRange(storeInst); return true; } bool CheckAsyncResourceType(IType type, out string disposeMethodFullName) { disposeMethodFullName = null; IType t = NullableType.GetUnderlyingType(type); if (t.GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IAsyncDisposable))) { disposeMethodFullName = "System.IAsyncDisposable.DisposeAsync"; return true; } IMethod disposeMethod = t .GetMethods(m => m.Parameters.Count == 0 && m.TypeParameters.Count == 0 && m.Name == "DisposeAsync") .SingleOrDefault(); if (disposeMethod != null) { disposeMethodFullName = disposeMethod.FullName; return true; } return false; } bool UnwrapAwait(ref ILInstruction awaitInstruction) { if (awaitInstruction == null) return false; if (!awaitInstruction.MatchAwait(out var arg)) return false; if (arg.MatchAddressOf(out var awaitInstructionInAddressOf, out var type)) { awaitInstruction = awaitInstructionInAddressOf; } else { awaitInstruction = arg; } return true; } } } ================================================ FILE: ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs ================================================ // Copyright (c) 2021 Christoph Wille // // 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.Diagnostics.Tracing; namespace ICSharpCode.Decompiler.Instrumentation { [EventSource(Name = "ICSharpCode.Decompiler")] public sealed class DecompilerEventSource : EventSource { [Event(1, Level = EventLevel.Informational)] public void DoDecompileEvent(string eventName, long elapsedMilliseconds) { WriteEvent(1, eventName, elapsedMilliseconds); } [Event(2, Level = EventLevel.Informational)] public void DoDecompileProperty(string propertyName, long elapsedMilliseconds) { WriteEvent(2, propertyName, elapsedMilliseconds); } [Event(3, Level = EventLevel.Informational)] public void DoDecompileField(string fieldName, long elapsedMilliseconds) { WriteEvent(3, fieldName, elapsedMilliseconds); } [Event(4, Level = EventLevel.Informational)] public void DoDecompileTypeDefinition(string typeDefName, long elapsedMilliseconds) { WriteEvent(4, typeDefName, elapsedMilliseconds); } [Event(5, Level = EventLevel.Informational)] public void DoDecompileMethod(string methodName, long elapsedMilliseconds) { WriteEvent(5, methodName, elapsedMilliseconds); } public static DecompilerEventSource Log = new DecompilerEventSource(); } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs ================================================ // 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. #nullable enable using System; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Metadata { public sealed class ResolutionException : Exception { public IAssemblyReference? Reference { get; } public string? ModuleName { get; } public string? MainModuleFullPath { get; } public string? ResolvedFullPath { get; } public ResolutionException(IAssemblyReference reference, string? resolvedPath, Exception? innerException) : base($"Failed to resolve assembly: '{reference}'{Environment.NewLine}" + $"Resolve result: {resolvedPath ?? ""}", innerException) { this.Reference = reference ?? throw new ArgumentNullException(nameof(reference)); this.ResolvedFullPath = resolvedPath; } public ResolutionException(string mainModule, string moduleName, string? resolvedPath, Exception? innerException) : base($"Failed to resolve module: '{moduleName} of {mainModule}'{Environment.NewLine}" + $"Resolve result: {resolvedPath ?? ""}", innerException) { this.MainModuleFullPath = mainModule ?? throw new ArgumentNullException(nameof(mainModule)); this.ModuleName = moduleName ?? throw new ArgumentNullException(nameof(moduleName)); this.ResolvedFullPath = resolvedPath; } } public interface IAssemblyResolver { #if !VSADDIN MetadataFile? Resolve(IAssemblyReference reference); MetadataFile? ResolveModule(MetadataFile mainModule, string moduleName); Task ResolveAsync(IAssemblyReference reference); Task ResolveModuleAsync(MetadataFile mainModule, string moduleName); #endif } public class AssemblyReferenceClassifier { /// /// For GAC assembly references, the WholeProjectDecompiler will omit the HintPath in the /// generated .csproj file. /// public virtual bool IsGacAssembly(IAssemblyReference reference) { return UniversalAssemblyResolver.GetAssemblyInGac(reference) != null; } /// /// For .NET Core framework references, the WholeProjectDecompiler will omit the /// assembly reference if the runtimePack is already included as an SDK. /// public virtual bool IsSharedAssembly(IAssemblyReference reference, [NotNullWhen(true)] out string? runtimePack) { runtimePack = null; return false; } } public interface IAssemblyReference { string Name { get; } string FullName { get; } Version? Version { get; } string? Culture { get; } byte[]? PublicKeyToken { get; } bool IsWindowsRuntime { get; } bool IsRetargetable { get; } } public class AssemblyNameReference : IAssemblyReference { string? fullName; public string Name { get; private set; } = string.Empty; public string FullName { get { if (fullName != null) return fullName; const string sep = ", "; var builder = new StringBuilder(); builder.Append(Name); builder.Append(sep); builder.Append("Version="); builder.Append((Version ?? UniversalAssemblyResolver.ZeroVersion).ToString(fieldCount: 4)); builder.Append(sep); builder.Append("Culture="); builder.Append(string.IsNullOrEmpty(Culture) ? "neutral" : Culture); builder.Append(sep); builder.Append("PublicKeyToken="); var pk_token = PublicKeyToken; if (pk_token != null && pk_token.Length > 0) { for (int i = 0; i < pk_token.Length; i++) { builder.Append(pk_token[i].ToString("x2")); } } else builder.Append("null"); if (IsRetargetable) { builder.Append(sep); builder.Append("Retargetable=Yes"); } return fullName = builder.ToString(); } } public Version? Version { get; private set; } public string? Culture { get; private set; } public byte[]? PublicKeyToken { get; private set; } public bool IsWindowsRuntime { get; private set; } public bool IsRetargetable { get; private set; } public static AssemblyNameReference Parse(string fullName) { if (fullName == null) throw new ArgumentNullException(nameof(fullName)); if (fullName.Length == 0) throw new ArgumentException("Name can not be empty"); var name = new AssemblyNameReference(); var tokens = fullName.Split(','); for (int i = 0; i < tokens.Length; i++) { var token = tokens[i].Trim(); if (i == 0) { name.Name = token; continue; } var parts = token.Split('='); if (parts.Length != 2) throw new ArgumentException("Malformed name"); switch (parts[0].ToLowerInvariant()) { case "version": name.Version = new Version(parts[1]); break; case "culture": name.Culture = parts[1] == "neutral" ? "" : parts[1]; break; case "publickeytoken": var pk_token = parts[1]; if (pk_token == "null") break; name.PublicKeyToken = new byte[pk_token.Length / 2]; for (int j = 0; j < name.PublicKeyToken.Length; j++) name.PublicKeyToken[j] = Byte.Parse(pk_token.Substring(j * 2, 2), System.Globalization.NumberStyles.HexNumber); break; } } return name; } public override string ToString() { return FullName; } } #if !VSADDIN public class AssemblyReference : IAssemblyReference { readonly System.Reflection.Metadata.AssemblyReference entry; public MetadataReader Metadata { get; } public AssemblyReferenceHandle Handle { get; } public bool IsWindowsRuntime => (entry.Flags & AssemblyFlags.WindowsRuntime) != 0; public bool IsRetargetable => (entry.Flags & AssemblyFlags.Retargetable) != 0; string? name; string? fullName; public string Name { get { if (name == null) { try { name = Metadata.GetString(entry.Name); } catch (BadImageFormatException) { name = $"AR:{Handle}"; } } return name; } } public string FullName { get { if (fullName == null) { try { fullName = entry.GetFullAssemblyName(Metadata); } catch (BadImageFormatException) { fullName = $"fullname(AR:{Handle})"; } } return fullName; } } public Version? Version => entry.Version; public string Culture => Metadata.GetString(entry.Culture); byte[]? IAssemblyReference.PublicKeyToken => GetPublicKeyToken(); byte[]? publicKeyToken; public byte[]? GetPublicKeyToken() { if (entry.PublicKeyOrToken.IsNil) return null; if (publicKeyToken == null) { var bytes = Metadata.GetBlobBytes(entry.PublicKeyOrToken); if ((entry.Flags & AssemblyFlags.PublicKey) != 0) { using (var hasher = SHA1.Create()) { bytes = hasher.ComputeHash(bytes).Skip(12).ToArray(); } } publicKeyToken = bytes; } return publicKeyToken; } ImmutableArray typeReferences; public ImmutableArray TypeReferences { get { var value = typeReferences; if (value.IsDefault) { value = Metadata.TypeReferences .Select(r => new TypeReferenceMetadata(Metadata, r)) .Where(r => r.ResolutionScope == Handle) .OrderBy(r => r.Namespace) .ThenBy(r => r.Name) .ToImmutableArray(); typeReferences = value; } return value; } } ImmutableArray exportedTypes; public ImmutableArray ExportedTypes { get { var value = exportedTypes; if (value.IsDefault) { value = Metadata.ExportedTypes .Select(r => new ExportedTypeMetadata(Metadata, r)) .Where(r => r.Implementation == Handle) .OrderBy(r => r.Namespace) .ThenBy(r => r.Name) .ToImmutableArray(); exportedTypes = value; } return value; } } public AssemblyReference(MetadataReader metadata, AssemblyReferenceHandle handle) { if (metadata == null) throw new ArgumentNullException(nameof(metadata)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Metadata = metadata; Handle = handle; entry = metadata.GetAssemblyReference(handle); } public AssemblyReference(MetadataFile module, AssemblyReferenceHandle handle) { if (module == null) throw new ArgumentNullException(nameof(module)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Metadata = module.Metadata; Handle = handle; entry = Metadata.GetAssemblyReference(handle); } public override string ToString() { return FullName; } } #endif } ================================================ FILE: ICSharpCode.Decompiler/Metadata/CodeMappingInfo.cs ================================================ // 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.Collections.Generic; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { /// /// Describes which parts of the (compiler-generated) code belong to which user code. /// A part could be: /// - the body (method) of a lambda. /// - the MoveNext method of async/yield state machines. /// public class CodeMappingInfo { /// /// The module containing the code. /// public MetadataFile Module { get; } /// /// The (parent) TypeDef containing the code. /// public TypeDefinitionHandle TypeDefinition { get; } readonly Dictionary> parts; readonly Dictionary parents; /// /// Creates a instance using the given and . /// public CodeMappingInfo(MetadataFile module, TypeDefinitionHandle type) { this.Module = module; this.TypeDefinition = type; this.parts = new Dictionary>(); this.parents = new Dictionary(); } /// /// Returns all parts of a method. /// A method has at least one part, that is, the method itself. /// If no parts are found, only the method itself is returned. /// public IEnumerable GetMethodParts(MethodDefinitionHandle method) { if (parts.TryGetValue(method, out var p)) return p; return new[] { method }; } /// /// Returns the parent of a part. /// The parent is usually the "calling method" of lambdas, async and yield state machines. /// The "calling method" has itself as parent. /// If no parent is found, the method itself is returned. /// public MethodDefinitionHandle GetParentMethod(MethodDefinitionHandle method) { if (parents.TryGetValue(method, out var p)) return p; return method; } /// /// Adds a bidirectional mapping between and . /// public void AddMapping(MethodDefinitionHandle parent, MethodDefinitionHandle part) { //Debug.Print("Parent: " + MetadataTokens.GetRowNumber(parent) + " Part: " + MetadataTokens.GetRowNumber(part)); if (parents.ContainsKey(part)) return; parents.Add(part, parent); if (!parts.TryGetValue(parent, out var list)) { list = new List(); parts.Add(parent, list); } list.Add(part); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Immutable; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { /// /// Decodes custom attribute blobs. /// internal readonly struct CustomAttributeDecoder { // This is a stripped-down copy of SRM's internal CustomAttributeDecoder. // We need it to decode security declarations. private readonly ICustomAttributeTypeProvider _provider; private readonly MetadataReader _reader; private readonly bool _provideBoxingTypeInfo; public CustomAttributeDecoder(ICustomAttributeTypeProvider provider, MetadataReader reader, bool provideBoxingTypeInfo = false) { _reader = reader; _provider = provider; _provideBoxingTypeInfo = provideBoxingTypeInfo; } public ImmutableArray> DecodeNamedArguments(ref BlobReader valueReader, int count) { var arguments = ImmutableArray.CreateBuilder>(count); for (int i = 0; i < count; i++) { CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadSerializationTypeCode(); if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property) { throw new BadImageFormatException(); } ArgumentTypeInfo info = DecodeNamedArgumentType(ref valueReader); string name = valueReader.ReadSerializedString(); CustomAttributeTypedArgument argument = DecodeArgument(ref valueReader, info); arguments.Add(new CustomAttributeNamedArgument(name, kind, argument.Type, argument.Value)); } return arguments.MoveToImmutable(); } private struct ArgumentTypeInfo { public TType Type; public TType ElementType; public SerializationTypeCode TypeCode; public SerializationTypeCode ElementTypeCode; } private ArgumentTypeInfo DecodeNamedArgumentType(ref BlobReader valueReader, bool isElementType = false) { var info = new ArgumentTypeInfo { TypeCode = valueReader.ReadSerializationTypeCode(), }; switch (info.TypeCode) { case SerializationTypeCode.Boolean: case SerializationTypeCode.Byte: case SerializationTypeCode.Char: case SerializationTypeCode.Double: case SerializationTypeCode.Int16: case SerializationTypeCode.Int32: case SerializationTypeCode.Int64: case SerializationTypeCode.SByte: case SerializationTypeCode.Single: case SerializationTypeCode.String: case SerializationTypeCode.UInt16: case SerializationTypeCode.UInt32: case SerializationTypeCode.UInt64: info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)info.TypeCode); break; case SerializationTypeCode.Type: info.Type = _provider.GetSystemType(); break; case SerializationTypeCode.TaggedObject: info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object); break; case SerializationTypeCode.SZArray: if (isElementType) { // jagged arrays are not allowed. throw new BadImageFormatException(); } var elementInfo = DecodeNamedArgumentType(ref valueReader, isElementType: true); info.ElementType = elementInfo.Type; info.ElementTypeCode = elementInfo.TypeCode; info.Type = _provider.GetSZArrayType(info.ElementType); break; case SerializationTypeCode.Enum: string typeName = valueReader.ReadSerializedString(); info.Type = _provider.GetTypeFromSerializedName(typeName); info.TypeCode = (SerializationTypeCode)_provider.GetUnderlyingEnumType(info.Type); break; default: throw new BadImageFormatException(); } return info; } private CustomAttributeTypedArgument DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info) { var outer = info; if (info.TypeCode == SerializationTypeCode.TaggedObject) { info = DecodeNamedArgumentType(ref valueReader); } // PERF_TODO: https://github.com/dotnet/corefx/issues/6533 // Cache /reuse common arguments to avoid boxing (small integers, true, false). object value; switch (info.TypeCode) { case SerializationTypeCode.Boolean: value = valueReader.ReadBoolean(); break; case SerializationTypeCode.Byte: value = valueReader.ReadByte(); break; case SerializationTypeCode.Char: value = valueReader.ReadChar(); break; case SerializationTypeCode.Double: value = valueReader.ReadDouble(); break; case SerializationTypeCode.Int16: value = valueReader.ReadInt16(); break; case SerializationTypeCode.Int32: value = valueReader.ReadInt32(); break; case SerializationTypeCode.Int64: value = valueReader.ReadInt64(); break; case SerializationTypeCode.SByte: value = valueReader.ReadSByte(); break; case SerializationTypeCode.Single: value = valueReader.ReadSingle(); break; case SerializationTypeCode.UInt16: value = valueReader.ReadUInt16(); break; case SerializationTypeCode.UInt32: value = valueReader.ReadUInt32(); break; case SerializationTypeCode.UInt64: value = valueReader.ReadUInt64(); break; case SerializationTypeCode.String: value = valueReader.ReadSerializedString(); break; case SerializationTypeCode.Type: string typeName = valueReader.ReadSerializedString(); value = _provider.GetTypeFromSerializedName(typeName); break; case SerializationTypeCode.SZArray: value = DecodeArrayArgument(ref valueReader, info); break; default: throw new BadImageFormatException(); } if (_provideBoxingTypeInfo && outer.TypeCode == SerializationTypeCode.TaggedObject) { return new CustomAttributeTypedArgument(outer.Type, new CustomAttributeTypedArgument(info.Type, value)); } return new CustomAttributeTypedArgument(info.Type, value); } private ImmutableArray>? DecodeArrayArgument(ref BlobReader blobReader, ArgumentTypeInfo info) { int count = blobReader.ReadInt32(); if (count == -1) { return null; } if (count == 0) { return ImmutableArray>.Empty; } if (count < 0) { throw new BadImageFormatException(); } var elementInfo = new ArgumentTypeInfo { Type = info.ElementType, TypeCode = info.ElementTypeCode, }; var array = ImmutableArray.CreateBuilder>(count); for (int i = 0; i < count; i++) { array.Add(DecodeArgument(ref blobReader, elementInfo)); } return array.MoveToImmutable(); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs ================================================ // 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.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using ICSharpCode.Decompiler.Util; using LightJson.Serialization; namespace ICSharpCode.Decompiler.Metadata { public class DotNetCorePathFinder { class DotNetCorePackageInfo { public readonly string Name; public readonly string Version; public readonly string Type; public readonly string Path; public readonly string[] RuntimeComponents; public DotNetCorePackageInfo(string fullName, string type, string path, string[] runtimeComponents) { var parts = fullName.Split('/'); this.Name = parts[0]; if (parts.Length > 1) { this.Version = parts[1]; } else { this.Version = ""; } this.Type = type; this.Path = path; this.RuntimeComponents = runtimeComponents ?? Empty.Array; } } static readonly string[] LookupPaths = new string[] { Environment.GetEnvironmentVariable("NUGET_PACKAGES"), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages") }; static readonly string[] RuntimePacks = new[] { "Microsoft.NETCore.App", "Microsoft.WindowsDesktop.App", "Microsoft.AspNetCore.App", "Microsoft.AspNetCore.All" }; readonly DotNetCorePackageInfo[] packages; readonly List searchPaths = new List(); readonly List packageBasePaths = new List(); readonly Version targetFrameworkVersion; readonly string dotnetBasePath = FindDotNetExeDirectory(); readonly string preferredRuntimePack; public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, string preferredRuntimePack) { this.targetFrameworkVersion = targetFrameworkVersion; this.preferredRuntimePack = preferredRuntimePack; if (targetFramework == TargetFrameworkIdentifier.NETStandard) { // .NET Standard 2.1 is implemented by .NET Core 3.0 or higher if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) { this.targetFrameworkVersion = new Version(3, 0, 0); } } } public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, string preferredRuntimePack, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null) : this(targetFramework, targetFrameworkVersion, preferredRuntimePack) { string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName); string basePath = Path.GetDirectoryName(parentAssemblyFileName); searchPaths.Add(basePath); var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json"); if (File.Exists(depsJsonFileName)) { packages = LoadPackageInfos(depsJsonFileName, targetFrameworkIdString).ToArray(); foreach (var path in LookupPaths) { if (string.IsNullOrWhiteSpace(path)) { continue; } foreach (var p in packages) { foreach (var item in p.RuntimeComponents) { var itemPath = Path.GetDirectoryName(item); var fullPath = Path.Combine(path, p.Name, p.Version, itemPath).ToLowerInvariant(); if (Directory.Exists(fullPath)) packageBasePaths.Add(fullPath); } } } } else { loadInfo?.AddMessage(assemblyName, MessageKind.Warning, $"{assemblyName}.deps.json could not be found!"); } } public void AddSearchDirectory(string path) { this.searchPaths.Add(path); } public void RemoveSearchDirectory(string path) { this.searchPaths.Remove(path); } public string TryResolveDotNetCore(IAssemblyReference name) { foreach (var basePath in searchPaths.Concat(packageBasePaths)) { if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) { return Path.Combine(basePath, name.Name + ".dll"); } else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) { return Path.Combine(basePath, name.Name + ".exe"); } } return TryResolveDotNetCoreShared(name, out _); } internal string GetReferenceAssemblyPath(string targetFramework) { var (tfi, version) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework); string identifier, identifierExt; switch (tfi) { case TargetFrameworkIdentifier.NETCoreApp: identifier = "Microsoft.NETCore.App"; identifierExt = "netcoreapp" + version.Major + "." + version.Minor; break; case TargetFrameworkIdentifier.NETStandard: identifier = "NETStandard.Library"; identifierExt = "netstandard" + version.Major + "." + version.Minor; break; case TargetFrameworkIdentifier.NET: identifier = "Microsoft.NETCore.App"; identifierExt = "net" + version.Major + "." + version.Minor; break; default: throw new NotSupportedException(); } string basePath = Path.Combine(dotnetBasePath, "packs", identifier + ".Ref"); string versionFolder = GetClosestVersionFolder(basePath, version); return Path.Combine(basePath, versionFolder, "ref", identifierExt); } static IEnumerable LoadPackageInfos(string depsJsonFileName, string targetFramework) { var dependencies = JsonReader.Parse(File.ReadAllText(depsJsonFileName)); var runtimeInfos = dependencies["targets"][targetFramework].AsJsonObject; var libraries = dependencies["libraries"].AsJsonObject; if (runtimeInfos == null || libraries == null) yield break; foreach (var library in libraries) { var type = library.Value["type"].AsString; var path = library.Value["path"].AsString; var runtimeInfo = runtimeInfos[library.Key].AsJsonObject?["runtime"].AsJsonObject; string[] components = new string[runtimeInfo?.Count ?? 0]; if (runtimeInfo != null) { int i = 0; foreach (var component in runtimeInfo) { components[i] = component.Key; i++; } } yield return new DotNetCorePackageInfo(library.Key, type, path, components); } } public string TryResolveDotNetCoreShared(IAssemblyReference name, out string runtimePack) { if (dotnetBasePath == null) { runtimePack = null; return null; } IEnumerable runtimePacks = RuntimePacks; if (preferredRuntimePack != null) { runtimePacks = new[] { preferredRuntimePack }.Concat(runtimePacks); } foreach (string pack in runtimePacks) { runtimePack = pack; string basePath = Path.Combine(dotnetBasePath, "shared", pack); if (!Directory.Exists(basePath)) continue; var closestVersion = GetClosestVersionFolder(basePath, targetFrameworkVersion); if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) { return Path.Combine(basePath, closestVersion, name.Name + ".dll"); } else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) { return Path.Combine(basePath, closestVersion, name.Name + ".exe"); } } runtimePack = null; return null; } static string GetClosestVersionFolder(string basePath, Version version) { var foundVersions = new DirectoryInfo(basePath).GetDirectories() .Select(ConvertToVersion) .Where(v => v.version != null); foreach (var folder in foundVersions.OrderBy(v => v.version)) { if (folder.version >= version && folder.directory.EnumerateFiles("*.dll", SearchOption.AllDirectories).Any()) { return folder.directory.Name; } } return version.ToString(); } internal static (Version version, DirectoryInfo directory) ConvertToVersion(DirectoryInfo directory) { string RemoveTrailingVersionInfo() { string shortName = directory.Name; int dashIndex = shortName.IndexOf('-'); if (dashIndex > 0) { shortName = shortName.Remove(dashIndex); } return shortName; } try { return (new Version(RemoveTrailingVersionInfo()), directory); } catch (Exception ex) { Trace.TraceWarning(ex.ToString()); return (null, null); } } public static string FindDotNetExeDirectory() { string dotnetExeName = (Environment.OSVersion.Platform == PlatformID.Unix) ? "dotnet" : "dotnet.exe"; foreach (var item in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator)) { try { string fileName = Path.Combine(item, dotnetExeName); if (!File.Exists(fileName)) continue; if (Environment.OSVersion.Platform == PlatformID.Unix) { if ((new FileInfo(fileName).Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { fileName = GetRealPath(fileName, Encoding.Default); if (!File.Exists(fileName)) continue; } } return Path.GetDirectoryName(fileName); } catch (ArgumentException) { } } return null; } static unsafe string GetRealPath(string path, Encoding encoding) { var bytes = encoding.GetBytes(path); fixed (byte* input = bytes) { byte* output = GetRealPath(input, null); if (output == null) { return null; } int len = 0; for (byte* c = output; *c != 0; c++) { len++; } byte[] result = new byte[len]; Marshal.Copy((IntPtr)output, result, 0, result.Length); Free(output); return encoding.GetString(result); } } [DllImport("libc", EntryPoint = "realpath")] static extern unsafe byte* GetRealPath(byte* path, byte* resolvedPath); [DllImport("libc", EntryPoint = "free")] static extern unsafe void Free(void* ptr); } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs ================================================ // 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.Reflection.Metadata; using System.Text.RegularExpressions; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Metadata { public static class DotNetCorePathFinderExtensions { static readonly string PathPattern = @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?.NETFramework)[/\\]v(?[^/\\]+)[/\\])" + @"|((?Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" + @"|((?Microsoft\.NET)[/\\]Framework(64)?[/\\](?[^/\\]+)[/\\])" + @"|(NuGetFallbackFolder[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + @"|(shared[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\])" + @"|(packs[/\\](?[^/\\]+)\\(?[^/\\]+)\\ref([/\\].*)?[/\\])"; static readonly string RefPathPattern = @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?.NETFramework)[/\\]v(?[^/\\]+)[/\\])" + @"|(NuGetFallbackFolder[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + @"|(packs[/\\](?[^/\\]+)\\(?[^/\\]+)\\ref([/\\].*)?[/\\])"; public static string DetectTargetFrameworkId(this MetadataFile assembly) { return DetectTargetFrameworkId(assembly.Metadata, assembly.FileName); } public static string DetectTargetFrameworkId(this MetadataReader metadata, string assemblyPath = null) { if (metadata == null) throw new ArgumentNullException(nameof(metadata)); const string TargetFrameworkAttributeName = "System.Runtime.Versioning.TargetFrameworkAttribute"; foreach (var h in metadata.GetCustomAttributes(Handle.AssemblyDefinition)) { try { var attribute = metadata.GetCustomAttribute(h); if (attribute.GetAttributeType(metadata).GetFullTypeName(metadata).ToString() != TargetFrameworkAttributeName) continue; var blobReader = metadata.GetBlobReader(attribute.Value); if (blobReader.ReadUInt16() == 0x0001) { return blobReader.ReadSerializedString()?.Replace(" ", ""); } } catch (BadImageFormatException) { // ignore malformed attributes } } if (metadata.IsAssembly) { AssemblyDefinition assemblyDefinition = metadata.GetAssemblyDefinition(); switch (metadata.GetString(assemblyDefinition.Name)) { case "mscorlib": return $".NETFramework,Version=v{assemblyDefinition.Version.ToString(2)}"; case "netstandard": return $".NETStandard,Version=v{assemblyDefinition.Version.ToString(2)}"; case "System.Runtime": case "System.Private.CoreLib": { string version = GetDotNetCoreVersion(assemblyDefinition.Version); if (version != null) { return $".NETCoreApp,Version=v{version}"; } else { break; } } } } foreach (var h in metadata.AssemblyReferences) { try { var r = metadata.GetAssemblyReference(h); if (r.PublicKeyOrToken.IsNil) continue; string version; switch (metadata.GetString(r.Name)) { case "mscorlib": version = r.Version.ToString(2); return $".NETFramework,Version=v{version}"; case "System.Runtime": case "System.Private.CoreLib": version = GetDotNetCoreVersion(r.Version); if (version != null) { return $".NETCoreApp,Version=v{version}"; } else { continue; } } } catch (BadImageFormatException) { // ignore malformed references } } // We check for netstandard separately because .NET Core/Framework assemblies can reference it. foreach (var h in metadata.AssemblyReferences) { try { var r = metadata.GetAssemblyReference(h); if (r.PublicKeyOrToken.IsNil || metadata.GetString(r.Name) is not "netstandard") continue; string version = r.Version.ToString(2); return $".NETStandard,Version=v{version}"; } catch (BadImageFormatException) { // ignore malformed references } } // Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies) if (assemblyPath != null) { /* * Detected path patterns (examples): * * - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll * - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll * - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll */ var pathMatch = Regex.Match(assemblyPath, PathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); string version; if (pathMatch.Success) { var type = pathMatch.Groups["type"].Value; version = pathMatch.Groups["version"].Value; if (string.IsNullOrEmpty(version)) version = metadata.MetadataVersion; if (string.IsNullOrEmpty(version)) version = "4.0"; version = version.TrimStart('v'); if (type == "Microsoft.NET" || type == ".NETFramework") { return $".NETFramework,Version=v{version.Substring(0, Math.Min(3, version.Length))}"; } else if (type.IndexOf("netcore", StringComparison.OrdinalIgnoreCase) >= 0) { return $".NETCoreApp,Version=v{version}"; } else if (type.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) >= 0) { return $".NETStandard,Version=v{version}"; } } else { version = metadata.MetadataVersion; if (string.IsNullOrEmpty(version)) version = "4.0"; version = version.TrimStart('v'); return $".NETFramework,Version=v{version.Substring(0, Math.Min(3, version.Length))}"; } } return string.Empty; } static string GetDotNetCoreVersion(Version assemblyVersion) { // System.Runtime.dll and System.Private.CoreLib.dll use the following scheme: // 4.1.0 => .NET Core 1.0 / 1.1 // 4.2.0 => .NET Core 2.0 // 4.2.1 => .NET Core 2.1 / 3.0 // 4.2.2 => .NET Core 3.1 // 5.0.0+ => .NET 5+ return (assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build) switch { (4, 1, 0) => "1.1", (4, 2, 0) => "2.0", (4, 2, 1) => "3.0", (4, 2, 2) => "3.1", ( >= 5, _, _) => assemblyVersion.ToString(2), _ => null }; } public static bool IsReferenceAssembly(this MetadataFile assembly) { return IsReferenceAssembly(assembly.Metadata, assembly.FileName); } public static bool IsReferenceAssembly(this MetadataReader metadata, string assemblyPath) { if (metadata == null) throw new ArgumentNullException(nameof(metadata)); if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly)) return true; // Try to detect reference assembly through specific path pattern var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); return refPathMatch.Success; } public static string DetectRuntimePack(this MetadataFile assembly) { if (assembly is null) { throw new ArgumentNullException(nameof(assembly)); } var metadata = assembly.Metadata; foreach (var r in metadata.AssemblyReferences) { var reference = metadata.GetAssemblyReference(r); if (reference.PublicKeyOrToken.IsNil) continue; if (metadata.StringComparer.Equals(reference.Name, "WindowsBase")) { return "Microsoft.WindowsDesktop.App"; } if (metadata.StringComparer.Equals(reference.Name, "PresentationFramework")) { return "Microsoft.WindowsDesktop.App"; } if (metadata.StringComparer.Equals(reference.Name, "PresentationCore")) { return "Microsoft.WindowsDesktop.App"; } // TODO add support for ASP.NET Core } return "Microsoft.NETCore.App"; } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/EnumUnderlyingTypeResolveException.cs ================================================ // 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.Runtime.Serialization; using System.Text; namespace ICSharpCode.Decompiler.Metadata { [Serializable] public class EnumUnderlyingTypeResolveException : Exception { public EnumUnderlyingTypeResolveException() { } public EnumUnderlyingTypeResolveException(string message) : base(message) { } public EnumUnderlyingTypeResolveException(string message, Exception inner) : base(message, inner) { } protected EnumUnderlyingTypeResolveException( SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public class MetadataFileNotSupportedException : Exception { public MetadataFileNotSupportedException() { } public MetadataFileNotSupportedException(string message) : base(message) { } public MetadataFileNotSupportedException(string message, Exception inner) : base(message, inner) { } protected MetadataFileNotSupportedException( SerializationInfo info, StreamingContext context) : base(info, context) { } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/ExportedTypeMetadata.cs ================================================ // Copyright (c) 2023 James May // // 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. #nullable enable using System; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { #if !VSADDIN /// /// Convenience wrapper for and . /// public sealed class ExportedTypeMetadata { readonly ExportedType entry; public MetadataReader Metadata { get; } public ExportedTypeHandle Handle { get; } string? name; public string Name { get { try { return name ??= Metadata.GetString(entry.Name); } catch (BadImageFormatException) { return name = $"ET:{Handle}"; } } } string? @namespace; public string Namespace { get { try { return @namespace ??= Metadata.GetString(entry.Namespace); } catch (BadImageFormatException) { return @namespace = $"namespace(ET:{Handle})"; } } } public EntityHandle Implementation => entry.Implementation; public TypeAttributes Attributes => entry.Attributes; public bool IsForwarder => entry.IsForwarder; public NamespaceDefinition NamespaceDefinition => Metadata.GetNamespaceDefinition(entry.NamespaceDefinition); ImmutableArray exportedTypes; public ImmutableArray ExportedTypes { get { var value = exportedTypes; if (value.IsDefault) { value = Metadata.ExportedTypes .Select(r => new ExportedTypeMetadata(Metadata, r)) .Where(r => r.Implementation == Handle) .OrderBy(r => r.Namespace) .ThenBy(r => r.Name) .ToImmutableArray(); exportedTypes = value; } return value; } } public ExportedTypeMetadata(MetadataReader metadata, ExportedTypeHandle handle) { Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Handle = handle; entry = metadata.GetExportedType(handle); } public override string ToString() => $"{Namespace}::{Name}"; } #endif } ================================================ FILE: ICSharpCode.Decompiler/Metadata/FindTypeDecoder.cs ================================================ // Copyright (c) 2022 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. #nullable enable using System; using System.Collections.Immutable; using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { public class FindTypeDecoder : ISignatureTypeProvider { readonly MetadataFile declaringModule; readonly MetadataModule? currentModule; readonly TypeDefinitionHandle handle; readonly string? typeName; readonly string? namespaceName; readonly PrimitiveTypeCode primitiveType; /// /// Constructs a FindTypeDecoder that finds uses of a specific type-definition handle. /// This assumes that the module we are search in is the same as the module containing the type-definiton. /// internal FindTypeDecoder(TypeDefinitionHandle handle, MetadataFile declaringModule) { this.handle = handle; this.declaringModule = declaringModule; this.primitiveType = 0; this.currentModule = null; } /// /// Constructs a FindTypeDecoder that can be used to find in signatures from . /// public FindTypeDecoder(MetadataModule currentModule, ITypeDefinition type) { this.currentModule = currentModule; this.declaringModule = type.ParentModule?.MetadataFile ?? throw new InvalidOperationException("Cannot use MetadataModule without PEFile as context."); this.handle = (TypeDefinitionHandle)type.MetadataToken; this.primitiveType = type.KnownTypeCode == KnownTypeCode.None ? 0 : type.KnownTypeCode.ToPrimitiveTypeCode(); this.typeName = type.MetadataName; this.namespaceName = type.Namespace; } public bool GetArrayType(bool elementType, ArrayShape shape) => elementType; public bool GetByReferenceType(bool elementType) => elementType; public bool GetFunctionPointerType(MethodSignature signature) { return AnyInMethodSignature(signature); } public static bool AnyInMethodSignature(MethodSignature signature) { if (signature.ReturnType) return true; foreach (bool type in signature.ParameterTypes) { if (type) return true; } return false; } public bool GetGenericInstantiation(bool genericType, ImmutableArray typeArguments) { if (genericType) return true; foreach (bool ta in typeArguments) { if (ta) return true; } return false; } public bool GetGenericMethodParameter(Unit genericContext, int index) => false; public bool GetGenericTypeParameter(Unit genericContext, int index) => false; public bool GetModifiedType(bool modifier, bool unmodifiedType, bool isRequired) => unmodifiedType || modifier; public bool GetPinnedType(bool elementType) => elementType; public bool GetPointerType(bool elementType) => elementType; public bool GetPrimitiveType(PrimitiveTypeCode typeCode) { return typeCode == primitiveType; } public bool GetSZArrayType(bool elementType) => elementType; public bool GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { return this.handle == handle && reader == declaringModule.Metadata; } public bool GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { if (currentModule == null || typeName == null || namespaceName == null) return false; var tr = reader.GetTypeReference(handle); if (!reader.StringComparer.Equals(tr.Name, typeName)) return false; if (!((tr.Namespace.IsNil && namespaceName.Length == 0) || reader.StringComparer.Equals(tr.Namespace, namespaceName))) return false; var t = currentModule.ResolveType(handle, default); var td = t.GetDefinition(); if (td == null) return false; return td.MetadataToken == this.handle && td.ParentModule?.MetadataFile == declaringModule; } public bool GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); } public bool GetTypeFromEntity(MetadataReader reader, EntityHandle handle, Unit genericContext = default, byte rawTypeKind = 0) { switch (handle.Kind) { case HandleKind.TypeReference: return GetTypeFromReference(reader, (TypeReferenceHandle)handle, rawTypeKind); case HandleKind.TypeDefinition: return GetTypeFromDefinition(reader, (TypeDefinitionHandle)handle, rawTypeKind); case HandleKind.TypeSpecification: return GetTypeFromSpecification(reader, genericContext, (TypeSpecificationHandle)handle, rawTypeKind); default: return false; } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/FullTypeNameSignatureDecoder.cs ================================================ // 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. #nullable enable using System; using System.Collections.Immutable; using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { public sealed class FullTypeNameSignatureDecoder : ISignatureTypeProvider, ICustomAttributeTypeProvider { readonly MetadataReader metadata; public FullTypeNameSignatureDecoder(MetadataReader metadata) { this.metadata = metadata; } public FullTypeName GetArrayType(FullTypeName elementType, ArrayShape shape) { return elementType; } public FullTypeName GetByReferenceType(FullTypeName elementType) { return elementType; } public FullTypeName GetFunctionPointerType(MethodSignature signature) { return default; } public FullTypeName GetGenericInstantiation(FullTypeName genericType, ImmutableArray typeArguments) { return genericType; } public FullTypeName GetGenericMethodParameter(Unit genericContext, int index) { return default; } public FullTypeName GetGenericTypeParameter(Unit genericContext, int index) { return default; } public FullTypeName GetModifiedType(FullTypeName modifier, FullTypeName unmodifiedType, bool isRequired) { return unmodifiedType; } public FullTypeName GetPinnedType(FullTypeName elementType) { return elementType; } public FullTypeName GetPointerType(FullTypeName elementType) { return elementType; } public FullTypeName GetPrimitiveType(PrimitiveTypeCode typeCode) { var ktr = KnownTypeReference.Get(typeCode.ToKnownTypeCode()); if (ktr == null) return default; return new TopLevelTypeName(ktr.Namespace, ktr.Name, ktr.TypeParameterCount); } public FullTypeName GetSystemType() { return new TopLevelTypeName("System", "Type"); } public FullTypeName GetSZArrayType(FullTypeName elementType) { return elementType; } public FullTypeName GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { return handle.GetFullTypeName(reader); } public FullTypeName GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { return handle.GetFullTypeName(reader); } public FullTypeName GetTypeFromSerializedName(string name) { return new FullTypeName(name); } public FullTypeName GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { return reader.GetTypeSpecification(handle).DecodeSignature(new FullTypeNameSignatureDecoder(metadata), default); } public PrimitiveTypeCode GetUnderlyingEnumType(FullTypeName type) { throw new NotImplementedException(); } public bool IsSystemType(FullTypeName type) { return type.IsKnownType(KnownTypeCode.Type); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/ILOpCodes.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { static partial class ILOpCodeExtensions { // We use a byte array instead of an enum array because it can be initialized more efficiently static readonly byte[] operandTypes = { (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.ShortVariable, (byte)OperandType.ShortVariable, (byte)OperandType.ShortVariable, (byte)OperandType.ShortVariable, (byte)OperandType.ShortVariable, (byte)OperandType.ShortVariable, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.ShortI, (byte)OperandType.I, (byte)OperandType.I8, (byte)OperandType.ShortR, (byte)OperandType.R, 255, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Method, (byte)OperandType.Method, (byte)OperandType.Sig, (byte)OperandType.None, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.BrTarget, (byte)OperandType.Switch, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Method, (byte)OperandType.Type, (byte)OperandType.Type, (byte)OperandType.String, (byte)OperandType.Method, (byte)OperandType.Type, (byte)OperandType.Type, (byte)OperandType.None, 255, 255, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.Field, (byte)OperandType.Field, (byte)OperandType.Field, (byte)OperandType.Field, (byte)OperandType.Field, (byte)OperandType.Field, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Type, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Type, (byte)OperandType.Type, (byte)OperandType.Type, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, 255, 255, 255, 255, 255, 255, 255, (byte)OperandType.Type, (byte)OperandType.None, 255, 255, (byte)OperandType.Type, 255, 255, 255, 255, 255, 255, 255, 255, 255, (byte)OperandType.Tok, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.BrTarget, (byte)OperandType.ShortBrTarget, (byte)OperandType.None, (byte)OperandType.None, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Method, (byte)OperandType.Method, 255, (byte)OperandType.Variable, (byte)OperandType.Variable, (byte)OperandType.Variable, (byte)OperandType.Variable, (byte)OperandType.Variable, (byte)OperandType.Variable, (byte)OperandType.None, 255, (byte)OperandType.None, (byte)OperandType.ShortI, (byte)OperandType.None, (byte)OperandType.None, (byte)OperandType.Type, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.None, 255, (byte)OperandType.None, 255, (byte)OperandType.Type, (byte)OperandType.None, (byte)OperandType.None, }; static readonly string[] operandNames = { "nop", "break", "ldarg.0", "ldarg.1", "ldarg.2", "ldarg.3", "ldloc.0", "ldloc.1", "ldloc.2", "ldloc.3", "stloc.0", "stloc.1", "stloc.2", "stloc.3", "ldarg.s", "ldarga.s", "starg.s", "ldloc.s", "ldloca.s", "stloc.s", "ldnull", "ldc.i4.m1", "ldc.i4.0", "ldc.i4.1", "ldc.i4.2", "ldc.i4.3", "ldc.i4.4", "ldc.i4.5", "ldc.i4.6", "ldc.i4.7", "ldc.i4.8", "ldc.i4.s", "ldc.i4", "ldc.i8", "ldc.r4", "ldc.r8", "", "dup", "pop", "jmp", "call", "calli", "ret", "br.s", "brfalse.s", "brtrue.s", "beq.s", "bge.s", "bgt.s", "ble.s", "blt.s", "bne.un.s", "bge.un.s", "bgt.un.s", "ble.un.s", "blt.un.s", "br", "brfalse", "brtrue", "beq", "bge", "bgt", "ble", "blt", "bne.un", "bge.un", "bgt.un", "ble.un", "blt.un", "switch", "ldind.i1", "ldind.u1", "ldind.i2", "ldind.u2", "ldind.i4", "ldind.u4", "ldind.i8", "ldind.i", "ldind.r4", "ldind.r8", "ldind.ref", "stind.ref", "stind.i1", "stind.i2", "stind.i4", "stind.i8", "stind.r4", "stind.r8", "add", "sub", "mul", "div", "div.un", "rem", "rem.un", "and", "or", "xor", "shl", "shr", "shr.un", "neg", "not", "conv.i1", "conv.i2", "conv.i4", "conv.i8", "conv.r4", "conv.r8", "conv.u4", "conv.u8", "callvirt", "cpobj", "ldobj", "ldstr", "newobj", "castclass", "isinst", "conv.r.un", "", "", "unbox", "throw", "ldfld", "ldflda", "stfld", "ldsfld", "ldsflda", "stsfld", "stobj", "conv.ovf.i1.un", "conv.ovf.i2.un", "conv.ovf.i4.un", "conv.ovf.i8.un", "conv.ovf.u1.un", "conv.ovf.u2.un", "conv.ovf.u4.un", "conv.ovf.u8.un", "conv.ovf.i.un", "conv.ovf.u.un", "box", "newarr", "ldlen", "ldelema", "ldelem.i1", "ldelem.u1", "ldelem.i2", "ldelem.u2", "ldelem.i4", "ldelem.u4", "ldelem.i8", "ldelem.i", "ldelem.r4", "ldelem.r8", "ldelem.ref", "stelem.i", "stelem.i1", "stelem.i2", "stelem.i4", "stelem.i8", "stelem.r4", "stelem.r8", "stelem.ref", "ldelem", "stelem", "unbox.any", "", "", "", "", "", "", "", "", "", "", "", "", "", "conv.ovf.i1", "conv.ovf.u1", "conv.ovf.i2", "conv.ovf.u2", "conv.ovf.i4", "conv.ovf.u4", "conv.ovf.i8", "conv.ovf.u8", "", "", "", "", "", "", "", "refanyval", "ckfinite", "", "", "mkrefany", "", "", "", "", "", "", "", "", "", "ldtoken", "conv.u2", "conv.u1", "conv.i", "conv.ovf.i", "conv.ovf.u", "add.ovf", "add.ovf.un", "mul.ovf", "mul.ovf.un", "sub.ovf", "sub.ovf.un", "endfinally", "leave", "leave.s", "stind.i", "conv.u", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "prefix7", "prefix6", "prefix5", "prefix4", "prefix3", "prefix2", "prefix1", "prefixref", "arglist", "ceq", "cgt", "cgt.un", "clt", "clt.un", "ldftn", "ldvirtftn", "", "ldarg", "ldarga", "starg", "ldloc", "ldloca", "stloc", "localloc", "", "endfilter", "unaligned.", "volatile.", "tail.", "initobj", "constrained.", "cpblk", "initblk", "", "rethrow", "", "sizeof", "refanytype", "readonly.", }; } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/ILOpCodes.tt ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Reflection.Metadata" #> <#@ import namespace="System.Reflection.Emit" #> <#@ output extension=".cs" #> using System; using System.Collections.Generic; using System.Reflection.Metadata; <# var operandTypes = Enumerable.Repeat((OperandType)0xff, 0x11f).ToArray(); var operandNames = new string[0x11f]; #> namespace ICSharpCode.Decompiler.Metadata { <# foreach (var field in typeof(OpCodes).GetFields()) { var opCode = (OpCode)field.GetValue(null); ushort index = (ushort)(((opCode.Value & 0x200) >> 1) | (opCode.Value & 0xff)); operandTypes[index] = opCode.OperandType; operandNames[index] = opCode.Name; } #> static partial class ILOpCodeExtensions { // We use a byte array instead of an enum array because it can be initialized more efficiently static readonly byte[] operandTypes = { <# foreach (var operandType in operandTypes) { if ((byte)operandType == 255) { Write("255, "); } else { string operandTypeName = operandType.ToString().Replace("Inline", "").Replace("Var", "Variable"); Write("(byte)OperandType." + operandTypeName + ", "); } } #> }; static readonly string[] operandNames = { <# foreach (var operandName in operandNames) { Write("\"" + operandName + "\", "); } #> }; } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/JsonArray.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson { using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; /// /// Represents an ordered collection of JsonValues. /// [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(JsonArrayDebugView))] internal sealed class JsonArray : IEnumerable { private IList items; /// /// Initializes a new instance of the class. /// public JsonArray() { this.items = new List(); } /// /// Initializes a new instance of the class, adding the given values to the collection. /// /// The values to be added to this collection. public JsonArray(params JsonValue[] values) : this() { if (values == null) { throw new ArgumentNullException(nameof(values)); } foreach (var value in values) { this.items.Add(value); } } /// /// Gets the number of values in this collection. /// /// The number of values in this collection. public int Count { get { return this.items.Count; } } /// /// Gets or sets the value at the given index. /// /// The zero-based index of the value to get or set. /// /// The getter will return JsonValue.Null if the given index is out of range. /// public JsonValue this[int index] { get { if (index >= 0 && index < this.items.Count) { return this.items[index]; } else { return JsonValue.Null; } } set { this.items[index] = value; } } /// /// Adds the given value to this collection. /// /// The value to be added. /// Returns this collection. public JsonArray Add(JsonValue value) { this.items.Add(value); return this; } /// /// Inserts the given value at the given index in this collection. /// /// The index where the given value will be inserted. /// The value to be inserted into this collection. /// Returns this collection. public JsonArray Insert(int index, JsonValue value) { this.items.Insert(index, value); return this; } /// /// Removes the value at the given index. /// /// The index of the value to be removed. /// Return this collection. public JsonArray Remove(int index) { this.items.RemoveAt(index); return this; } /// /// Clears the contents of this collection. /// /// Returns this collection. public JsonArray Clear() { this.items.Clear(); return this; } /// /// Determines whether the given item is in the JsonArray. /// /// The item to locate in the JsonArray. /// Returns true if the item is found; otherwise, false. public bool Contains(JsonValue item) { return this.items.Contains(item); } /// /// Determines the index of the given item in this JsonArray. /// /// The item to locate in this JsonArray. /// The index of the item, if found. Otherwise, returns -1. public int IndexOf(JsonValue item) { return this.items.IndexOf(item); } /// /// Returns an enumerator that iterates through the collection. /// /// The enumerator that iterates through the collection. public IEnumerator GetEnumerator() { return this.items.GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// The enumerator that iterates through the collection. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } [ExcludeFromCodeCoverage] private class JsonArrayDebugView { private JsonArray jsonArray; public JsonArrayDebugView(JsonArray jsonArray) { this.jsonArray = jsonArray; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public JsonValue[] Items { get { var items = new JsonValue[this.jsonArray.Count]; for (int i = 0; i < this.jsonArray.Count; i += 1) { items[i] = this.jsonArray[i]; } return items; } } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/JsonObject.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson { using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; /// /// Represents a key-value pair collection of JsonValue objects. /// [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(JsonObjectDebugView))] internal sealed class JsonObject : IEnumerable>, IEnumerable { private IDictionary properties; /// /// Initializes a new instance of the class. /// public JsonObject() { this.properties = new Dictionary(); } /// /// Gets the number of properties in this JsonObject. /// /// The number of properties in this JsonObject. public int Count { get { return this.properties.Count; } } /// /// Gets or sets the property with the given key. /// /// The key of the property to get or set. /// /// The getter will return JsonValue.Null if the given key is not associated with any value. /// public JsonValue this[string key] { get { JsonValue value; if (this.properties.TryGetValue(key, out value)) { return value; } else { return JsonValue.Null; } } set { this.properties[key] = value; } } /// /// Adds a key with a null value to this collection. /// /// The key of the property to be added. /// Returns this JsonObject. /// The that was added. public JsonObject Add(string key) { return this.Add(key, JsonValue.Null); } /// /// Adds a value associated with a key to this collection. /// /// The key of the property to be added. /// The value of the property to be added. /// Returns this JsonObject. public JsonObject Add(string key, JsonValue value) { this.properties.Add(key, value); return this; } /// /// Removes a property with the given key. /// /// The key of the property to be removed. /// /// Returns true if the given key is found and removed; otherwise, false. /// public bool Remove(string key) { return this.properties.Remove(key); } /// /// Clears the contents of this collection. /// /// Returns this JsonObject. public JsonObject Clear() { this.properties.Clear(); return this; } /// /// Changes the key of one of the items in the collection. /// /// /// This method has no effects if the oldKey does not exists. /// If the newKey already exists, the value will be overwritten. /// /// The name of the key to be changed. /// The new name of the key. /// Returns this JsonObject. public JsonObject Rename(string oldKey, string newKey) { if (oldKey == newKey) { // Renaming to the same name just does nothing return this; } JsonValue value; if (this.properties.TryGetValue(oldKey, out value)) { this[newKey] = value; this.Remove(oldKey); } return this; } /// /// Determines whether this collection contains an item assosiated with the given key. /// /// The key to locate in this collection. /// Returns true if the key is found; otherwise, false. public bool ContainsKey(string key) { return this.properties.ContainsKey(key); } /// /// Determines whether this collection contains the given JsonValue. /// /// The value to locate in this collection. /// Returns true if the value is found; otherwise, false. public bool Contains(JsonValue value) { return this.properties.Values.Contains(value); } /// /// Returns an enumerator that iterates through this collection. /// /// The enumerator that iterates through this collection. public IEnumerator> GetEnumerator() { return this.properties.GetEnumerator(); } /// /// Returns an enumerator that iterates through this collection. /// /// The enumerator that iterates through this collection. IEnumerator IEnumerable.GetEnumerator() { return this.properties.Values.GetEnumerator(); } /// /// Returns an enumerator that iterates through this collection. /// /// The enumerator that iterates through this collection. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } [ExcludeFromCodeCoverage] private class JsonObjectDebugView { private JsonObject jsonObject; public JsonObjectDebugView(JsonObject jsonObject) { this.jsonObject = jsonObject; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public KeyValuePair[] Keys { get { var keys = new KeyValuePair[this.jsonObject.Count]; var i = 0; foreach (var property in this.jsonObject) { keys[i] = new KeyValuePair(property.Key, property.Value); i += 1; } return keys; } } [DebuggerDisplay("{value.ToString(),nq}", Name = "{key}", Type = "JsonValue({Type})")] public class KeyValuePair { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string key; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private JsonValue value; public KeyValuePair(string key, JsonValue value) { this.key = key; this.value = value; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public object View { get { if (this.value.IsJsonObject) { return (JsonObject)this.value; } else if (this.value.IsJsonArray) { return (JsonArray)this.value; } else { return this.value; } } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private JsonValueType Type { get { return this.value.Type; } } } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/JsonValue.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson { using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using LightJson.Serialization; /// /// A wrapper object that contains a valid JSON value. /// [DebuggerDisplay("{ToString(),nq}", Type = "JsonValue({Type})")] [DebuggerTypeProxy(typeof(JsonValueDebugView))] internal struct JsonValue { /// /// Represents a null JsonValue. /// public static readonly JsonValue Null = new JsonValue(JsonValueType.Null, default(double), null); private readonly JsonValueType type; private readonly object reference; private readonly double value; /// /// Initializes a new instance of the struct, representing a Boolean value. /// /// The value to be wrapped. public JsonValue(bool? value) { if (value.HasValue) { this.reference = null; this.type = JsonValueType.Boolean; this.value = value.Value ? 1 : 0; } else { this = JsonValue.Null; } } /// /// Initializes a new instance of the struct, representing a Number value. /// /// The value to be wrapped. public JsonValue(double? value) { if (value.HasValue) { this.reference = null; this.type = JsonValueType.Number; this.value = value.Value; } else { this = JsonValue.Null; } } /// /// Initializes a new instance of the struct, representing a String value. /// /// The value to be wrapped. public JsonValue(string value) { if (value != null) { this.value = default(double); this.type = JsonValueType.String; this.reference = value; } else { this = JsonValue.Null; } } /// /// Initializes a new instance of the struct, representing a JsonObject. /// /// The value to be wrapped. public JsonValue(JsonObject value) { if (value != null) { this.value = default(double); this.type = JsonValueType.Object; this.reference = value; } else { this = JsonValue.Null; } } /// /// Initializes a new instance of the struct, representing a Array reference value. /// /// The value to be wrapped. public JsonValue(JsonArray value) { if (value != null) { this.value = default(double); this.type = JsonValueType.Array; this.reference = value; } else { this = JsonValue.Null; } } /// /// Initializes a new instance of the struct. /// /// The Json type of the JsonValue. /// /// The internal value of the JsonValue. /// This is used when the Json type is Number or Boolean. /// /// /// The internal value reference of the JsonValue. /// This value is used when the Json type is String, JsonObject, or JsonArray. /// private JsonValue(JsonValueType type, double value, object reference) { this.type = type; this.value = value; this.reference = reference; } /// /// Gets the type of this JsonValue. /// /// The type of this JsonValue. public JsonValueType Type { get { return this.type; } } /// /// Gets a value indicating whether this JsonValue is Null. /// /// A value indicating whether this JsonValue is Null. public bool IsNull { get { return this.Type == JsonValueType.Null; } } /// /// Gets a value indicating whether this JsonValue is a Boolean. /// /// A value indicating whether this JsonValue is a Boolean. public bool IsBoolean { get { return this.Type == JsonValueType.Boolean; } } /// /// Gets a value indicating whether this JsonValue is an Integer. /// /// A value indicating whether this JsonValue is an Integer. public bool IsInteger { get { if (!this.IsNumber) { return false; } var value = this.value; return unchecked((int)value) == value; } } /// /// Gets a value indicating whether this JsonValue is a Number. /// /// A value indicating whether this JsonValue is a Number. public bool IsNumber { get { return this.Type == JsonValueType.Number; } } /// /// Gets a value indicating whether this JsonValue is a String. /// /// A value indicating whether this JsonValue is a String. public bool IsString { get { return this.Type == JsonValueType.String; } } /// /// Gets a value indicating whether this JsonValue is a JsonObject. /// /// A value indicating whether this JsonValue is a JsonObject. public bool IsJsonObject { get { return this.Type == JsonValueType.Object; } } /// /// Gets a value indicating whether this JsonValue is a JsonArray. /// /// A value indicating whether this JsonValue is a JsonArray. public bool IsJsonArray { get { return this.Type == JsonValueType.Array; } } /// /// Gets a value indicating whether this JsonValue represents a DateTime. /// /// A value indicating whether this JsonValue represents a DateTime. public bool IsDateTime { get { return this.AsDateTime != null; } } /// /// Gets a value indicating whether this value is true or false. /// /// This value as a Boolean type. public bool AsBoolean { get { switch (this.Type) { case JsonValueType.Boolean: return this.value == 1; case JsonValueType.Number: return this.value != 0; case JsonValueType.String: return (string)this.reference != string.Empty; case JsonValueType.Object: case JsonValueType.Array: return true; default: return false; } } } /// /// Gets this value as an Integer type. /// /// This value as an Integer type. public int AsInteger { get { var value = this.AsNumber; // Prevent overflow if the value doesn't fit. if (value >= int.MaxValue) { return int.MaxValue; } if (value <= int.MinValue) { return int.MinValue; } return (int)value; } } /// /// Gets this value as a Number type. /// /// This value as a Number type. public double AsNumber { get { switch (this.Type) { case JsonValueType.Boolean: return (this.value == 1) ? 1 : 0; case JsonValueType.Number: return this.value; case JsonValueType.String: double number; if (double.TryParse((string)this.reference, NumberStyles.Float, CultureInfo.InvariantCulture, out number)) { return number; } else { goto default; } default: return 0; } } } /// /// Gets this value as a String type. /// /// This value as a String type. public string AsString { get { switch (this.Type) { case JsonValueType.Boolean: return (this.value == 1) ? "true" : "false"; case JsonValueType.Number: return this.value.ToString(CultureInfo.InvariantCulture); case JsonValueType.String: return (string)this.reference; default: return null; } } } /// /// Gets this value as an JsonObject. /// /// This value as an JsonObject. public JsonObject AsJsonObject { get { return this.IsJsonObject ? (JsonObject)this.reference : null; } } /// /// Gets this value as an JsonArray. /// /// This value as an JsonArray. public JsonArray AsJsonArray { get { return this.IsJsonArray ? (JsonArray)this.reference : null; } } /// /// Gets this value as a system.DateTime. /// /// This value as a system.DateTime. public DateTime? AsDateTime { get { DateTime value; if (this.IsString && DateTime.TryParse((string)this.reference, out value)) { return value; } else { return null; } } } /// /// Gets this (inner) value as a System.object. /// /// This (inner) value as a System.object. public object AsObject { get { switch (this.Type) { case JsonValueType.Boolean: case JsonValueType.Number: return this.value; case JsonValueType.String: case JsonValueType.Object: case JsonValueType.Array: return this.reference; default: return null; } } } /// /// Gets or sets the value associated with the specified key. /// /// The key of the value to get or set. /// /// Thrown when this JsonValue is not a JsonObject. /// public JsonValue this[string key] { get { if (this.IsJsonObject) { return ((JsonObject)this.reference)[key]; } else { throw new InvalidOperationException("This value does not represent a JsonObject."); } } set { if (this.IsJsonObject) { ((JsonObject)this.reference)[key] = value; } else { throw new InvalidOperationException("This value does not represent a JsonObject."); } } } /// /// Gets or sets the value at the specified index. /// /// The zero-based index of the value to get or set. /// /// Thrown when this JsonValue is not a JsonArray /// public JsonValue this[int index] { get { if (this.IsJsonArray) { return ((JsonArray)this.reference)[index]; } else { throw new InvalidOperationException("This value does not represent a JsonArray."); } } set { if (this.IsJsonArray) { ((JsonArray)this.reference)[index] = value; } else { throw new InvalidOperationException("This value does not represent a JsonArray."); } } } /// /// Converts the given nullable boolean into a JsonValue. /// /// The value to be converted. public static implicit operator JsonValue(bool? value) { return new JsonValue(value); } /// /// Converts the given nullable double into a JsonValue. /// /// The value to be converted. public static implicit operator JsonValue(double? value) { return new JsonValue(value); } /// /// Converts the given string into a JsonValue. /// /// The value to be converted. public static implicit operator JsonValue(string value) { return new JsonValue(value); } /// /// Converts the given JsonObject into a JsonValue. /// /// The value to be converted. public static implicit operator JsonValue(JsonObject value) { return new JsonValue(value); } /// /// Converts the given JsonArray into a JsonValue. /// /// The value to be converted. public static implicit operator JsonValue(JsonArray value) { return new JsonValue(value); } /// /// Converts the given DateTime? into a JsonValue. /// /// /// The DateTime value will be stored as a string using ISO 8601 format, /// since JSON does not define a DateTime type. /// /// The value to be converted. public static implicit operator JsonValue(DateTime? value) { if (value == null) { return JsonValue.Null; } return new JsonValue(value.Value.ToString("o")); } /// /// Converts the given JsonValue into an Int. /// /// The JsonValue to be converted. public static explicit operator int(JsonValue jsonValue) { if (jsonValue.IsInteger) { return jsonValue.AsInteger; } else { return 0; } } /// /// Converts the given JsonValue into a nullable Int. /// /// The JsonValue to be converted. /// /// Throws System.InvalidCastException when the inner value type of the /// JsonValue is not the desired type of the conversion. /// public static explicit operator int?(JsonValue jsonValue) { if (jsonValue.IsNull) { return null; } else { return (int)jsonValue; } } /// /// Converts the given JsonValue into a Bool. /// /// The JsonValue to be converted. public static explicit operator bool(JsonValue jsonValue) { if (jsonValue.IsBoolean) { return jsonValue.value == 1; } else { return false; } } /// /// Converts the given JsonValue into a nullable Bool. /// /// The JsonValue to be converted. /// /// Throws System.InvalidCastException when the inner value type of the /// JsonValue is not the desired type of the conversion. /// public static explicit operator bool?(JsonValue jsonValue) { if (jsonValue.IsNull) { return null; } else { return (bool)jsonValue; } } /// /// Converts the given JsonValue into a Double. /// /// The JsonValue to be converted. public static explicit operator double(JsonValue jsonValue) { if (jsonValue.IsNumber) { return jsonValue.value; } else { return double.NaN; } } /// /// Converts the given JsonValue into a nullable Double. /// /// The JsonValue to be converted. /// /// Throws System.InvalidCastException when the inner value type of the /// JsonValue is not the desired type of the conversion. /// public static explicit operator double?(JsonValue jsonValue) { if (jsonValue.IsNull) { return null; } else { return (double)jsonValue; } } /// /// Converts the given JsonValue into a String. /// /// The JsonValue to be converted. public static explicit operator string(JsonValue jsonValue) { if (jsonValue.IsString || jsonValue.IsNull) { return jsonValue.reference as string; } else { return null; } } /// /// Converts the given JsonValue into a JsonObject. /// /// The JsonValue to be converted. public static explicit operator JsonObject(JsonValue jsonValue) { if (jsonValue.IsJsonObject || jsonValue.IsNull) { return jsonValue.reference as JsonObject; } else { return null; } } /// /// Converts the given JsonValue into a JsonArray. /// /// The JsonValue to be converted. public static explicit operator JsonArray(JsonValue jsonValue) { if (jsonValue.IsJsonArray || jsonValue.IsNull) { return jsonValue.reference as JsonArray; } else { return null; } } /// /// Converts the given JsonValue into a DateTime. /// /// The JsonValue to be converted. public static explicit operator DateTime(JsonValue jsonValue) { var dateTime = jsonValue.AsDateTime; if (dateTime.HasValue) { return dateTime.Value; } else { return DateTime.MinValue; } } /// /// Converts the given JsonValue into a nullable DateTime. /// /// The JsonValue to be converted. public static explicit operator DateTime?(JsonValue jsonValue) { if (jsonValue.IsDateTime || jsonValue.IsNull) { return jsonValue.AsDateTime; } else { return null; } } /// /// Returns a value indicating whether the two given JsonValues are equal. /// /// First JsonValue to compare. /// Second JsonValue to compare. public static bool operator ==(JsonValue a, JsonValue b) { return (a.Type == b.Type) && (a.value == b.value) && Equals(a.reference, b.reference); } /// /// Returns a value indicating whether the two given JsonValues are unequal. /// /// First JsonValue to compare. /// Second JsonValue to compare. public static bool operator !=(JsonValue a, JsonValue b) { return !(a == b); } /// /// Returns a JsonValue by parsing the given string. /// /// The JSON-formatted string to be parsed. /// The representing the parsed text. public static JsonValue Parse(string text) { return JsonReader.Parse(text); } /// public override bool Equals(object obj) { if (obj == null) { return this.IsNull; } var jsonValue = obj as JsonValue?; if (jsonValue == null) { return false; } else { return this == jsonValue.Value; } } /// public override int GetHashCode() { if (this.IsNull) { return this.Type.GetHashCode(); } else { return this.Type.GetHashCode() ^ this.value.GetHashCode() ^ EqualityComparer.Default.GetHashCode(this.reference); } } [ExcludeFromCodeCoverage] private class JsonValueDebugView { private JsonValue jsonValue; public JsonValueDebugView(JsonValue jsonValue) { this.jsonValue = jsonValue; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public JsonObject ObjectView { get { if (this.jsonValue.IsJsonObject) { return (JsonObject)this.jsonValue.reference; } else { return null; } } } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public JsonArray ArrayView { get { if (this.jsonValue.IsJsonArray) { return (JsonArray)this.jsonValue.reference; } else { return null; } } } public JsonValueType Type { get { return this.jsonValue.Type; } } public object Value { get { if (this.jsonValue.IsJsonObject) { return (JsonObject)this.jsonValue.reference; } else if (this.jsonValue.IsJsonArray) { return (JsonArray)this.jsonValue.reference; } else { return this.jsonValue; } } } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/JsonValueType.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson { /// /// Enumerates the types of Json values. /// internal enum JsonValueType : byte { /// /// A null value. /// Null = 0, /// /// A boolean value. /// Boolean, /// /// A number value. /// Number, /// /// A string value. /// String, /// /// An object value. /// Object, /// /// An array value. /// Array, } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/Serialization/JsonParseException.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson.Serialization { using System; /// /// The exception that is thrown when a JSON message cannot be parsed. /// /// /// This exception is only intended to be thrown by LightJson. /// internal sealed class JsonParseException : Exception { /// /// Initializes a new instance of the class. /// public JsonParseException() : base(GetDefaultMessage(ErrorType.Unknown)) { } /// /// Initializes a new instance of the class with the given error type and position. /// /// The error type that describes the cause of the error. /// The position in the text where the error occurred. public JsonParseException(ErrorType type, TextPosition position) : this(GetDefaultMessage(type), type, position) { } /// /// Initializes a new instance of the class with the given message, error type, and position. /// /// The message that describes the error. /// The error type that describes the cause of the error. /// The position in the text where the error occurred. public JsonParseException(string message, ErrorType type, TextPosition position) : base(message) { this.Type = type; this.Position = position; } /// /// Enumerates the types of errors that can occur when parsing a JSON message. /// public enum ErrorType : int { /// /// Indicates that the cause of the error is unknown. /// Unknown = 0, /// /// Indicates that the text ended before the message could be parsed. /// IncompleteMessage, /// /// Indicates that a JsonObject contains more than one key with the same name. /// DuplicateObjectKeys, /// /// Indicates that the parser encountered and invalid or unexpected character. /// InvalidOrUnexpectedCharacter, } /// /// Gets the text position where the error occurred. /// /// The text position where the error occurred. public TextPosition Position { get; private set; } /// /// Gets the type of error that caused the exception to be thrown. /// /// The type of error that caused the exception to be thrown. public ErrorType Type { get; private set; } private static string GetDefaultMessage(ErrorType type) { switch (type) { case ErrorType.IncompleteMessage: return "The string ended before a value could be parsed."; case ErrorType.InvalidOrUnexpectedCharacter: return "The parser encountered an invalid or unexpected character."; case ErrorType.DuplicateObjectKeys: return "The parser encountered a JsonObject with duplicate keys."; default: return "An error occurred while parsing the JSON message."; } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/Serialization/JsonReader.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson.Serialization { using System; using System.Globalization; using System.IO; using System.Text; using ErrorType = JsonParseException.ErrorType; /// /// Represents a reader that can read JsonValues. /// internal sealed class JsonReader { private TextScanner scanner; private JsonReader(TextReader reader) { this.scanner = new TextScanner(reader); } /// /// Creates a JsonValue by using the given TextReader. /// /// The TextReader used to read a JSON message. /// The parsed . public static JsonValue Parse(TextReader reader) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } return new JsonReader(reader).Parse(); } /// /// Creates a JsonValue by reader the JSON message in the given string. /// /// The string containing the JSON message. /// The parsed . public static JsonValue Parse(string source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } using (var reader = new StringReader(source)) { return Parse(reader); } } private string ReadJsonKey() { return this.ReadString(); } private JsonValue ReadJsonValue() { this.scanner.SkipWhitespace(); var next = this.scanner.Peek(); if (char.IsNumber(next)) { return this.ReadNumber(); } switch (next) { case '{': return this.ReadObject(); case '[': return this.ReadArray(); case '"': return this.ReadString(); case '-': return this.ReadNumber(); case 't': case 'f': return this.ReadBoolean(); case 'n': return this.ReadNull(); default: throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, this.scanner.Position); } } private JsonValue ReadNull() { this.scanner.Assert("null"); return JsonValue.Null; } private JsonValue ReadBoolean() { switch (this.scanner.Peek()) { case 't': this.scanner.Assert("true"); return true; default: this.scanner.Assert("false"); return false; } } private void ReadDigits(StringBuilder builder) { while (true) { int next = this.scanner.Peek(throwAtEndOfFile: false); if (next == -1 || !char.IsNumber((char)next)) { return; } builder.Append(this.scanner.Read()); } } private JsonValue ReadNumber() { var builder = new StringBuilder(); if (this.scanner.Peek() == '-') { builder.Append(this.scanner.Read()); } if (this.scanner.Peek() == '0') { builder.Append(this.scanner.Read()); } else { this.ReadDigits(builder); } if (this.scanner.Peek(throwAtEndOfFile: false) == '.') { builder.Append(this.scanner.Read()); this.ReadDigits(builder); } if (this.scanner.Peek(throwAtEndOfFile: false) == 'e' || this.scanner.Peek(throwAtEndOfFile: false) == 'E') { builder.Append(this.scanner.Read()); var next = this.scanner.Peek(); switch (next) { case '+': case '-': builder.Append(this.scanner.Read()); break; } this.ReadDigits(builder); } return double.Parse( builder.ToString(), CultureInfo.InvariantCulture); } private string ReadString() { var builder = new StringBuilder(); this.scanner.Assert('"'); while (true) { var errorPosition = this.scanner.Position; var c = this.scanner.Read(); if (c == '\\') { errorPosition = this.scanner.Position; c = this.scanner.Read(); switch (char.ToLower(c)) { case '"': case '\\': case '/': builder.Append(c); break; case 'b': builder.Append('\b'); break; case 'f': builder.Append('\f'); break; case 'n': builder.Append('\n'); break; case 'r': builder.Append('\r'); break; case 't': builder.Append('\t'); break; case 'u': builder.Append(this.ReadUnicodeLiteral()); break; default: throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } } else if (c == '"') { break; } else { if (char.IsControl(c)) { throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } else { builder.Append(c); } } } return builder.ToString(); } private int ReadHexDigit() { var errorPosition = this.scanner.Position; switch (char.ToUpper(this.scanner.Read())) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'A': return 10; case 'B': return 11; case 'C': return 12; case 'D': return 13; case 'E': return 14; case 'F': return 15; default: throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } } private char ReadUnicodeLiteral() { int value = 0; value += this.ReadHexDigit() * 4096; // 16^3 value += this.ReadHexDigit() * 256; // 16^2 value += this.ReadHexDigit() * 16; // 16^1 value += this.ReadHexDigit(); // 16^0 return (char)value; } private JsonObject ReadObject() { return this.ReadObject(new JsonObject()); } private JsonObject ReadObject(JsonObject jsonObject) { this.scanner.Assert('{'); this.scanner.SkipWhitespace(); if (this.scanner.Peek() == '}') { this.scanner.Read(); } else { while (true) { this.scanner.SkipWhitespace(); var errorPosition = this.scanner.Position; var key = this.ReadJsonKey(); if (jsonObject.ContainsKey(key)) { throw new JsonParseException( ErrorType.DuplicateObjectKeys, errorPosition); } this.scanner.SkipWhitespace(); this.scanner.Assert(':'); this.scanner.SkipWhitespace(); var value = this.ReadJsonValue(); jsonObject.Add(key, value); this.scanner.SkipWhitespace(); errorPosition = this.scanner.Position; var next = this.scanner.Read(); if (next == ',') { // Allow trailing commas in objects this.scanner.SkipWhitespace(); if (this.scanner.Peek() == '}') { next = this.scanner.Read(); } } if (next == '}') { break; } else if (next == ',') { continue; } else { throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } } } return jsonObject; } private JsonArray ReadArray() { return this.ReadArray(new JsonArray()); } private JsonArray ReadArray(JsonArray jsonArray) { this.scanner.Assert('['); this.scanner.SkipWhitespace(); if (this.scanner.Peek() == ']') { this.scanner.Read(); } else { while (true) { this.scanner.SkipWhitespace(); var value = this.ReadJsonValue(); jsonArray.Add(value); this.scanner.SkipWhitespace(); var errorPosition = this.scanner.Position; var next = this.scanner.Read(); if (next == ',') { // Allow trailing commas in arrays this.scanner.SkipWhitespace(); if (this.scanner.Peek() == ']') { next = this.scanner.Read(); } } if (next == ']') { break; } else if (next == ',') { continue; } else { throw new JsonParseException( ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } } } return jsonArray; } private JsonValue Parse() { this.scanner.SkipWhitespace(); return this.ReadJsonValue(); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/Serialization/TextPosition.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson.Serialization { /// /// Represents a position within a plain text resource. /// internal struct TextPosition { /// /// The column position, 0-based. /// public long Column; /// /// The line position, 0-based. /// public long Line; } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/LightJson/Serialization/TextScanner.cs ================================================ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. namespace LightJson.Serialization { using System.IO; using ErrorType = JsonParseException.ErrorType; /// /// Represents a text scanner that reads one character at a time. /// internal sealed class TextScanner { private TextReader reader; private TextPosition position; /// /// Initializes a new instance of the class. /// /// The TextReader to read the text. public TextScanner(TextReader reader) { this.reader = reader; } /// /// Gets the position of the scanner within the text. /// /// The position of the scanner within the text. public TextPosition Position { get { return this.position; } } /// /// Reads the next character in the stream without changing the current position. /// /// The next character in the stream. public char Peek() => (char)this.Peek(throwAtEndOfFile: true); /// /// Reads the next character in the stream without changing the current position. /// /// to throw an exception if the end of the file is /// reached; otherwise, . /// The next character in the stream, or -1 if the end of the file is reached with /// set to . public int Peek(bool throwAtEndOfFile) { var next = this.reader.Peek(); if (next == -1 && throwAtEndOfFile) { throw new JsonParseException( ErrorType.IncompleteMessage, this.position); } else { return next; } } /// /// Reads the next character in the stream, advancing the text position. /// /// The next character in the stream. public char Read() { var next = this.reader.Read(); if (next == -1) { throw new JsonParseException( ErrorType.IncompleteMessage, this.position); } else { if (next == '\n') { this.position.Line += 1; this.position.Column = 0; } else { this.position.Column += 1; } return (char)next; } } /// /// Advances the scanner to next non-whitespace character. /// public void SkipWhitespace() { while (true) { char next = this.Peek(); if (char.IsWhiteSpace(next)) { this.Read(); continue; } else if (next == '/') { this.SkipComment(); continue; } else { break; } } } /// /// Verifies that the given character matches the next character in the stream. /// If the characters do not match, an exception will be thrown. /// /// The expected character. public void Assert(char next) { var errorPosition = this.position; if (this.Read() != next) { throw new JsonParseException( string.Format("Parser expected '{0}'", next), ErrorType.InvalidOrUnexpectedCharacter, errorPosition); } } /// /// Verifies that the given string matches the next characters in the stream. /// If the strings do not match, an exception will be thrown. /// /// The expected string. public void Assert(string next) { for (var i = 0; i < next.Length; i += 1) { this.Assert(next[i]); } } private void SkipComment() { // First character is the first slash this.Read(); switch (this.Peek()) { case '/': this.SkipLineComment(); return; case '*': this.SkipBlockComment(); return; default: throw new JsonParseException( string.Format("Parser expected '{0}'", this.Peek()), ErrorType.InvalidOrUnexpectedCharacter, this.position); } } private void SkipLineComment() { // First character is the second '/' of the opening '//' this.Read(); while (true) { switch (this.reader.Peek()) { case '\n': // Reached the end of the line this.Read(); return; case -1: // Reached the end of the file return; default: this.Read(); continue; } } } private void SkipBlockComment() { // First character is the '*' of the opening '/*' this.Read(); bool foundStar = false; while (true) { switch (this.reader.Peek()) { case '*': this.Read(); foundStar = true; continue; case '/': this.Read(); if (foundStar) { return; } else { foundStar = false; continue; } case -1: // Reached the end of the file return; default: this.Read(); foundStar = false; continue; } } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MemberReferenceMetadata.cs ================================================ // Copyright (c) 2023 James May // // 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. #nullable enable using System; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { #if !VSADDIN /// /// Convenience wrapper for and . /// public sealed class MemberReferenceMetadata { readonly MemberReference entry; public MetadataReader Metadata { get; } public MemberReferenceHandle Handle { get; } string? name; public string Name { get { try { return name ??= Metadata.GetString(entry.Name); } catch (BadImageFormatException) { return name = $"MR:{Handle}"; } } } public EntityHandle Parent => entry.Parent; public MemberReferenceKind MemberReferenceKind => entry.GetKind(); public MemberReferenceMetadata(MetadataReader metadata, MemberReferenceHandle handle) { Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Handle = handle; entry = metadata.GetMemberReference(handle); } public override string ToString() => Name; } #endif } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs ================================================ // 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.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Security.Cryptography; using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { public static class MetadataExtensions { static string CalculatePublicKeyToken(BlobHandle blob, MetadataReader reader) { // Calculate public key token: // 1. hash the public key (always use SHA1). byte[] publicKeyTokenBytes; using (var hasher = SHA1.Create()) { publicKeyTokenBytes = hasher.ComputeHash(reader.GetBlobBytes(blob)); } // 2. take the last 8 bytes // 3. according to Cecil we need to reverse them, other sources did not mention this. return publicKeyTokenBytes.TakeLast(8).Reverse().ToHexString(8); } public static string GetPublicKeyToken(this MetadataReader reader) { if (!reader.IsAssembly) return string.Empty; var asm = reader.GetAssemblyDefinition(); string publicKey = "null"; if (!asm.PublicKey.IsNil) { // AssemblyFlags.PublicKey does not apply to assembly definitions publicKey = CalculatePublicKeyToken(asm.PublicKey, reader); } return publicKey; } public static string GetFullAssemblyName(this MetadataReader reader) { if (!reader.IsAssembly) return string.Empty; var asm = reader.GetAssemblyDefinition(); string publicKey = reader.GetPublicKeyToken(); return $"{reader.GetString(asm.Name)}, " + $"Version={asm.Version}, " + $"Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, " + $"PublicKeyToken={publicKey}"; } public static bool TryGetFullAssemblyName(this MetadataReader reader, out string assemblyName) { try { assemblyName = GetFullAssemblyName(reader); return true; } catch (BadImageFormatException) { assemblyName = null; return false; } } public static string GetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader) { StringBuilder builder = new StringBuilder(); builder.Append(reader.GetString(reference.Name)); builder.Append(", Version="); builder.Append(reference.Version); builder.Append(", Culture="); if (reference.Culture.IsNil) { builder.Append("neutral"); } else { builder.Append(reader.GetString(reference.Culture)); } if (reference.PublicKeyOrToken.IsNil) { builder.Append(", PublicKeyToken=null"); } else if ((reference.Flags & AssemblyFlags.PublicKey) != 0) { builder.Append(", PublicKeyToken="); builder.Append(CalculatePublicKeyToken(reference.PublicKeyOrToken, reader)); } else { builder.Append(", PublicKeyToken="); builder.AppendHexString(reader.GetBlobReader(reference.PublicKeyOrToken)); } if ((reference.Flags & AssemblyFlags.Retargetable) != 0) { builder.Append(", Retargetable=true"); } return builder.ToString(); } public static bool TryGetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader, out string assemblyName) { try { assemblyName = GetFullAssemblyName(reference, reader); return true; } catch (BadImageFormatException) { assemblyName = null; return false; } } public static string ToHexString(this IEnumerable bytes, int estimatedLength) { if (bytes == null) throw new ArgumentNullException(nameof(bytes)); StringBuilder sb = new StringBuilder(estimatedLength * 2); foreach (var b in bytes) sb.AppendFormat("{0:x2}", b); return sb.ToString(); } public static void AppendHexString(this StringBuilder builder, BlobReader reader) { for (int i = 0; i < reader.Length; i++) { builder.AppendFormat("{0:x2}", reader.ReadByte()); } } public static string ToHexString(this BlobReader reader) { StringBuilder sb = new StringBuilder(reader.Length * 3); for (int i = 0; i < reader.Length; i++) { if (i == 0) sb.AppendFormat("{0:X2}", reader.ReadByte()); else sb.AppendFormat("-{0:X2}", reader.ReadByte()); } return sb.ToString(); } public static IEnumerable GetTopLevelTypeDefinitions(this MetadataReader reader) { foreach (var handle in reader.TypeDefinitions) { var td = reader.GetTypeDefinition(handle); if (td.GetDeclaringType().IsNil) yield return handle; } } public static string ToILNameString(this FullTypeName typeName, bool omitGenerics = false) { string name; if (typeName.IsNested) { name = typeName.Name; if (!omitGenerics) { int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); if (localTypeParameterCount > 0) name += "`" + localTypeParameterCount; } name = Disassembler.DisassemblerHelpers.Escape(name); return $"{typeName.GetDeclaringType().ToILNameString(omitGenerics)}/{name}"; } if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) { name = $"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"; if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } else { name = typeName.Name; if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } return Disassembler.DisassemblerHelpers.Escape(name); } internal static readonly TypeProvider minimalCorlibTypeProvider = new TypeProvider(new SimpleCompilation(MinimalCorlib.Instance)); /// /// An attribute type provider that can be used to decode attribute signatures /// that only mention built-in types. /// public static ICustomAttributeTypeProvider MinimalAttributeTypeProvider { get => minimalCorlibTypeProvider; } public static ISignatureTypeProvider MinimalSignatureTypeProvider { get => minimalCorlibTypeProvider; } /// /// Converts to . /// Returns 0 for known types that are not primitive types (such as ). /// public static PrimitiveTypeCode ToPrimitiveTypeCode(this KnownTypeCode typeCode) { switch (typeCode) { case KnownTypeCode.Object: return PrimitiveTypeCode.Object; case KnownTypeCode.Boolean: return PrimitiveTypeCode.Boolean; case KnownTypeCode.Char: return PrimitiveTypeCode.Char; case KnownTypeCode.SByte: return PrimitiveTypeCode.SByte; case KnownTypeCode.Byte: return PrimitiveTypeCode.Byte; case KnownTypeCode.Int16: return PrimitiveTypeCode.Int16; case KnownTypeCode.UInt16: return PrimitiveTypeCode.UInt16; case KnownTypeCode.Int32: return PrimitiveTypeCode.Int32; case KnownTypeCode.UInt32: return PrimitiveTypeCode.UInt32; case KnownTypeCode.Int64: return PrimitiveTypeCode.Int64; case KnownTypeCode.UInt64: return PrimitiveTypeCode.UInt64; case KnownTypeCode.Single: return PrimitiveTypeCode.Single; case KnownTypeCode.Double: return PrimitiveTypeCode.Double; case KnownTypeCode.String: return PrimitiveTypeCode.String; case KnownTypeCode.Void: return PrimitiveTypeCode.Void; case KnownTypeCode.TypedReference: return PrimitiveTypeCode.TypedReference; case KnownTypeCode.IntPtr: return PrimitiveTypeCode.IntPtr; case KnownTypeCode.UIntPtr: return PrimitiveTypeCode.UIntPtr; default: return 0; } } public static KnownTypeCode ToKnownTypeCode(this PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: return KnownTypeCode.Boolean; case PrimitiveTypeCode.Byte: return KnownTypeCode.Byte; case PrimitiveTypeCode.SByte: return KnownTypeCode.SByte; case PrimitiveTypeCode.Char: return KnownTypeCode.Char; case PrimitiveTypeCode.Int16: return KnownTypeCode.Int16; case PrimitiveTypeCode.UInt16: return KnownTypeCode.UInt16; case PrimitiveTypeCode.Int32: return KnownTypeCode.Int32; case PrimitiveTypeCode.UInt32: return KnownTypeCode.UInt32; case PrimitiveTypeCode.Int64: return KnownTypeCode.Int64; case PrimitiveTypeCode.UInt64: return KnownTypeCode.UInt64; case PrimitiveTypeCode.Single: return KnownTypeCode.Single; case PrimitiveTypeCode.Double: return KnownTypeCode.Double; case PrimitiveTypeCode.IntPtr: return KnownTypeCode.IntPtr; case PrimitiveTypeCode.UIntPtr: return KnownTypeCode.UIntPtr; case PrimitiveTypeCode.Object: return KnownTypeCode.Object; case PrimitiveTypeCode.String: return KnownTypeCode.String; case PrimitiveTypeCode.TypedReference: return KnownTypeCode.TypedReference; case PrimitiveTypeCode.Void: return KnownTypeCode.Void; default: return KnownTypeCode.None; } } public static IEnumerable GetModuleReferences(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.ModuleRef); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.ModuleReferenceHandle(row); } } public static IEnumerable GetTypeSpecifications(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.TypeSpec); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.TypeSpecificationHandle(row); } } public static IEnumerable GetMethodSpecifications(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.MethodSpec); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.MethodSpecificationHandle(row); } } public static IEnumerable<(Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association)> GetMethodSemantics(this MetadataReader metadata) { int offset = metadata.GetTableMetadataOffset(TableIndex.MethodSemantics); int rowSize = metadata.GetTableRowSize(TableIndex.MethodSemantics); int rowCount = metadata.GetTableRowCount(TableIndex.MethodSemantics); bool methodSmall = metadata.GetTableRowCount(TableIndex.MethodDef) <= ushort.MaxValue; bool assocSmall = metadata.GetTableRowCount(TableIndex.Property) <= ushort.MaxValue && metadata.GetTableRowCount(TableIndex.Event) <= ushort.MaxValue; int assocOffset = (methodSmall ? 2 : 4) + 2; for (int row = 0; row < rowCount; row++) { yield return Read(row); } (Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association) Read(int row) { var span = metadata.AsReadOnlySpan(); var methodDefSpan = span.Slice(offset + rowSize * row + 2); int methodDef = methodSmall ? BinaryPrimitives.ReadUInt16LittleEndian(methodDefSpan) : (int)BinaryPrimitives.ReadUInt32LittleEndian(methodDefSpan); var assocSpan = span.Slice(assocOffset); int assocDef = assocSmall ? BinaryPrimitives.ReadUInt16LittleEndian(assocSpan) : (int)BinaryPrimitives.ReadUInt32LittleEndian(assocSpan); EntityHandle propOrEvent; if ((assocDef & 0x1) == 1) { propOrEvent = MetadataTokens.PropertyDefinitionHandle(assocDef >> 1); } else { propOrEvent = MetadataTokens.EventDefinitionHandle(assocDef >> 1); } return (MetadataTokens.Handle(0x18000000 | (row + 1)), (MethodSemanticsAttributes)(BinaryPrimitives.ReadUInt16LittleEndian(span)), MetadataTokens.MethodDefinitionHandle(methodDef), propOrEvent); } } public static IEnumerable GetFieldLayouts(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.EntityHandle(TableIndex.FieldLayout, row); } } public static (int Offset, FieldDefinitionHandle FieldDef) GetFieldLayout(this MetadataReader metadata, EntityHandle fieldLayoutHandle) { var startPointer = metadata.AsReadOnlySpan(); int offset = metadata.GetTableMetadataOffset(TableIndex.FieldLayout); int rowSize = metadata.GetTableRowSize(TableIndex.FieldLayout); int rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); int fieldRowNo = metadata.GetRowNumber(fieldLayoutHandle); bool small = metadata.GetTableRowCount(TableIndex.Field) <= ushort.MaxValue; for (int row = rowCount - 1; row >= 0; row--) { ReadOnlySpan ptr = startPointer.Slice(offset + rowSize * row); var rowNoSpan = ptr.Slice(4); uint rowNo = small ? BinaryPrimitives.ReadUInt16LittleEndian(rowNoSpan) : BinaryPrimitives.ReadUInt32LittleEndian(rowNoSpan); if (fieldRowNo == rowNo) { return (BinaryPrimitives.ReadInt32LittleEndian(ptr), MetadataTokens.FieldDefinitionHandle(fieldRowNo)); } } return (0, default); } public static ReadOnlySpan AsReadOnlySpan(this MetadataReader metadataReader) { unsafe { return new(metadataReader.MetadataPointer, metadataReader.MetadataLength); } } public static BlobReader AsBlobReader(this MetadataReader metadataReader) { unsafe { return new(metadataReader.MetadataPointer, metadataReader.MetadataLength); } } public static uint ReadULEB128(this BinaryReader reader) { uint val = 0; int shift = 0; while (true) { byte b = reader.ReadByte(); val |= (b & 0b0111_1111u) << shift; if ((b & 0b1000_0000) == 0) break; shift += 7; if (shift >= 35) throw new OverflowException(); } return val; } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MetadataFile.cs ================================================ // Copyright (c) 2024 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. #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { /// /// MetadataFile is the main class the decompiler uses to represent a metadata assembly/module. /// Every file on disk can be loaded into a standalone MetadataFile instance. /// /// A MetadataFile can be combined with its referenced assemblies/modules to form a type system, /// in that case the class is used instead. /// /// /// In addition to wrapping a System.Reflection.Metadata.MetadataReader, this class /// contains a few decompiler-specific caches to allow efficiently constructing a type /// system from multiple MetadataFiles. This allows the caches to be shared across multiple /// decompiled type systems. /// [DebuggerDisplay("{Kind}: {FileName}")] public class MetadataFile { public enum MetadataFileKind { PortableExecutable, ProgramDebugDatabase, WebCIL, Metadata } public string FileName { get; } public MetadataFileKind Kind { get; } public MetadataReader Metadata { get; } public virtual int MetadataOffset { get; } public virtual bool IsEmbedded { get; } public virtual bool IsMetadataOnly { get; } = true; public bool IsAssembly => Metadata.IsAssembly; string? name; public string Name { get { var value = LazyInit.VolatileRead(ref name); if (value == null) { var metadata = Metadata; if (metadata.IsAssembly) value = metadata.GetString(metadata.GetAssemblyDefinition().Name); else if (metadata.DebugMetadataHeader == null) // standalone debug metadata does not contain module table value = metadata.GetString(metadata.GetModuleDefinition().Name); else value = "debug metadata"; value = LazyInit.GetOrSet(ref name, value); } return value; } } string? fullName; public string FullName { get { var value = LazyInit.VolatileRead(ref fullName); if (value == null) { var metadata = Metadata; value = metadata.IsAssembly ? metadata.GetFullAssemblyName() : Name; value = LazyInit.GetOrSet(ref fullName, value); } return value; } } public TargetRuntime GetRuntime() { string version = Metadata.MetadataVersion; if (version == null || version.Length <= 1) return TargetRuntime.Unknown; switch (version[1]) { case '1': if (version.Length <= 3) return TargetRuntime.Unknown; if (version[3] == 1) return TargetRuntime.Net_1_0; else return TargetRuntime.Net_1_1; case '2': return TargetRuntime.Net_2_0; case '4': return TargetRuntime.Net_4_0; default: return TargetRuntime.Unknown; } } ImmutableArray assemblyReferences; public ImmutableArray AssemblyReferences { get { var value = assemblyReferences; if (value.IsDefault) { value = Metadata.AssemblyReferences.Select(r => new AssemblyReference(this.Metadata, r)).ToImmutableArray(); assemblyReferences = value; } return value; } } ImmutableArray moduleReferences; public ImmutableArray ModuleReferences { get { var value = moduleReferences; if (value.IsDefault) { value = Metadata.GetModuleReferences() .Select(m => new ModuleReferenceMetadata(this.Metadata, m)) .ToImmutableArray(); moduleReferences = value; } return value; } } public ImmutableArray Resources => GetResources().ToImmutableArray(); IEnumerable GetResources() { var metadata = Metadata; foreach (var h in metadata.ManifestResources) { yield return new MetadataResource(this, h); } } Dictionary? typeLookup; /// /// Finds the top-level-type with the specified name. /// public TypeDefinitionHandle GetTypeDefinition(TopLevelTypeName typeName) { var lookup = LazyInit.VolatileRead(ref typeLookup); if (lookup == null) { lookup = new Dictionary(); foreach (var handle in Metadata.TypeDefinitions) { var td = Metadata.GetTypeDefinition(handle); if (!td.GetDeclaringType().IsNil) { continue; // nested type } var nsHandle = td.Namespace; string ns = nsHandle.IsNil ? string.Empty : Metadata.GetString(nsHandle); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(Metadata.GetString(td.Name), out int typeParameterCount); lookup[new TopLevelTypeName(ns, name, typeParameterCount)] = handle; } lookup = LazyInit.GetOrSet(ref typeLookup, lookup); } if (lookup.TryGetValue(typeName, out var resultHandle)) return resultHandle; else return default; } Dictionary? typeForwarderLookup; /// /// Finds the type forwarder with the specified name. /// public ExportedTypeHandle GetTypeForwarder(FullTypeName typeName) { var lookup = LazyInit.VolatileRead(ref typeForwarderLookup); if (lookup == null) { lookup = new Dictionary(); foreach (var handle in Metadata.ExportedTypes) { var td = Metadata.GetExportedType(handle); lookup[td.GetFullTypeName(Metadata)] = handle; } lookup = LazyInit.GetOrSet(ref typeForwarderLookup, lookup); } if (lookup.TryGetValue(typeName, out var resultHandle)) return resultHandle; else return default; } MethodSemanticsLookup? methodSemanticsLookup; internal MethodSemanticsLookup MethodSemanticsLookup { get { var r = LazyInit.VolatileRead(ref methodSemanticsLookup); if (r != null) return r; else return LazyInit.GetOrSet(ref methodSemanticsLookup, new MethodSemanticsLookup(Metadata)); } } PropertyAndEventBackingFieldLookup? propertyAndEventBackingFieldLookup; internal PropertyAndEventBackingFieldLookup PropertyAndEventBackingFieldLookup { get { var r = LazyInit.VolatileRead(ref propertyAndEventBackingFieldLookup); if (r != null) return r; else return LazyInit.GetOrSet(ref propertyAndEventBackingFieldLookup, new PropertyAndEventBackingFieldLookup(Metadata)); } } public MetadataFile(MetadataFileKind kind, string fileName, MetadataReaderProvider metadata, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, int metadataOffset = 0, bool isEmbedded = false, MetadataStringDecoder? utf8Decoder = null) { this.Kind = kind; this.FileName = fileName; this.Metadata = metadata.GetMetadataReader(metadataOptions, utf8Decoder); this.MetadataOffset = metadataOffset; this.IsEmbedded = isEmbedded; } public MetadataFile(MetadataFileKind kind, string fileName, MetadataReader metadataReader, int metadataOffset = 0, bool isEmbedded = false) { this.Kind = kind; this.FileName = fileName; this.Metadata = metadataReader; this.MetadataOffset = metadataOffset; this.IsEmbedded = isEmbedded; } private protected MetadataFile(MetadataFileKind kind, string fileName, PEReader reader, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, MetadataStringDecoder? utf8Decoder = null) { this.Kind = kind; this.FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); _ = reader ?? throw new ArgumentNullException(nameof(reader)); if (!reader.HasMetadata) throw new MetadataFileNotSupportedException("PE file does not contain any managed metadata."); this.Metadata = reader.GetMetadataReader(metadataOptions, utf8Decoder); } public virtual MethodBodyBlock GetMethodBody(int rva) { throw new BadImageFormatException("This metadata file does not contain method bodies."); } public virtual SectionData GetSectionData(int rva) { throw new BadImageFormatException("This metadata file does not support sections."); } public virtual int GetContainingSectionIndex(int rva) { throw new BadImageFormatException("This metadata file does not support sections."); } public virtual ImmutableArray SectionHeaders => throw new BadImageFormatException("This metadata file does not support sections."); /// /// Gets the CLI header or null if the image does not have one. /// public virtual CorHeader? CorHeader => null; public IModuleReference WithOptions(TypeSystemOptions options) { return new MetadataFileWithOptions(this, options); } private class MetadataFileWithOptions : IModuleReference { readonly MetadataFile peFile; readonly TypeSystemOptions options; public MetadataFileWithOptions(MetadataFile peFile, TypeSystemOptions options) { this.peFile = peFile; this.options = options; } IModule IModuleReference.Resolve(ITypeResolveContext context) { return new MetadataModule(context.Compilation, peFile, options); } } } /// /// Abstraction over PEMemoryBlock /// public readonly unsafe struct SectionData { public byte* Pointer { get; } public int Length { get; } public SectionData(PEMemoryBlock block) { Pointer = block.Pointer; Length = block.Length; } public SectionData(byte* startPointer, int length) { Pointer = startPointer; Length = length; } public BlobReader GetReader() { return new BlobReader(Pointer, Length); } internal BlobReader GetReader(int offset, int size) { return new BlobReader(Pointer + offset, size); } } public struct SectionHeader { public string Name; public uint VirtualSize; public uint VirtualAddress; public uint RawDataSize; public uint RawDataPtr; } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MetadataGenericContext.cs ================================================ // 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. #nullable enable using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; namespace ICSharpCode.Decompiler.Metadata { public readonly struct MetadataGenericContext { readonly MetadataReader? metadata; readonly TypeDefinitionHandle declaringType; readonly MethodDefinitionHandle method; public MetadataGenericContext(MethodDefinitionHandle method, MetadataFile module) { this.metadata = module.Metadata; this.method = method; this.declaringType = module.Metadata.GetMethodDefinition(method).GetDeclaringType(); } public MetadataGenericContext(MethodDefinitionHandle method, MetadataReader metadata) { this.metadata = metadata; this.method = method; this.declaringType = metadata.GetMethodDefinition(method).GetDeclaringType(); } public MetadataGenericContext(TypeDefinitionHandle declaringType, MetadataFile module) { this.metadata = module.Metadata; this.method = default; this.declaringType = declaringType; } public MetadataGenericContext(TypeDefinitionHandle declaringType, MetadataReader metadata) { this.metadata = metadata; this.method = default; this.declaringType = declaringType; } public string GetGenericTypeParameterName(int index) { GenericParameterHandle genericParameter = GetGenericTypeParameterHandleOrNull(index); if (genericParameter.IsNil || metadata == null) return index.ToString(); return metadata.GetString(metadata.GetGenericParameter(genericParameter).Name); } public string GetGenericMethodTypeParameterName(int index) { GenericParameterHandle genericParameter = GetGenericMethodTypeParameterHandleOrNull(index); if (genericParameter.IsNil || metadata == null) return index.ToString(); return metadata.GetString(metadata.GetGenericParameter(genericParameter).Name); } public GenericParameterHandle GetGenericTypeParameterHandleOrNull(int index) { if (declaringType.IsNil || index < 0 || metadata == null) return MetadataTokens.GenericParameterHandle(0); var genericParameters = metadata.GetTypeDefinition(declaringType).GetGenericParameters(); if (index >= genericParameters.Count) return MetadataTokens.GenericParameterHandle(0); return genericParameters[index]; } public GenericParameterHandle GetGenericMethodTypeParameterHandleOrNull(int index) { if (method.IsNil || index < 0 || metadata == null) return MetadataTokens.GenericParameterHandle(0); var genericParameters = metadata.GetMethodDefinition(method).GetGenericParameters(); if (index >= genericParameters.Count) return MetadataTokens.GenericParameterHandle(0); return genericParameters[index]; } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MetadataTokenHelpers.cs ================================================ // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; namespace ICSharpCode.Decompiler.Metadata { public static class MetadataTokenHelpers { public static EntityHandle? TryAsEntityHandle(int metadataToken) { // SRM would interpret negative token values as virtual tokens, // but that causes problems later on. if (metadataToken < 0) return null; try { return MetadataTokens.EntityHandle(metadataToken); } catch (ArgumentException) { return null; } } public static EntityHandle EntityHandleOrNil(int metadataToken) { // SRM would interpret negative token values as virtual tokens, // but that causes problems later on. if (metadataToken < 0) return MetadataTokens.EntityHandle(0); try { return MetadataTokens.EntityHandle(metadataToken); } catch (ArgumentException) { return MetadataTokens.EntityHandle(0); } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/MethodSemanticsLookup.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { /// /// Lookup structure that, for an accessor, can find the associated property/event. /// class MethodSemanticsLookup { const MethodSemanticsAttributes csharpAccessors = MethodSemanticsAttributes.Getter | MethodSemanticsAttributes.Setter | MethodSemanticsAttributes.Adder | MethodSemanticsAttributes.Remover; readonly struct Entry : IComparable { public readonly MethodSemanticsAttributes Semantics; public readonly int MethodRowNumber; public MethodDefinitionHandle Method => MetadataTokens.MethodDefinitionHandle(MethodRowNumber); public readonly EntityHandle Association; public Entry(MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) { Semantics = semantics; MethodRowNumber = MetadataTokens.GetRowNumber(method); Association = association; } public int CompareTo(Entry other) { return MethodRowNumber.CompareTo(other.MethodRowNumber); } } // entries, sorted by MethodRowNumber readonly List entries; public MethodSemanticsLookup(MetadataReader metadata, MethodSemanticsAttributes filter = csharpAccessors) { if ((filter & MethodSemanticsAttributes.Other) != 0) { throw new NotSupportedException("SRM doesn't provide access to 'other' accessors"); } entries = new List(metadata.GetTableRowCount(TableIndex.MethodSemantics)); foreach (var propHandle in metadata.PropertyDefinitions) { var prop = metadata.GetPropertyDefinition(propHandle); var accessors = prop.GetAccessors(); AddEntry(MethodSemanticsAttributes.Getter, accessors.Getter, propHandle); AddEntry(MethodSemanticsAttributes.Setter, accessors.Setter, propHandle); } foreach (var eventHandle in metadata.EventDefinitions) { var ev = metadata.GetEventDefinition(eventHandle); var accessors = ev.GetAccessors(); AddEntry(MethodSemanticsAttributes.Adder, accessors.Adder, eventHandle); AddEntry(MethodSemanticsAttributes.Remover, accessors.Remover, eventHandle); AddEntry(MethodSemanticsAttributes.Raiser, accessors.Raiser, eventHandle); } entries.Sort(); void AddEntry(MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) { if ((semantics & filter) == 0 || method.IsNil) return; entries.Add(new Entry(semantics, method, association)); } } public (EntityHandle, MethodSemanticsAttributes) GetSemantics(MethodDefinitionHandle method) { int pos = entries.BinarySearch(new Entry(0, method, default(EntityHandle))); if (pos >= 0) { return (entries[pos].Association, entries[pos].Semantics); } else { return (default(EntityHandle), 0); } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/ModuleReferenceMetadata.cs ================================================ // Copyright (c) 2023 James May // // 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. #nullable enable using System; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { #if !VSADDIN public class ModuleReferenceMetadata /* : IModuleReference*/ { readonly ModuleReference entry; public MetadataReader Metadata { get; } public ModuleReferenceHandle Handle { get; } string? name; public string Name { get { if (name == null) { try { name = Metadata.GetString(entry.Name); } catch (BadImageFormatException) { name = $"AR:{Handle}"; } } return name; } } ImmutableArray attributes; public ImmutableArray Attributes { get { var value = attributes; if (value.IsDefault) { value = entry.GetCustomAttributes().Select(Metadata.GetCustomAttribute).ToImmutableArray(); attributes = value; } return value; } } ImmutableArray typeReferences; public ImmutableArray TypeReferences { get { var value = typeReferences; if (value.IsDefault) { value = Metadata.TypeReferences .Select(r => new TypeReferenceMetadata(Metadata, r)) .Where(r => r.ResolutionScope == Handle) .OrderBy(r => r.Namespace) .ThenBy(r => r.Name) .ToImmutableArray(); typeReferences = value; } return value; } } ImmutableArray exportedTypes; public ImmutableArray ExportedTypes { get { var value = exportedTypes; if (value.IsDefault) { value = Metadata.ExportedTypes .Select(r => new ExportedTypeMetadata(Metadata, r)) .Where(r => r.Implementation == Handle) .OrderBy(r => r.Namespace) .ThenBy(r => r.Name) .ToImmutableArray(); exportedTypes = value; } return value; } } public ModuleReferenceMetadata(MetadataReader metadata, ModuleReferenceHandle handle) { if (metadata == null) throw new ArgumentNullException(nameof(metadata)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Metadata = metadata; Handle = handle; entry = metadata.GetModuleReference(handle); } public ModuleReferenceMetadata(PEFile module, ModuleReferenceHandle handle) { if (module == null) throw new ArgumentNullException(nameof(module)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Metadata = module.Metadata; Handle = handle; entry = Metadata.GetModuleReference(handle); } public override string ToString() { return Name; } } #endif } ================================================ FILE: ICSharpCode.Decompiler/Metadata/OperandType.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Generic; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { public enum OperandType { BrTarget, Field, I, I8, Method, None, R = 7, Sig = 9, String, Switch, Tok, Type, Variable, ShortBrTarget, ShortI, ShortR, ShortVariable } public static partial class ILOpCodeExtensions { public static OperandType GetOperandType(this ILOpCode opCode) { ushort index = (ushort)((((int)opCode & 0x200) >> 1) | ((int)opCode & 0xff)); if (index >= operandTypes.Length) return (OperandType)255; return (OperandType)operandTypes[index]; } public static string GetDisplayName(this ILOpCode opCode) { ushort index = (ushort)((((int)opCode & 0x200) >> 1) | ((int)opCode & 0xff)); if (index >= operandNames.Length) return ""; return operandNames[index]; } public static bool IsDefined(this ILOpCode opCode) { return !string.IsNullOrEmpty(GetDisplayName(opCode)); } static ILOpCodeExtensions() { ILKeywords = BuildKeywordList( "abstract", "algorithm", "alignment", "ansi", "any", "arglist", "array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit", "blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr", "bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf", "char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal", "default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern", "false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime", "filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref", "fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal", "implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init", "initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall", "iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr", "lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested", "newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand", "noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable", "notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out", "permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6", "prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected", "public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval", "rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname", "static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct", "synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref", "unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype", "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper", // These are not listed as keywords in spec, but ILAsm treats them as such "property", "type", "flags", "codelabel", "callconv", "strict", // ILDasm uses these keywords for unsigned integers "uint8", "uint16", "uint32", "uint64" ); } public static readonly HashSet ILKeywords; static HashSet BuildKeywordList(params string[] keywords) { HashSet s = new HashSet(keywords); foreach (var inst in operandNames) { if (string.IsNullOrEmpty(inst)) continue; s.Add(inst); } return s; } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/PEFile.cs ================================================ // 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. #nullable enable using System; using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Metadata { [DebuggerDisplay("{FileName}")] public class PEFile : MetadataFile, IDisposable, IModuleReference { public PEReader Reader { get; } public PEFile(string fileName, PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, MetadataStringDecoder? utf8Decoder = null) : this(fileName, new PEReader(new FileStream(fileName, FileMode.Open, FileAccess.Read), streamOptions), metadataOptions, utf8Decoder) { } public PEFile(string fileName, Stream stream, PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, MetadataStringDecoder? utf8Decoder = null) : this(fileName, new PEReader(stream, streamOptions), metadataOptions, utf8Decoder) { } public PEFile(string fileName, PEReader reader, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, MetadataStringDecoder? utf8Decoder = null) : base(MetadataFileKind.PortableExecutable, fileName, reader, metadataOptions, utf8Decoder) { this.Reader = reader; } public override bool IsEmbedded => false; public override int MetadataOffset => Reader.PEHeaders.MetadataStartOffset; public override bool IsMetadataOnly => false; public void Dispose() { Reader.Dispose(); } IModule TypeSystem.IModuleReference.Resolve(ITypeResolveContext context) { return new MetadataModule(context.Compilation, this, TypeSystemOptions.Default); } public override MethodBodyBlock GetMethodBody(int rva) { return Reader.GetMethodBody(rva); } public override SectionData GetSectionData(int rva) { return new SectionData(Reader.GetSectionData(rva)); } public override int GetContainingSectionIndex(int rva) { return Reader.PEHeaders.GetContainingSectionIndex(rva); } ImmutableArray sectionHeaders; public override ImmutableArray SectionHeaders { get { var value = sectionHeaders; if (value.IsDefault) { value = Reader.PEHeaders.SectionHeaders .Select(h => new SectionHeader { Name = h.Name, RawDataPtr = unchecked((uint)h.PointerToRawData), RawDataSize = unchecked((uint)h.SizeOfRawData), VirtualAddress = unchecked((uint)h.VirtualAddress), VirtualSize = unchecked((uint)h.VirtualSize) }).ToImmutableArray(); sectionHeaders = value; } return value; } } public override CorHeader? CorHeader => Reader.PEHeaders.CorHeader; } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/PropertyAndEventBackingFieldLookup.cs ================================================ // Copyright (c) 2025 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.Collections.Generic; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { class PropertyAndEventBackingFieldLookup { private readonly MetadataReader metadata; private readonly Dictionary propertyLookup = new(); private readonly Dictionary eventLookup = new(); public PropertyAndEventBackingFieldLookup(MetadataReader metadata) { this.metadata = metadata; var nameToFieldMap = new MultiDictionary(); HashSet eventNames = new(); foreach (var tdh in metadata.TypeDefinitions) { var type = metadata.GetTypeDefinition(tdh); foreach (var fdh in type.GetFields()) { var field = metadata.GetFieldDefinition(fdh); var name = metadata.GetString(field.Name); nameToFieldMap.Add(name, fdh); } foreach (var pdh in type.GetProperties()) { var property = metadata.GetPropertyDefinition(pdh); var name = metadata.GetString(property.Name); // default C# property backing field name is "k__BackingField" if (nameToFieldMap.TryGetValues($"<{name}>k__BackingField", out var fieldHandles)) { foreach (var fieldHandle in fieldHandles) { propertyLookup[fieldHandle] = pdh; } } else if (nameToFieldMap.TryGetValues($"_{name}", out fieldHandles)) { foreach (var fieldHandle in fieldHandles) { if (fieldHandle.IsCompilerGenerated(metadata)) { propertyLookup[fieldHandle] = pdh; } } } } // first get all names of events defined, so that we can make sure we don't accidentally // associate the wrong backing field with the event, in case there is an event called "Something" // without a backing field (i.e., custom event) as well as an auto/field event called "SomethingEvent" // declared in the same type. foreach (var edh in type.GetEvents()) { var ev = metadata.GetEventDefinition(edh); eventNames.Add(metadata.GetString(ev.Name)); } foreach (var edh in type.GetEvents()) { var ev = metadata.GetEventDefinition(edh); var name = metadata.GetString(ev.Name); if (nameToFieldMap.TryGetValues(name, out var fieldHandles)) { foreach (var fieldHandle in fieldHandles) { eventLookup[fieldHandle] = edh; } } else { var nameWithSuffix = $"{name}Event"; if (!eventNames.Contains(nameWithSuffix) && nameToFieldMap.TryGetValues(nameWithSuffix, out fieldHandles)) { foreach (var fieldHandle in fieldHandles) { eventLookup[fieldHandle] = edh; } } } } eventNames.Clear(); nameToFieldMap.Clear(); } } public bool IsPropertyBackingField(FieldDefinitionHandle field, out PropertyDefinitionHandle handle) { return propertyLookup.TryGetValue(field, out handle); } public bool IsEventBackingField(FieldDefinitionHandle field, out EventDefinitionHandle handle) { return eventLookup.TryGetValue(field, out handle); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/ReferenceLoadInfo.cs ================================================ // 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; namespace ICSharpCode.Decompiler.Metadata { public class ReferenceLoadInfo { readonly Dictionary loadedAssemblyReferences = new Dictionary(); public void AddMessage(string fullName, MessageKind kind, string message) { lock (loadedAssemblyReferences) { if (!loadedAssemblyReferences.TryGetValue(fullName, out var referenceInfo)) { referenceInfo = new UnresolvedAssemblyNameReference(fullName); loadedAssemblyReferences.Add(fullName, referenceInfo); } referenceInfo.Messages.Add((kind, message)); } } public void AddMessageOnce(string fullName, MessageKind kind, string message) { lock (loadedAssemblyReferences) { if (!loadedAssemblyReferences.TryGetValue(fullName, out var referenceInfo)) { referenceInfo = new UnresolvedAssemblyNameReference(fullName); loadedAssemblyReferences.Add(fullName, referenceInfo); referenceInfo.Messages.Add((kind, message)); } else { var lastMsg = referenceInfo.Messages.LastOrDefault(); if (kind != lastMsg.Item1 && message != lastMsg.Item2) referenceInfo.Messages.Add((kind, message)); } } } public bool TryGetInfo(string fullName, out UnresolvedAssemblyNameReference info) { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.TryGetValue(fullName, out info); } } public IReadOnlyList Entries { get { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.Values.ToList(); } } } public bool HasErrors { get { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.Any(i => i.Value.HasErrors); } } } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/Resource.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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. #nullable enable using System; using System.IO; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; namespace ICSharpCode.Decompiler.Metadata { public enum ResourceType { Linked, Embedded, AssemblyLinked, } public abstract class Resource { public virtual ResourceType ResourceType => ResourceType.Embedded; public virtual ManifestResourceAttributes Attributes => ManifestResourceAttributes.Public; public abstract string Name { get; } public abstract Stream? TryOpenStream(); public abstract long? TryGetLength(); } public class ByteArrayResource : Resource { public override string Name { get; } byte[] data; public ByteArrayResource(string name, byte[] data) { this.Name = name ?? throw new ArgumentNullException(nameof(name)); this.data = data ?? throw new ArgumentNullException(nameof(data)); } public override Stream TryOpenStream() { return new MemoryStream(data); } public override long? TryGetLength() { return data.Length; } } sealed class MetadataResource : Resource { public MetadataFile Module { get; } public ManifestResourceHandle Handle { get; } public bool IsNil => Handle.IsNil; public MetadataResource(MetadataFile module, ManifestResourceHandle handle) { this.Module = module ?? throw new ArgumentNullException(nameof(module)); this.Handle = handle; } public bool Equals(MetadataResource other) { return Module == other.Module && Handle == other.Handle; } public override bool Equals(object? obj) { if (obj is MetadataResource res) return Equals(res); return false; } public override int GetHashCode() { return unchecked(982451629 * Module.GetHashCode() + 982451653 * MetadataTokens.GetToken(Handle)); } public override string Name => Module.Metadata.GetString(Module.Metadata.GetManifestResource(Handle).Name); public override ManifestResourceAttributes Attributes => Module.Metadata.GetManifestResource(Handle).Attributes; public bool HasFlag(ManifestResourceAttributes flag) => (Attributes & flag) == flag; public override ResourceType ResourceType => GetResourceType(); ResourceType GetResourceType() { var resource = Module.Metadata.GetManifestResource(Handle); if (resource.Implementation.IsNil) return ResourceType.Embedded; if (resource.Implementation.Kind == HandleKind.AssemblyReference) return ResourceType.AssemblyLinked; return ResourceType.Linked; } unsafe bool TryReadResource(out byte* ptr, out long length) { ptr = null; length = 0; // embedded resources cannot be read from this binary. if (ResourceType != ResourceType.Embedded) return false; if (Module.CorHeader == null) return false; var resources = Module.CorHeader.ResourcesDirectory; // validate resources directory, GetSectionData throws on negative RVAs if (resources.RelativeVirtualAddress <= 0) return false; var sectionData = Module.GetSectionData(resources.RelativeVirtualAddress); // validate section length: we need at least 4 bytes to extract // the actual length of the resource blob. if (sectionData.Length < 4) return false; var offset = Module.Metadata.GetManifestResource(Handle).Offset; // validate resource offset if (offset < 0 || offset > sectionData.Length - 4) return false; ptr = sectionData.Pointer + offset; // get actual length of resource blob. length = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24); return length >= 0 && length <= sectionData.Length; } public override unsafe Stream? TryOpenStream() { if (!TryReadResource(out var ptr, out var length)) return null; return new ResourceMemoryStream(Module, ptr + sizeof(int), length); } public unsafe override long? TryGetLength() { if (!TryReadResource(out _, out var length)) return null; return length; } } sealed unsafe class ResourceMemoryStream : UnmanagedMemoryStream { #pragma warning disable IDE0052 // Remove unread private members readonly MetadataFile peReader; #pragma warning restore IDE0052 // Remove unread private members public ResourceMemoryStream(MetadataFile peReader, byte* data, long length) : base(data, length, length, FileAccess.Read) { // Keep the PEReader alive while the stream in in use. this.peReader = peReader; } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs ================================================ // 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.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { public static class SignatureBlobComparer { public static bool EqualsMethodSignature(BlobReader a, BlobReader b, MetadataReader contextForA, MetadataReader contextForB) { return EqualsMethodSignature(ref a, ref b, contextForA, contextForB); } static bool EqualsMethodSignature(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB) { SignatureHeader header; // compare signature headers if (a.RemainingBytes == 0 || b.RemainingBytes == 0 || (header = a.ReadSignatureHeader()) != b.ReadSignatureHeader()) return false; if (header.IsGeneric) { // read & compare generic parameter count if (!IsSameCompressedInteger(ref a, ref b, out _)) return false; } // read & compare parameter count if (!IsSameCompressedInteger(ref a, ref b, out int totalParameterCount)) return false; if (!IsSameCompressedInteger(ref a, ref b, out int typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; int i = 0; for (; i < totalParameterCount; i++) { if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; // varargs sentinel if (typeCode == 65) break; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; } for (; i < totalParameterCount; i++) { if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; } return true; } public static bool EqualsTypeSignature(BlobReader a, BlobReader b, MetadataReader contextForA, MetadataReader contextForB) { return EqualsTypeSignature(ref a, ref b, contextForA, contextForB); } static bool EqualsTypeSignature(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB) { if (!IsSameCompressedInteger(ref a, ref b, out int typeCode)) return false; return TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode); } static bool IsSameCompressedInteger(ref BlobReader a, ref BlobReader b, out int value) { return a.TryReadCompressedInteger(out value) && b.TryReadCompressedInteger(out int otherValue) && value == otherValue; } static bool IsSameCompressedSignedInteger(ref BlobReader a, ref BlobReader b, out int value) { return a.TryReadCompressedSignedInteger(out value) && b.TryReadCompressedSignedInteger(out int otherValue) && value == otherValue; } static bool TypesAreEqual(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB, int typeCode) { switch (typeCode) { case 0x1: // ELEMENT_TYPE_VOID case 0x2: // ELEMENT_TYPE_BOOLEAN case 0x3: // ELEMENT_TYPE_CHAR case 0x4: // ELEMENT_TYPE_I1 case 0x5: // ELEMENT_TYPE_U1 case 0x6: // ELEMENT_TYPE_I2 case 0x7: // ELEMENT_TYPE_U2 case 0x8: // ELEMENT_TYPE_I4 case 0x9: // ELEMENT_TYPE_U4 case 0xA: // ELEMENT_TYPE_I8 case 0xB: // ELEMENT_TYPE_U8 case 0xC: // ELEMENT_TYPE_R4 case 0xD: // ELEMENT_TYPE_R8 case 0xE: // ELEMENT_TYPE_STRING case 0x16: // ELEMENT_TYPE_TYPEDBYREF case 0x18: // ELEMENT_TYPE_I case 0x19: // ELEMENT_TYPE_U case 0x1C: // ELEMENT_TYPE_OBJECT return true; case 0xF: // ELEMENT_TYPE_PTR case 0x10: // ELEMENT_TYPE_BYREF case 0x45: // ELEMENT_TYPE_PINNED case 0x1D: // ELEMENT_TYPE_SZARRAY if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; return true; case 0x1B: // ELEMENT_TYPE_FNPTR if (!EqualsMethodSignature(ref a, ref b, contextForA, contextForB)) return false; return true; case 0x14: // ELEMENT_TYPE_ARRAY // element type if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; // rank if (!IsSameCompressedInteger(ref a, ref b, out _)) return false; // sizes if (!IsSameCompressedInteger(ref a, ref b, out int numOfSizes)) return false; for (int i = 0; i < numOfSizes; i++) { if (!IsSameCompressedInteger(ref a, ref b, out _)) return false; } // lower bounds if (!IsSameCompressedInteger(ref a, ref b, out int numOfLowerBounds)) return false; for (int i = 0; i < numOfLowerBounds; i++) { if (!IsSameCompressedSignedInteger(ref a, ref b, out _)) return false; } return true; case 0x1F: // ELEMENT_TYPE_CMOD_REQD case 0x20: // ELEMENT_TYPE_CMOD_OPT // modifier if (!TypeHandleEquals(ref a, ref b, contextForA, contextForB)) return false; // unmodified type if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; return true; case 0x15: // ELEMENT_TYPE_GENERICINST // generic type if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; if (!IsSameCompressedInteger(ref a, ref b, out int numOfArguments)) return false; for (int i = 0; i < numOfArguments; i++) { if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) return false; } return true; case 0x13: // ELEMENT_TYPE_VAR case 0x1E: // ELEMENT_TYPE_MVAR // index if (!IsSameCompressedInteger(ref a, ref b, out _)) return false; return true; case 0x11: // ELEMENT_TYPE_VALUETYPE case 0x12: // ELEMENT_TYPE_CLASS if (!TypeHandleEquals(ref a, ref b, contextForA, contextForB)) return false; return true; default: return false; } } static bool TypeHandleEquals(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB) { var typeA = a.ReadTypeHandle(); var typeB = b.ReadTypeHandle(); if (typeA.IsNil || typeB.IsNil) return false; return typeA.GetFullTypeName(contextForA) == typeB.GetFullTypeName(contextForB); } } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/TypeReferenceMetadata.cs ================================================ // Copyright (c) 2023 James May // // 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. #nullable enable using System; using System.Collections.Immutable; using System.Linq; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { #if !VSADDIN public sealed class TypeReferenceMetadata { readonly TypeReference entry; public MetadataReader Metadata { get; } public TypeReferenceHandle Handle { get; } string? name; public string Name { get { try { return name ??= Metadata.GetString(entry.Name); } catch (BadImageFormatException) { return name = $"TR:{Handle}"; } } } string? @namespace; public string Namespace { get { try { return @namespace ??= Metadata.GetString(entry.Namespace); } catch (BadImageFormatException) { return @namespace = $"namespace(TR:{Handle})"; } } } public EntityHandle ResolutionScope => entry.ResolutionScope; ImmutableArray memberReferences; public ImmutableArray MemberReferences { get { var value = memberReferences; if (value.IsDefault) { value = Metadata.MemberReferences .Select(r => new MemberReferenceMetadata(Metadata, r)) .Where(r => r.Parent == Handle) .OrderBy(r => r.Name) .ToImmutableArray(); memberReferences = value; } return value; } } ImmutableArray typeReferences; public ImmutableArray TypeReferences { get { var value = typeReferences; if (value.IsDefault) { value = Metadata.TypeReferences .Select(r => new TypeReferenceMetadata(Metadata, r)) .Where(r => r.ResolutionScope == Handle) .OrderBy(r => r.Name) .ToImmutableArray(); typeReferences = value; } return value; } } public TypeReferenceMetadata(MetadataReader metadata, TypeReferenceHandle handle) { Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); Handle = handle; entry = metadata.GetTypeReference(handle); } public override string ToString() => $"{Namespace}::{Name}"; } #endif } ================================================ FILE: ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs ================================================ // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Metadata { public enum TargetRuntime { Unknown, Net_1_0, Net_1_1, Net_2_0, Net_4_0 } public enum TargetFrameworkIdentifier { NETFramework, NETCoreApp, NETStandard, Silverlight, NET } enum DecompilerRuntime { NETFramework, NETCoreApp, Mono } /// /// Used to resolve assemblies referenced by an assembly. /// public class UniversalAssemblyResolver : AssemblyReferenceClassifier, IAssemblyResolver { static UniversalAssemblyResolver() { // TODO : test whether this works with Mono on *Windows*, not sure if we'll // ever need this... if (Type.GetType("Mono.Runtime") != null) decompilerRuntime = DecompilerRuntime.Mono; else if (typeof(object).Assembly.GetName().Name == "System.Private.CoreLib") decompilerRuntime = DecompilerRuntime.NETCoreApp; else if (Environment.OSVersion.Platform == PlatformID.Unix) decompilerRuntime = DecompilerRuntime.Mono; } readonly Lazy dotNetCorePathFinder; readonly bool throwOnError; readonly PEStreamOptions streamOptions; readonly MetadataReaderOptions metadataOptions; readonly string? mainAssemblyFileName; readonly string? baseDirectory; readonly List directories = new List(); static readonly List gac_paths = GetGacPaths(); static readonly DecompilerRuntime decompilerRuntime; public void AddSearchDirectory(string? directory) { directories.Add(directory); if (dotNetCorePathFinder.IsValueCreated) { dotNetCorePathFinder.Value.AddSearchDirectory(directory); } } public void RemoveSearchDirectory(string? directory) { directories.Remove(directory); if (dotNetCorePathFinder.IsValueCreated) { dotNetCorePathFinder.Value.RemoveSearchDirectory(directory); } } public string?[] GetSearchDirectories() { return directories.ToArray(); } readonly string targetFramework; readonly string runtimePack; readonly TargetFrameworkIdentifier targetFrameworkIdentifier; readonly Version targetFrameworkVersion; /// /// Creates a new instance of the . /// /// /// The full path to the "main assembly" (i.e., the assembly being decompiled). This is used to /// resolve assemblies that are located next the main assembly. If no full path is used, the resolver /// falls back to using . /// /// /// If an is thrown, in case the /// assembly reference cannot be resolved. /// /// /// The target framework name as used by . /// That is, "{framework},Version={version}": currently it supports ".NETCoreApp", ".NETStandard" and /// "Silverlight", if the string doesn't match any of these, the resolver falls back to ".NET Framework", /// which is "classic" .NET <= 4.8. /// /// /// Identifier of the runtime pack this assembly was compiled for. /// If omitted, falling back to "Microsoft.NETCore.App" and this is ignored in case of classic .NET /// Options used for the . /// Options used for the . public UniversalAssemblyResolver(string? mainAssemblyFileName, bool throwOnError, string? targetFramework, string? runtimePack = null, PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) { this.mainAssemblyFileName = mainAssemblyFileName; this.throwOnError = throwOnError; this.streamOptions = streamOptions; this.metadataOptions = metadataOptions; this.targetFramework = targetFramework ?? string.Empty; this.runtimePack = runtimePack ?? "Microsoft.NETCore.App"; (targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework); this.dotNetCorePathFinder = new Lazy(InitDotNetCorePathFinder); if (mainAssemblyFileName != null) { this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName); if (string.IsNullOrWhiteSpace(this.baseDirectory)) this.baseDirectory = Environment.CurrentDirectory; AddSearchDirectory(baseDirectory); } } internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework) { if (string.IsNullOrEmpty(targetFramework)) return (TargetFrameworkIdentifier.NETFramework, ZeroVersion); string[] tokens = targetFramework.Split(','); TargetFrameworkIdentifier identifier; switch (tokens[0].Trim().ToUpperInvariant()) { case ".NETCOREAPP": identifier = TargetFrameworkIdentifier.NETCoreApp; break; case ".NETSTANDARD": identifier = TargetFrameworkIdentifier.NETStandard; break; case "SILVERLIGHT": identifier = TargetFrameworkIdentifier.Silverlight; break; default: identifier = TargetFrameworkIdentifier.NETFramework; break; } Version? version = null; for (int i = 1; i < tokens.Length; i++) { var pair = tokens[i].Trim().Split('='); if (pair.Length != 2) continue; switch (pair[0].Trim().ToUpperInvariant()) { case "VERSION": var versionString = pair[1].TrimStart('v', 'V', ' ', '\t'); if (!Version.TryParse(versionString, out version)) { version = null; } else { version = new Version(version.Major, version.Minor, version.Build < 0 ? 0 : version.Build); } // .NET 5 or greater still use ".NETCOREAPP" as TargetFrameworkAttribute value... if (version?.Major >= 5 && identifier == TargetFrameworkIdentifier.NETCoreApp) identifier = TargetFrameworkIdentifier.NET; break; } } return (identifier, version ?? ZeroVersion); } #if !VSADDIN public MetadataFile? Resolve(IAssemblyReference name) { var file = FindAssemblyFile(name); return CreatePEFileFromFileName(file, ex => new ResolutionException(name, file, ex)); } public MetadataFile? ResolveModule(MetadataFile mainModule, string moduleName) { string? baseDirectory = Path.GetDirectoryName(mainModule.FileName); if (baseDirectory == null) return null; string moduleFileName = Path.Combine(baseDirectory, moduleName); return CreatePEFileFromFileName(moduleFileName, ex => new ResolutionException(mainModule.FileName, moduleName, moduleFileName, ex)); } private MetadataFile? CreatePEFileFromFileName(string? fileName, Func makeException) { if (fileName == null) { if (throwOnError) throw makeException(null); return null; } try { FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); return new PEFile(fileName, stream, streamOptions, metadataOptions); } catch (BadImageFormatException ex) { if (throwOnError) throw makeException(ex); } catch (IOException ex) { if (throwOnError) throw makeException(ex); } return null; } public Task ResolveAsync(IAssemblyReference name) { return Task.Run(() => Resolve(name)); } public Task ResolveModuleAsync(MetadataFile mainModule, string moduleName) { return Task.Run(() => ResolveModule(mainModule, moduleName)); } #endif public override bool IsSharedAssembly(IAssemblyReference reference, [NotNullWhen(true)] out string? runtimePack) { return dotNetCorePathFinder.Value.TryResolveDotNetCoreShared(reference, out runtimePack) != null; } public string? FindAssemblyFile(IAssemblyReference name) { if (name.IsWindowsRuntime) { return FindWindowsMetadataFile(name); } string? file; switch (targetFrameworkIdentifier) { case TargetFrameworkIdentifier.NET: case TargetFrameworkIdentifier.NETCoreApp: case TargetFrameworkIdentifier.NETStandard: if (IsZeroOrAllOnes(targetFrameworkVersion)) goto default; file = dotNetCorePathFinder.Value.TryResolveDotNetCore(name); if (file != null) return file; goto default; case TargetFrameworkIdentifier.Silverlight: if (IsZeroOrAllOnes(targetFrameworkVersion)) goto default; file = ResolveSilverlight(name, targetFrameworkVersion); if (file != null) return file; goto default; default: return ResolveInternal(name); } } DotNetCorePathFinder InitDotNetCorePathFinder() { DotNetCorePathFinder dotNetCorePathFinder; if (mainAssemblyFileName == null) dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion, runtimePack); else dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, runtimePack, targetFrameworkIdentifier, targetFrameworkVersion); foreach (var directory in directories) { dotNetCorePathFinder.AddSearchDirectory(directory); } return dotNetCorePathFinder; } string? FindWindowsMetadataFile(IAssemblyReference name) { // Finding Windows Metadata (winmd) is currently only supported on Windows. if (Environment.OSVersion.Platform != PlatformID.Win32NT) return null; // TODO : Find a way to detect the base directory for the required Windows SDK. string? basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Windows Kits", "10", "References"); if (!Directory.Exists(basePath)) return FindWindowsMetadataInSystemDirectory(name); // TODO : Find a way to detect the required Windows SDK version. var di = new DirectoryInfo(basePath); basePath = null; foreach (var versionFolder in di.EnumerateDirectories()) { basePath = versionFolder.FullName; } if (basePath == null) return FindWindowsMetadataInSystemDirectory(name); basePath = Path.Combine(basePath, name.Name); if (!Directory.Exists(basePath)) return FindWindowsMetadataInSystemDirectory(name); basePath = Path.Combine(basePath, FindClosestVersionDirectory(basePath, name.Version)); if (!Directory.Exists(basePath)) return FindWindowsMetadataInSystemDirectory(name); string file = Path.Combine(basePath, name.Name + ".winmd"); if (!File.Exists(file)) return FindWindowsMetadataInSystemDirectory(name); return file; } string? FindWindowsMetadataInSystemDirectory(IAssemblyReference name) { string file = Path.Combine(Environment.SystemDirectory, "WinMetadata", name.Name + ".winmd"); if (File.Exists(file)) return file; return null; } /// /// This only works on Windows /// string? ResolveSilverlight(IAssemblyReference name, Version? version) { string[] targetFrameworkSearchPaths = { Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Silverlight"), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Silverlight") }; foreach (var baseDirectory in targetFrameworkSearchPaths) { if (!Directory.Exists(baseDirectory)) continue; var versionDirectory = Path.Combine(baseDirectory, FindClosestVersionDirectory(baseDirectory, version)); var file = SearchDirectory(name, versionDirectory); if (file != null) return file; } return null; } string FindClosestVersionDirectory(string basePath, Version? version) { string? path = null; foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(DotNetCorePathFinder.ConvertToVersion) .Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) { if (path == null || version == null || folder.Item1 >= version) path = folder.Item2.Name; } return path ?? version?.ToString() ?? "."; } string? ResolveInternal(IAssemblyReference name) { if (name == null) throw new ArgumentNullException(nameof(name)); var assembly = SearchDirectory(name, directories); if (assembly != null) return assembly; string[] framework_dirs; string framework_dir; switch (decompilerRuntime) { case DecompilerRuntime.Mono: framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)!; framework_dirs = new[] { framework_dir, Path.Combine(framework_dir, "Facades") }; break; case DecompilerRuntime.NETCoreApp: string windir; try { windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); if (string.IsNullOrEmpty(windir)) { goto default; } } catch (PlatformNotSupportedException) { goto default; } framework_dir = Path.Combine(windir, "Microsoft.NET", "Framework64", "v4.0.30319"); framework_dirs = new[] { framework_dir }; break; default: framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)!; framework_dirs = new[] { framework_dir }; break; } if (IsSpecialVersionOrRetargetable(name)) { assembly = SearchDirectory(name, framework_dirs); if (assembly != null) return assembly; } if (name.Name == "mscorlib") { assembly = GetCorlib(name); if (assembly != null) return assembly; } assembly = GetAssemblyInGac(name); if (assembly != null) return assembly; // when decompiling assemblies that target frameworks prior to 4.0, we can fall back to the 4.0 assemblies in case the target framework is not installed. // but when looking for Microsoft.Build.Framework, Version=15.0.0.0 we should not use the version 4.0 assembly here so that the LoadedAssembly logic can instead fall back to version 15.1.0.0 if (name.Version <= new Version(4, 0, 0, 0)) { assembly = SearchDirectory(name, framework_dirs); if (assembly != null) return assembly; } if (throwOnError) throw new ResolutionException(name, null, null); return null; } #region .NET / mono GAC handling string? SearchDirectory(IAssemblyReference name, IEnumerable directories) { foreach (var directory in directories) { if (directory == null) continue; string? file = SearchDirectory(name, directory); if (file != null) return file; } return null; } static bool IsSpecialVersionOrRetargetable(IAssemblyReference reference) { return IsZeroOrAllOnes(reference.Version) || reference.IsRetargetable; } string? SearchDirectory(IAssemblyReference name, string directory) { var extensions = name.IsWindowsRuntime ? new[] { ".winmd", ".dll" } : new[] { ".dll", ".exe" }; foreach (var extension in extensions) { string file = Path.Combine(directory, name.Name + extension); if (!File.Exists(file)) continue; try { return file; } catch (BadImageFormatException) { continue; } } return null; } static bool IsZeroOrAllOnes(Version? version) { return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0) || (version.Major == 65535 && version.Minor == 65535 && version.Build == 65535 && version.Revision == 65535); } internal static Version ZeroVersion = new Version(0, 0, 0, 0); string? GetCorlib(IAssemblyReference reference) { var version = reference.Version; var corlib = typeof(object).Assembly.GetName(); if (decompilerRuntime != DecompilerRuntime.NETCoreApp) { if (corlib.Version == version || IsSpecialVersionOrRetargetable(reference)) return typeof(object).Module.FullyQualifiedName; } if (reference.PublicKeyToken == null) return null; if (version == null) return null; string? path; if (decompilerRuntime == DecompilerRuntime.Mono) { path = GetMonoMscorlibBasePath(version); } else { path = GetMscorlibBasePath(version, reference.PublicKeyToken.ToHexString(8)); } if (path == null) return null; var file = Path.Combine(path, "mscorlib.dll"); if (File.Exists(file)) return file; return null; } string? GetMscorlibBasePath(Version version, string? publicKeyToken) { string? GetSubFolderForVersion() { switch (version.Major) { case 1: if (version.MajorRevision == 3300) return "v1.0.3705"; return "v1.1.4322"; case 2: return "v2.0.50727"; case 4: return "v4.0.30319"; default: if (throwOnError) throw new NotSupportedException("Version not supported: " + version); return null; } } if (publicKeyToken == "969db8053d3322ac") { string programFiles = Environment.Is64BitOperatingSystem ? Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) : Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); string cfPath = $@"Microsoft.NET\SDK\CompactFramework\v{version.Major}.{version.Minor}\WindowsCE\"; string cfBasePath = Path.Combine(programFiles, cfPath); if (Directory.Exists(cfBasePath)) return cfBasePath; } else { string rootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Microsoft.NET"); string[] frameworkPaths = new[] { Path.Combine(rootPath, "Framework"), Path.Combine(rootPath, "Framework64") }; string? folder = GetSubFolderForVersion(); if (folder != null) { foreach (var path in frameworkPaths) { var basePath = Path.Combine(path, folder); if (Directory.Exists(basePath)) return basePath; } } } if (throwOnError) throw new NotSupportedException("Version not supported: " + version); return null; } string? GetMonoMscorlibBasePath(Version version) { var path = Directory.GetParent(typeof(object).Module.FullyQualifiedName)!.Parent!.FullName; if (version.Major == 1) path = Path.Combine(path, "1.0"); else if (version.Major == 2) { if (version.MajorRevision == 5) path = Path.Combine(path, "2.1"); else path = Path.Combine(path, "2.0"); } else if (version.Major == 4) path = Path.Combine(path, "4.0"); else { if (throwOnError) throw new NotSupportedException("Version not supported: " + version); return null; } return path; } public static List GetGacPaths() { if (decompilerRuntime == DecompilerRuntime.Mono) return GetDefaultMonoGacPaths(); var paths = new List(2); var windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); if (windir == null) return paths; paths.Add(Path.Combine(windir, "assembly")); paths.Add(Path.Combine(windir, "Microsoft.NET", "assembly")); return paths; } static List GetDefaultMonoGacPaths() { var paths = new List(1); var gac = GetCurrentMonoGac(); if (gac != null) paths.Add(gac); var gac_paths_env = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX"); if (string.IsNullOrEmpty(gac_paths_env)) return paths; var prefixes = gac_paths_env.Split(Path.PathSeparator); foreach (var prefix in prefixes) { if (string.IsNullOrEmpty(prefix)) continue; var gac_path = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac"); if (Directory.Exists(gac_path) && !paths.Contains(gac_path)) paths.Add(gac_path); } return paths; } static string GetCurrentMonoGac() { return Path.Combine( Directory.GetParent( Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)!)!.FullName, "gac"); } public static string? GetAssemblyInGac(IAssemblyReference reference) { if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) return null; if (decompilerRuntime == DecompilerRuntime.Mono) return GetAssemblyInMonoGac(reference); return GetAssemblyInNetGac(reference); } static string? GetAssemblyInMonoGac(IAssemblyReference reference) { for (int i = 0; i < gac_paths.Count; i++) { var gac_path = gac_paths[i]; var file = GetAssemblyFile(reference, string.Empty, gac_path); if (File.Exists(file)) return file; } return null; } static string? GetAssemblyInNetGac(IAssemblyReference reference) { var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; var prefixes = new[] { string.Empty, "v4.0_" }; for (int i = 0; i < gac_paths.Count; i++) { for (int j = 0; j < gacs.Length; j++) { var gac = Path.Combine(gac_paths[i], gacs[j]); var file = GetAssemblyFile(reference, prefixes[i], gac); if (File.Exists(file)) return file; } } return null; } static string GetAssemblyFile(IAssemblyReference reference, string prefix, string gac) { var gac_folder = new StringBuilder() .Append(prefix) .Append(reference.Version); gac_folder.Append("__"); for (int i = 0; i < reference.PublicKeyToken!.Length; i++) gac_folder.Append(reference.PublicKeyToken[i].ToString("x2")); return Path.Combine(gac, reference.Name, gac_folder.ToString(), reference.Name + ".dll"); } /// /// Gets the names of all assemblies in the GAC. /// public static IEnumerable EnumerateGac() { var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; foreach (var path in GetGacPaths()) { foreach (var gac in gacs) { string rootPath = Path.Combine(path, gac); if (!Directory.Exists(rootPath)) continue; foreach (var item in new DirectoryInfo(rootPath).EnumerateFiles("*.dll", SearchOption.AllDirectories)) { // The root of the GAC should only contain folders, but make sure we handle the case where it does NOT in case // someone has a non-standard layout (e.g. due to a broken installer). string? assemblyParentPath = Path.GetDirectoryName(item.FullName); if (assemblyParentPath?.Length > rootPath.Length) { string[]? name = assemblyParentPath.Substring(rootPath.Length + 1).Split(new[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); if (name?.Length != 2) continue; var match = Regex.Match(name[1], $"(v4.0_)?(?[^_]+)_(?[^_]+)?_(?[^_]+)"); if (!match.Success) continue; string culture = match.Groups["culture"].Value; if (string.IsNullOrEmpty(culture)) culture = "neutral"; yield return AssemblyNameReference.Parse(name[0] + ", Version=" + match.Groups["version"].Value + ", Culture=" + culture + ", PublicKeyToken=" + match.Groups["publicKey"].Value); } } } } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/UnresolvedAssemblyNameReference.cs ================================================ // 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; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Metadata { public sealed class UnresolvedAssemblyNameReference { public string FullName { get; } public bool HasErrors => Messages.Any(m => m.Item1 == MessageKind.Error); public List<(MessageKind, string)> Messages { get; } = new List<(MessageKind, string)>(); public UnresolvedAssemblyNameReference(string fullName) { this.FullName = fullName; } } public enum MessageKind { Error, Warning, Info } } ================================================ FILE: ICSharpCode.Decompiler/Metadata/WebCilFile.cs ================================================ // Copyright (c) 2024 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.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.MemoryMappedFiles; using System.Reflection.Metadata; using System.Text; using ICSharpCode.Decompiler.TypeSystem; #nullable enable namespace ICSharpCode.Decompiler.Metadata { public class WebCilFile : MetadataFile, IDisposable, IModuleReference { readonly MemoryMappedViewAccessor view; readonly long webcilOffset; private WebCilFile(string fileName, long webcilOffset, long metadataOffset, MemoryMappedViewAccessor view, ImmutableArray sectionHeaders, ImmutableArray wasmSections, MetadataReaderProvider provider, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) : base(MetadataFileKind.WebCIL, fileName, provider, metadataOptions, 0) { this.webcilOffset = webcilOffset; this.MetadataOffset = (int)metadataOffset; this.view = view; this.SectionHeaders = sectionHeaders; this.WasmSections = wasmSections; } public static WebCilFile? FromFile(string fileName, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) { using var memoryMappedFile = TryCreateFromFile(fileName); if (memoryMappedFile == null) { return null; } var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); try { // read magic "\0asm" if (view.ReadUInt32(0) != WASM_MAGIC) return null; // read version if (view.ReadUInt32(4) != 1) return null; using var stream = view.AsStream(); using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); stream.Position += 8; long metadataOffset = -1; List sections = new List(); while (stream.Position < stream.Length) { WasmSectionId id = (WasmSectionId)reader.ReadByte(); uint size = reader.ReadULEB128(); sections.Add(new WasmSection(id, stream.Position, size, view)); if (id == WasmSectionId.Custom && size == 0) { break; } stream.Seek(size, SeekOrigin.Current); } foreach (var section in sections) { if (section.Id != WasmSectionId.Data || metadataOffset > -1) continue; stream.Seek(section.Offset, SeekOrigin.Begin); uint numSegments = reader.ReadULEB128(); if (numSegments != 2) continue; // skip the first segment if (reader.ReadByte() != 1) continue; long segmentLength = reader.ReadULEB128(); long segmentStart = reader.BaseStream.Position; reader.BaseStream.Seek(segmentLength, SeekOrigin.Current); if (reader.ReadByte() != 1) continue; segmentLength = reader.ReadULEB128(); if (TryReadWebCilSegment(reader, out var header, out metadataOffset, out var webcilOffset, out var sectionHeaders)) { stream.Seek(metadataOffset, SeekOrigin.Begin); var metadata = MetadataReaderProvider.FromMetadataStream(stream, MetadataStreamOptions.LeaveOpen | MetadataStreamOptions.PrefetchMetadata); var result = new WebCilFile(fileName, webcilOffset, metadataOffset, view, ImmutableArray.Create(sectionHeaders), sections.ToImmutableArray(), metadata, metadataOptions); view = null; // don't dispose the view, we're still using it in the sections return result; } } return null; } finally { view?.Dispose(); } static MemoryMappedFile? TryCreateFromFile(string fileName) { try { return MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); } catch (IOException) { return null; } } } static unsafe bool TryReadWebCilSegment(BinaryReader reader, out WebcilHeader webcilHeader, out long metadataOffset, out long webcilOffset, [NotNullWhen(true)] out SectionHeader[]? sectionHeaders) { webcilHeader = default; metadataOffset = -1; sectionHeaders = null; webcilOffset = reader.BaseStream.Position; if (reader.ReadUInt32() != WEBCIL_MAGIC) return false; webcilHeader.VersionMajor = reader.ReadUInt16(); webcilHeader.VersionMinor = reader.ReadUInt16(); webcilHeader.CoffSections = reader.ReadUInt16(); _ = reader.ReadUInt16(); // reserved0 webcilHeader.PECliHeaderRVA = reader.ReadUInt32(); webcilHeader.PECliHeaderSize = reader.ReadUInt32(); webcilHeader.PEDebugRVA = reader.ReadUInt32(); webcilHeader.PEDebugSize = reader.ReadUInt32(); sectionHeaders = new SectionHeader[webcilHeader.CoffSections]; for (int i = 0; i < webcilHeader.CoffSections; i++) { sectionHeaders[i].VirtualSize = reader.ReadUInt32(); sectionHeaders[i].VirtualAddress = reader.ReadUInt32(); sectionHeaders[i].RawDataSize = reader.ReadUInt32(); sectionHeaders[i].RawDataPtr = reader.ReadUInt32(); } long corHeaderStart = TranslateRVA(sectionHeaders, webcilOffset, webcilHeader.PECliHeaderRVA); if (reader.BaseStream.Seek(corHeaderStart, SeekOrigin.Begin) != corHeaderStart) return false; int byteCount = reader.ReadInt32(); int majorVersion = reader.ReadUInt16(); int minorVersion = reader.ReadUInt16(); metadataOffset = TranslateRVA(sectionHeaders, webcilOffset, (uint)reader.ReadInt32()); return reader.BaseStream.Seek(metadataOffset, SeekOrigin.Begin) == metadataOffset; } public override int MetadataOffset { get; } public override bool IsMetadataOnly => false; private static int GetContainingSectionIndex(IEnumerable sections, int rva) { int i = 0; foreach (var section in sections) { if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) { return i; } i++; } return -1; } private static long TranslateRVA(IEnumerable sections, long webcilOffset, uint rva) { foreach (var section in sections) { if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) { return section.RawDataPtr + (rva - section.VirtualAddress) + webcilOffset; } } throw new BadImageFormatException("RVA not found in any section"); } public override MethodBodyBlock GetMethodBody(int rva) { var reader = GetSectionData(rva).GetReader(); return MethodBodyBlock.Create(reader); } public override int GetContainingSectionIndex(int rva) { return GetContainingSectionIndex(SectionHeaders, rva); } public override unsafe SectionData GetSectionData(int rva) { foreach (var section in SectionHeaders) { if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) { byte* ptr = (byte*)0; view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); return new SectionData(ptr + section.RawDataPtr + webcilOffset + (rva - section.VirtualAddress), (int)section.RawDataSize); } } throw new BadImageFormatException("RVA not found in any section"); } public override ImmutableArray SectionHeaders { get; } public ImmutableArray WasmSections { get; } IModule? IModuleReference.Resolve(ITypeResolveContext context) { return new MetadataModule(context.Compilation, this, TypeSystemOptions.Default); } public void Dispose() { view.Dispose(); } public struct WebcilHeader { public ushort VersionMajor; public ushort VersionMinor; public ushort CoffSections; public uint PECliHeaderRVA; public uint PECliHeaderSize; public uint PEDebugRVA; public uint PEDebugSize; } const uint WASM_MAGIC = 0x6d736100u; // "\0asm" const uint WEBCIL_MAGIC = 0x4c496257u; // "WbIL" [DebuggerDisplay("WasmSection {Id}: {Offset} {Size}")] public class WasmSection { public WasmSectionId Id; public long Offset; public uint Size; private MemoryMappedViewAccessor view; public WasmSection(WasmSectionId id, long offset, uint size, MemoryMappedViewAccessor view) { this.Id = id; this.Size = size; this.Offset = offset; this.view = view; } } public enum WasmSectionId : byte { // order matters: enum values must match the WebAssembly spec Custom = 0, Type = 1, Import = 2, Function = 3, Table = 4, Memory = 5, Global = 6, Export = 7, Start = 8, Element = 9, Code = 10, Data = 11, DataCount = 12, } } } ================================================ FILE: ICSharpCode.Decompiler/NRExtensions.cs ================================================ // Copyright (c) 2015 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.Reflection.Metadata; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { public static class NRExtensions { public static bool IsCompilerGenerated(this IEntity entity) { if (entity != null) { return entity.HasAttribute(KnownAttribute.CompilerGenerated); } return false; } public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IEntity entity) { if (entity == null) return false; if (entity.IsCompilerGenerated()) return true; return IsCompilerGeneratedOrIsInCompilerGeneratedClass(entity.DeclaringTypeDefinition); } public static bool HasGeneratedName(this IMember member) { return member.Name.StartsWith("<", StringComparison.Ordinal); } public static bool HasGeneratedName(this IType type) { return type.Name.StartsWith("<", StringComparison.Ordinal) || type.Name.Contains("<"); } public static bool IsAnonymousType(this IType type) { if (type == null) return false; if (string.IsNullOrEmpty(type.Namespace) && type.HasGeneratedName() && (type.Name.Contains("AnonType") || type.Name.Contains("AnonymousType"))) { ITypeDefinition td = type.GetDefinition(); return td != null && td.IsCompilerGenerated(); } return false; } public static bool ContainsAnonymousType(this IType type) { var visitor = new ContainsAnonTypeVisitor(); type.AcceptVisitor(visitor); return visitor.ContainsAnonType; } class ContainsAnonTypeVisitor : TypeVisitor { public bool ContainsAnonType; public override IType VisitOtherType(IType type) { if (IsAnonymousType(type)) ContainsAnonType = true; return base.VisitOtherType(type); } public override IType VisitTypeDefinition(ITypeDefinition type) { if (IsAnonymousType(type)) ContainsAnonType = true; return base.VisitTypeDefinition(type); } } internal static string GetDocumentation(this IEntity entity) { var docProvider = XmlDocLoader.LoadDocumentation(entity.ParentModule.MetadataFile); if (docProvider == null) return null; return docProvider.GetDocumentation(entity); } internal static System.Reflection.TypeAttributes GetMetadataAttributes(this ITypeDefinition type) { var metadata = type.ParentModule.MetadataFile?.Metadata; if (metadata == null || type.MetadataToken.IsNil) return 0; return metadata.GetTypeDefinition((TypeDefinitionHandle)type.MetadataToken).Attributes; } } } ================================================ FILE: ICSharpCode.Decompiler/NRTAttributes.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } } ================================================ FILE: ICSharpCode.Decompiler/Output/IAmbience.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Output { [Flags] public enum ConversionFlags { /// /// Convert only the name. /// None = 0, /// /// Show the parameter list /// ShowParameterList = 1, /// /// Show names for parameters /// ShowParameterNames = 2, /// /// Show the accessibility (private, public, etc.) /// ShowAccessibility = 4, /// /// Show the definition key word (class, struct, Sub, Function, etc.) /// ShowDefinitionKeyword = 8, /// /// Show the declaring type for the type or member /// ShowDeclaringType = 0x10, /// /// Show modifiers (virtual, override, etc.) /// ShowModifiers = 0x20, /// /// Show the return type /// ShowReturnType = 0x40, /// /// Use fully qualified names for types. /// UseFullyQualifiedTypeNames = 0x80, /// /// Show the list of type parameters on method and class declarations. /// Type arguments for parameter/return types are always shown. /// ShowTypeParameterList = 0x100, /// /// For fields, events and methods: adds a semicolon at the end. /// For properties: shows "{ get; }" or similar. /// ShowBody = 0x200, /// /// Use fully qualified names for members. /// UseFullyQualifiedEntityNames = 0x400, /// /// Instead of placing the return type before the entity name, /// append it after the parameter list, preceeded by a colon. /// PlaceReturnTypeAfterParameterList = 0x800, /// /// Show the variance modifier of the type parameter. /// If active, shows 'Func<in T, out TResult>' instead of 'Func<T, TResult>'. /// ShowTypeParameterVarianceModifier = 0x1000, /// /// Show modifiers of parameters, e.g. 'this', 'params', 'ref', 'out' and 'in'. /// ShowParameterModifiers = 0x2000, /// /// Show default values of parameters. /// ShowParameterDefaultValues = 0x4000, /// /// Use T? instead of Nullable<T>. /// UseNullableSpecifierForValueTypes = 0x8000, /// /// Support init accessors. /// SupportInitAccessors = 0x10000, /// /// Support record classes. /// SupportRecordClasses = 0x20000, /// /// Support record structs. /// SupportRecordStructs = 0x40000, /// /// Support >>> as unsigned right shift operator. /// SupportUnsignedRightShift = 0x80000, /// /// Support C# 11 operator checked. /// SupportOperatorChecked = 0x100000, /// /// Support C# 7.2 private protected. /// UsePrivateProtectedAccessibility = 0x200000, StandardConversionFlags = ShowParameterNames | ShowAccessibility | UsePrivateProtectedAccessibility | ShowParameterList | ShowParameterModifiers | ShowParameterDefaultValues | UseNullableSpecifierForValueTypes | ShowReturnType | ShowModifiers | ShowTypeParameterList | ShowTypeParameterVarianceModifier | ShowDefinitionKeyword | ShowBody, All = 0x1fffff, } /// /// Ambiences are used to convert type system symbols to text (usually for displaying the symbol to the user; e.g. in editor tooltips). /// public interface IAmbience { ConversionFlags ConversionFlags { get; set; } string ConvertSymbol(ISymbol symbol); string ConvertType(IType type); string ConvertConstantValue(object constantValue); string WrapComment(string comment); } } ================================================ FILE: ICSharpCode.Decompiler/Output/ITextOutput.cs ================================================ // 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 // 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.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { public interface ITextOutput { string IndentationString { get; set; } void Indent(); void Unindent(); void Write(char ch); void Write(string text); void WriteLine(); void WriteReference(OpCodeInfo opCode, bool omitSuffix = false); void WriteReference(MetadataFile metadata, Handle handle, string text, string protocol = "decompile", bool isDefinition = false); void WriteReference(IType type, string text, bool isDefinition = false); void WriteReference(IMember member, string text, bool isDefinition = false); void WriteLocalReference(string text, object reference, bool isDefinition = false); void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false); void MarkFoldEnd(); } public static class TextOutputExtensions { public static void Write(this ITextOutput output, string format, params object[] args) { output.Write(string.Format(format, args)); } public static void WriteLine(this ITextOutput output, string text) { output.Write(text); output.WriteLine(); } public static void WriteLine(this ITextOutput output, string format, params object[] args) { output.WriteLine(string.Format(format, args)); } } } ================================================ FILE: ICSharpCode.Decompiler/Output/PlainTextOutput.cs ================================================ // 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 // 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.IO; using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { public sealed class PlainTextOutput : ITextOutput { readonly TextWriter writer; int indent; bool needsIndent; int line = 1; int column = 1; public string IndentationString { get; set; } = "\t"; public PlainTextOutput(TextWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); this.writer = writer; } public PlainTextOutput() { this.writer = new StringWriter(); } public TextLocation Location { get { return new TextLocation(line, column + (needsIndent ? indent : 0)); } } public override string ToString() { return writer.ToString(); } public void Indent() { indent++; } public void Unindent() { indent--; } void WriteIndent() { if (needsIndent) { needsIndent = false; for (int i = 0; i < indent; i++) { writer.Write(IndentationString); } column += indent; } } public void Write(char ch) { WriteIndent(); writer.Write(ch); column++; } public void Write(string text) { WriteIndent(); writer.Write(text); column += text.Length; } public void WriteLine() { writer.WriteLine(); needsIndent = true; line++; column = 1; } public void WriteReference(Disassembler.OpCodeInfo opCode, bool omitSuffix = false) { if (omitSuffix) { int lastDot = opCode.Name.LastIndexOf('.'); if (lastDot > 0) { Write(opCode.Name.Remove(lastDot + 1)); } } else { Write(opCode.Name); } } public void WriteReference(MetadataFile module, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) { Write(text); } public void WriteReference(IType type, string text, bool isDefinition = false) { Write(text); } public void WriteReference(IMember member, string text, bool isDefinition = false) { Write(text); } public void WriteLocalReference(string text, object reference, bool isDefinition = false) { Write(text); } void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed, bool isDefinition) { } void ITextOutput.MarkFoldEnd() { } } internal class TextOutputWithRollback : ITextOutput { List> actions; ITextOutput target; public TextOutputWithRollback(ITextOutput target) { this.target = target; this.actions = new List>(); } string ITextOutput.IndentationString { get { return target.IndentationString; } set { target.IndentationString = value; } } public void Commit() { foreach (var action in actions) { action(target); } } public void Indent() { actions.Add(target => target.Indent()); } public void MarkFoldEnd() { actions.Add(target => target.MarkFoldEnd()); } public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false) { actions.Add(target => target.MarkFoldStart(collapsedText, defaultCollapsed)); } public void Unindent() { actions.Add(target => target.Unindent()); } public void Write(char ch) { actions.Add(target => target.Write(ch)); } public void Write(string text) { actions.Add(target => target.Write(text)); } public void WriteLine() { actions.Add(target => target.WriteLine()); } public void WriteLocalReference(string text, object reference, bool isDefinition = false) { actions.Add(target => target.WriteLocalReference(text, reference, isDefinition)); } public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false) { actions.Add(target => target.WriteReference(opCode)); } public void WriteReference(MetadataFile module, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) { actions.Add(target => target.WriteReference(module, handle, text, protocol, isDefinition)); } public void WriteReference(IType type, string text, bool isDefinition = false) { actions.Add(target => target.WriteReference(type, text, isDefinition)); } public void WriteReference(IMember member, string text, bool isDefinition = false) { actions.Add(target => target.WriteReference(member, text, isDefinition)); } } } ================================================ FILE: ICSharpCode.Decompiler/Output/TextOutputWriter.cs ================================================ // 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 // 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.IO; using System.Text; namespace ICSharpCode.Decompiler { public class TextOutputWriter : TextWriter { readonly ITextOutput output; public TextOutputWriter(ITextOutput output) { if (output == null) throw new ArgumentNullException(nameof(output)); this.output = output; } public override Encoding Encoding { get { return Encoding.UTF8; } } public override void Write(char value) { output.Write(value); } public override void Write(string value) { output.Write(value); } public override void WriteLine() { output.WriteLine(); } } } ================================================ FILE: ICSharpCode.Decompiler/Output/TextTokenWriter.cs ================================================ // 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 // 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; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler { public class TextTokenWriter : TokenWriter { readonly ITextOutput output; readonly DecompilerSettings settings; readonly IDecompilerTypeSystem typeSystem; readonly Stack nodeStack = new Stack(); int braceLevelWithinType = -1; bool inDocumentationComment = false; bool firstUsingDeclaration; bool lastUsingDeclaration; public TextTokenWriter(ITextOutput output, DecompilerSettings settings, IDecompilerTypeSystem typeSystem) { if (output == null) throw new ArgumentNullException(nameof(output)); if (settings == null) throw new ArgumentNullException(nameof(settings)); if (typeSystem == null) throw new ArgumentNullException(nameof(typeSystem)); this.output = output; this.settings = settings; this.typeSystem = typeSystem; } public override void WriteIdentifier(Identifier identifier) { if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { output.Write('@'); } var definition = GetCurrentDefinition(); string name = TextWriterTokenWriter.EscapeIdentifier(identifier.Name); switch (definition) { case IType t: output.WriteReference(t, name, true); return; case IMember m: output.WriteReference(m, name, true); return; } var member = GetCurrentMemberReference(); switch (member) { case IType t: output.WriteReference(t, name, false); return; case IMember m: output.WriteReference(m, name, false); return; } var localDefinition = GetCurrentLocalDefinition(identifier); if (localDefinition != null) { output.WriteLocalReference(name, localDefinition, isDefinition: true); return; } var localRef = GetCurrentLocalReference(); if (localRef != null) { output.WriteLocalReference(name, localRef); return; } if (firstUsingDeclaration && !lastUsingDeclaration) { output.MarkFoldStart(defaultCollapsed: !settings.ExpandUsingDeclarations); firstUsingDeclaration = false; } output.Write(name); } ISymbol GetCurrentMemberReference() { AstNode node = nodeStack.Peek(); var symbol = node.GetSymbol(); if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) { symbol = node.Parent.GetSymbol(); } if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) { var ctorSymbol = node.Parent.GetSymbol(); if (ctorSymbol != null) symbol = ctorSymbol; } if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { var declaringType = member.DeclaringType; if (declaringType != null && declaringType.Kind == TypeKind.Delegate) return null; } return FilterMember(symbol); } ISymbol FilterMember(ISymbol symbol) { if (symbol == null) return null; if (symbol is LocalFunctionMethod) return null; return symbol; } object GetCurrentLocalReference() { AstNode node = nodeStack.Peek(); ILVariable variable = node.Annotation()?.Variable; if (variable != null) return variable; var letClauseVariable = node.Annotation(); if (letClauseVariable != null) return letClauseVariable; if (node is GotoStatement gotoStatement) { var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); if (method != null) return method + gotoStatement.Label; } if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) { var symbol = node.Parent.GetSymbol(); if (symbol is LocalFunctionMethod) return symbol; } return null; } object GetCurrentLocalDefinition(Identifier id) { AstNode node = nodeStack.Peek(); if (node is Identifier && node.Parent != null) node = node.Parent; if (node is ParameterDeclaration || node is VariableInitializer || node is CatchClause || node is VariableDesignation) { var variable = node.Annotation()?.Variable; if (variable != null) return variable; } if (id.Role == QueryJoinClause.IntoIdentifierRole || id.Role == QueryJoinClause.JoinIdentifierRole) { var variable = id.Annotation()?.Variable; if (variable != null) return variable; } if (node is QueryLetClause) { var variable = node.Annotation(); if (variable != null) return variable; } if (node is LabelStatement label) { var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); if (method != null) return method + label.Label; } if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) { var localFunction = node.Parent.GetResolveResult() as MemberResolveResult; if (localFunction != null) return localFunction.Member; } return null; } ISymbol GetCurrentDefinition() { if (nodeStack == null || nodeStack.Count == 0) return null; var node = nodeStack.Peek(); if (node is Identifier) node = node.Parent; if (IsDefinition(ref node)) return node.GetSymbol(); return null; } public override void WriteKeyword(Role role, string keyword) { //To make reference for 'this' and 'base' keywords in the ClassName():this() expression if (role == ConstructorInitializer.ThisKeywordRole || role == ConstructorInitializer.BaseKeywordRole) { if (nodeStack.Peek() is ConstructorInitializer initializer && initializer.GetSymbol() is IMember member) { output.WriteReference(member, keyword); return; } } output.Write(keyword); } public override void WriteToken(Role role, string token) { switch (token) { case "{": if (role != Roles.LBrace) { output.Write("{"); break; } if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration) braceLevelWithinType++; if (nodeStack.PeekOrDefault() is TypeDeclaration or ExtensionDeclaration or BlockStatement { Parent: EntityDeclaration or LocalFunctionDeclarationStatement or AnonymousMethodExpression or LambdaExpression } || settings.FoldBraces) { output.MarkFoldStart(defaultCollapsed: !settings.ExpandMemberDefinitions && braceLevelWithinType == 1, isDefinition: braceLevelWithinType == 1); } output.Write("{"); break; case "}": output.Write('}'); if (role != Roles.RBrace) break; if (nodeStack.PeekOrDefault() is TypeDeclaration or ExtensionDeclaration or BlockStatement { Parent: EntityDeclaration or LocalFunctionDeclarationStatement or AnonymousMethodExpression or LambdaExpression } || settings.FoldBraces) output.MarkFoldEnd(); if (braceLevelWithinType >= 0) braceLevelWithinType--; break; default: // Attach member reference to token only if there's no identifier in the current node. var member = GetCurrentMemberReference(); var node = nodeStack.Peek(); if (member != null && node.GetChildByRole(Roles.Identifier).IsNull) { switch (member) { case IType t: output.WriteReference(t, token, false); return; case IMember m: output.WriteReference(m, token, false); return; } } else output.Write(token); break; } } public override void Space() { output.Write(' '); } public override void Indent() { output.Indent(); } public override void Unindent() { output.Unindent(); } public override void NewLine() { if (!firstUsingDeclaration && lastUsingDeclaration) { output.MarkFoldEnd(); lastUsingDeclaration = false; } output.WriteLine(); } public override void WriteComment(CommentType commentType, string content) { switch (commentType) { case CommentType.SingleLine: output.Write("//"); output.WriteLine(content); break; case CommentType.MultiLine: output.Write("/*"); output.Write(content); output.Write("*/"); break; case CommentType.Documentation: bool isLastLine = !(nodeStack.Peek().NextSibling is Comment); if (!inDocumentationComment && !isLastLine) { inDocumentationComment = true; output.MarkFoldStart("///" + content, true); } output.Write("///"); output.Write(content); if (inDocumentationComment && isLastLine) { inDocumentationComment = false; output.MarkFoldEnd(); } output.WriteLine(); break; default: output.Write(content); break; } } public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { // pre-processor directive must start on its own line output.Write('#'); output.Write(type.ToString().ToLowerInvariant()); if (!string.IsNullOrEmpty(argument)) { output.Write(' '); output.Write(argument); } output.WriteLine(); } public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, format); } public override void WriteInterpolatedText(string text) { output.Write(TextWriterTokenWriter.ConvertString(text)); } public override void WritePrimitiveType(string type) { switch (type) { case "new": output.Write(type); output.Write("()"); break; case "bool": case "byte": case "sbyte": case "short": case "ushort": case "int": case "uint": case "long": case "ulong": case "float": case "double": case "decimal": case "char": case "string": case "object": var node = nodeStack.Peek(); ISymbol symbol; if (node.Role == Roles.Type && node.Parent is ObjectCreateExpression) { symbol = node.Parent.GetSymbol(); } else { symbol = nodeStack.Peek().GetSymbol(); } if (symbol == null) goto default; switch (symbol) { case IType t: output.WriteReference(t, type, false); return; case IMember m: output.WriteReference(m, type, false); return; } break; default: output.Write(type); break; } } public override void StartNode(AstNode node) { if (nodeStack.Count == 0) { if (IsUsingDeclaration(node)) { firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling); lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling); } else { firstUsingDeclaration = false; lastUsingDeclaration = false; } } nodeStack.Push(node); } private bool IsUsingDeclaration(AstNode node) { return node is UsingDeclaration || node is UsingAliasDeclaration; } public override void EndNode(AstNode node) { if (nodeStack.Pop() != node) throw new InvalidOperationException(); } public static bool IsDefinition(ref AstNode node) { if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement)) return true; if (node is VariableInitializer && node.Parent is FieldDeclaration or EventDeclaration) { node = node.Parent; return true; } if (node is FixedVariableInitializer && node.Parent is FixedFieldDeclaration) { node = node.Parent; return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/PackageReadme.md ================================================ ## About ICSharpCode.Decompiler is the decompiler engine used in [ILSpy](https://github.com/icsharpcode/ILSpy/). You can learn how to use it from the following samples/real-world applications: * **decompiler-nuget-demos.ipynb** in the [ILSpy repository](https://github.com/icsharpcode/ILSpy/). This shows the basics of using the NuGet. * **ILSpyCmd** project in the [ILSpy repository](https://github.com/icsharpcode/ILSpy/tree/master/ICSharpCode.ILSpyCmd). This dotnet tool shows automation of whole assembly decompilation. * **ILSpy.Backend** in the [ILSpy Visual Studio Code Extension repository](https://github.com/icsharpcode/ilspy-vscode/tree/master/backend). This LSP shows listing / decompiling members. * **ILSpy** itself. Complex, recommended only for advanced users and definitely **not** the place to get started. ================================================ FILE: ICSharpCode.Decompiler/PartialTypeInfo.cs ================================================ // Copyright (c) 2022 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler { public class PartialTypeInfo { readonly HashSet declaredMembers = new(); public PartialTypeInfo(ITypeDefinition declaringTypeDefinition) { DeclaringTypeDefinitionHandle = (TypeDefinitionHandle)declaringTypeDefinition.MetadataToken; } public PartialTypeInfo(TypeDefinitionHandle declaringTypeDefinitionHandle) { DeclaringTypeDefinitionHandle = declaringTypeDefinitionHandle; } public TypeDefinitionHandle DeclaringTypeDefinitionHandle { get; } public void AddDeclaredMember(IMember member) { declaredMembers.Add(member.MetadataToken); } public void AddDeclaredMember(EntityHandle handle) { declaredMembers.Add(handle); } public bool IsDeclaredMember(IMember member) { return declaredMembers.Contains(member.MetadataToken); } public bool IsDeclaredMember(EntityHandle handle) { return declaredMembers.Contains(handle); } public void AddDeclaredMembers(PartialTypeInfo info) { foreach (var member in info.declaredMembers) { declaredMembers.Add(member); } } public string DebugOutput => string.Join(", ", declaredMembers.Select(m => MetadataTokens.GetToken(m).ToString("X"))); } } ================================================ FILE: ICSharpCode.Decompiler/Properties/AssemblyInfo.cs ================================================ #region Using directives using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #endregion [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] [assembly: AssemblyVersion(DecompilerVersionInfo.Version)] [assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithCommitHash)] [assembly: InternalsVisibleTo("ICSharpCode.Decompiler.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004dcf3979c4e902efa4dd2163a039701ed5822e6f1134d77737296abbb97bf0803083cfb2117b4f5446a217782f5c7c634f9fe1fc60b4c11d62c5b3d33545036706296d31903ddcf750875db38a8ac379512f51620bb948c94d0831125fbc5fe63707cbb93f48c1459c4d1749eb7ac5e681a2f0d6d7c60fa527a3c0b8f92b02bf")] [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] ================================================ FILE: ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs ================================================ public static class DecompilerVersionInfo { public const string Major = "10"; public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; public const string VersionName = "preview3"; public const string Version = Major + "." + Minor + "." + Build + "." + Revision; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; public const string FullVersionWithShortCommitHash = FullVersion + "+$INSERTSHORTCOMMITHASH$"; public const string FullVersionWithCommitHash = FullVersion + "+$INSERTCOMMITHASH$"; } ================================================ FILE: ICSharpCode.Decompiler/SRMExtensions.cs ================================================ using System; using System.Collections.Immutable; using System.IO; using System.IO.MemoryMappedFiles; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler { public static partial class SRMExtensions { public static bool HasFlag(this TypeDefinition typeDefinition, TypeAttributes attribute) => (typeDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this MethodDefinition methodDefinition, MethodAttributes attribute) => (methodDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this FieldDefinition fieldDefinition, FieldAttributes attribute) => (fieldDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this PropertyDefinition propertyDefinition, PropertyAttributes attribute) => (propertyDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this EventDefinition eventDefinition, EventAttributes attribute) => (eventDefinition.Attributes & attribute) == attribute; public static bool IsTypeKind(this HandleKind kind) => kind == HandleKind.TypeDefinition || kind == HandleKind.TypeReference || kind == HandleKind.TypeSpecification; public static bool IsMemberKind(this HandleKind kind) => kind == HandleKind.MethodDefinition || kind == HandleKind.PropertyDefinition || kind == HandleKind.FieldDefinition || kind == HandleKind.EventDefinition || kind == HandleKind.MemberReference || kind == HandleKind.MethodSpecification; public static bool IsEntityHandle(this Handle handle) => handle.IsNil || (byte)handle.Kind < 112; public static bool IsValueType(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsValueType(reader); } public static bool IsValueType(this TypeDefinition typeDefinition, MetadataReader reader) { EntityHandle baseType = typeDefinition.GetBaseTypeOrNil(); if (baseType.IsNil) return false; if (baseType.IsKnownType(reader, KnownTypeCode.Enum)) return true; if (!baseType.IsKnownType(reader, KnownTypeCode.ValueType)) return false; var thisType = typeDefinition.GetFullTypeName(reader); return !thisType.IsKnownType(KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsEnum(reader); } public static bool IsEnum(this TypeDefinition typeDefinition, MetadataReader reader) { EntityHandle baseType = typeDefinition.GetBaseTypeOrNil(); if (baseType.IsNil) return false; return baseType.IsKnownType(reader, KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader, out PrimitiveTypeCode underlyingType) { return reader.GetTypeDefinition(handle).IsEnum(reader, out underlyingType); } public static bool IsEnum(this TypeDefinition typeDefinition, MetadataReader reader, out PrimitiveTypeCode underlyingType) { underlyingType = 0; EntityHandle baseType = typeDefinition.GetBaseTypeOrNil(); if (baseType.IsNil) return false; if (!baseType.IsKnownType(reader, KnownTypeCode.Enum)) return false; foreach (var handle in typeDefinition.GetFields()) { var field = reader.GetFieldDefinition(handle); if ((field.Attributes & FieldAttributes.Static) != 0) continue; var blob = reader.GetBlobReader(field.Signature); if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) return false; underlyingType = (PrimitiveTypeCode)blob.ReadByte(); return true; } return false; } public static bool IsDelegate(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsDelegate(reader); } public static bool IsDelegate(this TypeDefinition typeDefinition, MetadataReader reader) { var baseType = typeDefinition.GetBaseTypeOrNil(); return !baseType.IsNil && baseType.IsKnownType(reader, KnownTypeCode.MulticastDelegate); } public static bool HasBody(this MethodDefinition methodDefinition) { const MethodAttributes noBodyAttrs = MethodAttributes.Abstract | MethodAttributes.PinvokeImpl; const MethodImplAttributes noBodyImplAttrs = MethodImplAttributes.InternalCall | MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.Runtime; return (methodDefinition.Attributes & noBodyAttrs) == 0 && (methodDefinition.ImplAttributes & noBodyImplAttrs) == 0 && methodDefinition.RelativeVirtualAddress > 0; } public static int GetCodeSize(this MethodBodyBlock body) { if (body == null) throw new ArgumentNullException(nameof(body)); return body.GetILReader().Length; } public static MethodDefinitionHandle GetAny(this PropertyAccessors accessors) { if (!accessors.Getter.IsNil) return accessors.Getter; return accessors.Setter; } public static MethodDefinitionHandle GetAny(this EventAccessors accessors) { if (!accessors.Adder.IsNil) return accessors.Adder; if (!accessors.Remover.IsNil) return accessors.Remover; return accessors.Raiser; } public static EntityHandle GetGenericType(this in TypeSpecification ts, MetadataReader metadata) { if (ts.Signature.IsNil) return default; // Do a quick scan using BlobReader var signature = metadata.GetBlobReader(ts.Signature); // When dealing with FSM implementations, we can safely assume that if it's a type spec, // it must be a generic type instance. if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance) return default; // Skip over the rawTypeKind: value type or class var rawTypeKind = signature.ReadCompressedInteger(); if (rawTypeKind < 17 || rawTypeKind > 18) return default; // Only read the generic type, ignore the type arguments return signature.ReadTypeHandle(); } public static EntityHandle GetDeclaringType(this EntityHandle entity, MetadataReader metadata) { switch (entity.Kind) { case HandleKind.TypeDefinition: var td = metadata.GetTypeDefinition((TypeDefinitionHandle)entity); return td.GetDeclaringType(); case HandleKind.TypeReference: var tr = metadata.GetTypeReference((TypeReferenceHandle)entity); return tr.GetDeclaringType(); case HandleKind.TypeSpecification: var ts = metadata.GetTypeSpecification((TypeSpecificationHandle)entity); return ts.GetGenericType(metadata).GetDeclaringType(metadata); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)entity); return fd.GetDeclaringType(); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); return md.GetDeclaringType(); case HandleKind.MemberReference: var mr = metadata.GetMemberReference((MemberReferenceHandle)entity); return mr.Parent; case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)entity); return metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)entity); return metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); case HandleKind.MethodSpecification: var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)entity); return ms.Method.GetDeclaringType(metadata); default: throw new ArgumentOutOfRangeException(); } } public static TypeReferenceHandle GetDeclaringType(this in TypeReference tr) { switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return (TypeReferenceHandle)tr.ResolutionScope; default: return default(TypeReferenceHandle); } } public static FullTypeName GetFullTypeName(this EntityHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); switch (handle.Kind) { case HandleKind.TypeDefinition: return ((TypeDefinitionHandle)handle).GetFullTypeName(reader); case HandleKind.TypeReference: return ((TypeReferenceHandle)handle).GetFullTypeName(reader); case HandleKind.TypeSpecification: return ((TypeSpecificationHandle)handle).GetFullTypeName(reader); default: throw new ArgumentOutOfRangeException(); } } public static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownTypeCode knownType) { return IsKnownType(handle, reader, KnownTypeReference.Get(knownType).TypeName); } internal static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownAttribute knownType) { return IsKnownType(handle, reader, knownType.GetTypeName()); } private static bool IsKnownType(EntityHandle handle, MetadataReader reader, TopLevelTypeName knownType) { if (handle.IsNil) return false; StringHandle nameHandle, namespaceHandle; try { switch (handle.Kind) { case HandleKind.TypeReference: var tr = reader.GetTypeReference((TypeReferenceHandle)handle); // ignore exported and nested types if (tr.ResolutionScope.IsNil || tr.ResolutionScope.Kind == HandleKind.TypeReference) return false; nameHandle = tr.Name; namespaceHandle = tr.Namespace; break; case HandleKind.TypeDefinition: var td = reader.GetTypeDefinition((TypeDefinitionHandle)handle); if (td.IsNested) return false; nameHandle = td.Name; namespaceHandle = td.Namespace; break; case HandleKind.TypeSpecification: var ts = reader.GetTypeSpecification((TypeSpecificationHandle)handle); var blob = reader.GetBlobReader(ts.Signature); return SignatureIsKnownType(reader, knownType, ref blob); default: return false; } } catch (BadImageFormatException) { // ignore bad metadata when trying to resolve ResolutionScope et al. return false; } if (knownType.TypeParameterCount == 0) { if (!reader.StringComparer.Equals(nameHandle, knownType.Name)) return false; } else { string name = reader.GetString(nameHandle); name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out int typeParameterCount); if (typeParameterCount != knownType.TypeParameterCount || name != knownType.Name) return false; } if (namespaceHandle.IsNil) { return knownType.Namespace.Length == 0; } else { return reader.StringComparer.Equals(namespaceHandle, knownType.Namespace); } } private static bool SignatureIsKnownType(MetadataReader reader, TopLevelTypeName knownType, ref BlobReader blob) { if (!blob.TryReadCompressedInteger(out int typeCode)) return false; switch (typeCode) { case 0x1: // ELEMENT_TYPE_VOID return knownType.IsKnownType(KnownTypeCode.Void); case 0x2: // ELEMENT_TYPE_BOOLEAN return knownType.IsKnownType(KnownTypeCode.Boolean); case 0x3: // ELEMENT_TYPE_CHAR return knownType.IsKnownType(KnownTypeCode.Char); case 0x4: // ELEMENT_TYPE_I1 return knownType.IsKnownType(KnownTypeCode.SByte); case 0x5: // ELEMENT_TYPE_U1 return knownType.IsKnownType(KnownTypeCode.Byte); case 0x6: // ELEMENT_TYPE_I2 return knownType.IsKnownType(KnownTypeCode.Int16); case 0x7: // ELEMENT_TYPE_U2 return knownType.IsKnownType(KnownTypeCode.UInt16); case 0x8: // ELEMENT_TYPE_I4 return knownType.IsKnownType(KnownTypeCode.Int32); case 0x9: // ELEMENT_TYPE_U4 return knownType.IsKnownType(KnownTypeCode.UInt32); case 0xA: // ELEMENT_TYPE_I8 return knownType.IsKnownType(KnownTypeCode.Int64); case 0xB: // ELEMENT_TYPE_U8 return knownType.IsKnownType(KnownTypeCode.UInt64); case 0xC: // ELEMENT_TYPE_R4 return knownType.IsKnownType(KnownTypeCode.Single); case 0xD: // ELEMENT_TYPE_R8 return knownType.IsKnownType(KnownTypeCode.Double); case 0xE: // ELEMENT_TYPE_STRING return knownType.IsKnownType(KnownTypeCode.String); case 0x16: // ELEMENT_TYPE_TYPEDBYREF return knownType.IsKnownType(KnownTypeCode.TypedReference); case 0x18: // ELEMENT_TYPE_I return knownType.IsKnownType(KnownTypeCode.IntPtr); case 0x19: // ELEMENT_TYPE_U return knownType.IsKnownType(KnownTypeCode.UIntPtr); case 0x1C: // ELEMENT_TYPE_OBJECT return knownType.IsKnownType(KnownTypeCode.Object); case 0xF: // ELEMENT_TYPE_PTR case 0x10: // ELEMENT_TYPE_BYREF case 0x45: // ELEMENT_TYPE_PINNED case 0x1D: // ELEMENT_TYPE_SZARRAY case 0x1B: // ELEMENT_TYPE_FNPTR case 0x14: // ELEMENT_TYPE_ARRAY return false; case 0x1F: // ELEMENT_TYPE_CMOD_REQD case 0x20: // ELEMENT_TYPE_CMOD_OPT // modifier blob.ReadTypeHandle(); // skip modifier return SignatureIsKnownType(reader, knownType, ref blob); case 0x15: // ELEMENT_TYPE_GENERICINST // generic type return SignatureIsKnownType(reader, knownType, ref blob); case 0x13: // ELEMENT_TYPE_VAR case 0x1E: // ELEMENT_TYPE_MVAR // index return false; case 0x11: // ELEMENT_TYPE_VALUETYPE case 0x12: // ELEMENT_TYPE_CLASS return IsKnownType(blob.ReadTypeHandle(), reader, knownType); default: return false; } } public static FullTypeName GetFullTypeName(this TypeSpecificationHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); var ts = reader.GetTypeSpecification(handle); return ts.DecodeSignature(new Metadata.FullTypeNameSignatureDecoder(reader), default(Unit)); } public static FullTypeName GetFullTypeName(this TypeReferenceHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); var tr = reader.GetTypeReference(handle); string name; try { name = reader.GetString(tr.Name); } catch (BadImageFormatException) { name = $"TR{reader.GetToken(handle):x8}"; } name = ReflectionHelper.SplitTypeParameterCountFromReflectionName( name, out var typeParameterCount); TypeReferenceHandle declaringTypeHandle; try { declaringTypeHandle = tr.GetDeclaringType(); } catch (BadImageFormatException) { declaringTypeHandle = default; } if (declaringTypeHandle.IsNil) { string ns; try { ns = tr.Namespace.IsNil ? "" : reader.GetString(tr.Namespace); } catch (BadImageFormatException) { ns = ""; } return new FullTypeName(new TopLevelTypeName(ns, name, typeParameterCount)); } else { return declaringTypeHandle.GetFullTypeName(reader).NestedType(name, typeParameterCount); } } public static FullTypeName GetFullTypeName(this TypeDefinitionHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); return reader.GetTypeDefinition(handle).GetFullTypeName(reader); } public static FullTypeName GetFullTypeName(this TypeDefinition td, MetadataReader reader) { TypeDefinitionHandle declaringTypeHandle; string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName( reader.GetString(td.Name), out var typeParameterCount); if ((declaringTypeHandle = td.GetDeclaringType()).IsNil) { string @namespace = td.Namespace.IsNil ? "" : reader.GetString(td.Namespace); return new FullTypeName(new TopLevelTypeName(@namespace, name, typeParameterCount)); } else { return declaringTypeHandle.GetFullTypeName(reader).NestedType(name, typeParameterCount); } } public static FullTypeName GetFullTypeName(this ExportedType type, MetadataReader metadata) { string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName( metadata.GetString(type.Name), out int typeParameterCount); if (type.Implementation.Kind == HandleKind.ExportedType) { var outerType = metadata.GetExportedType((ExportedTypeHandle)type.Implementation); return outerType.GetFullTypeName(metadata).NestedType(name, typeParameterCount); } else { string ns = type.Namespace.IsNil ? "" : metadata.GetString(type.Namespace); return new TopLevelTypeName(ns, name, typeParameterCount); } } public static bool IsAnonymousType(this TypeDefinition type, MetadataReader metadata) { string name = metadata.GetString(type.Name); if (type.Namespace.IsNil && type.HasGeneratedName(metadata) && (name.Contains("AnonType") || name.Contains("AnonymousType"))) { return type.IsCompilerGenerated(metadata); } return false; } #region HasGeneratedName public static bool IsGeneratedName(this StringHandle handle, MetadataReader metadata) { return !handle.IsNil && (metadata.GetString(handle).StartsWith("<", StringComparison.Ordinal) || metadata.GetString(handle).Contains("$")); } public static bool HasGeneratedName(this MethodDefinitionHandle handle, MetadataReader metadata) { return metadata.GetMethodDefinition(handle).Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this TypeDefinitionHandle handle, MetadataReader metadata) { return metadata.GetTypeDefinition(handle).Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this TypeDefinition type, MetadataReader metadata) { return type.Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this FieldDefinitionHandle handle, MetadataReader metadata) { return metadata.GetFieldDefinition(handle).Name.IsGeneratedName(metadata); } #endregion #region IsCompilerGenerated public static bool IsCompilerGenerated(this MethodDefinitionHandle handle, MetadataReader metadata) { return metadata.GetMethodDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this MethodDefinitionHandle handle, MetadataReader metadata) { MethodDefinition method = metadata.GetMethodDefinition(handle); if (method.IsCompilerGenerated(metadata)) return true; TypeDefinitionHandle declaringTypeHandle = method.GetDeclaringType(); if (!declaringTypeHandle.IsNil && declaringTypeHandle.IsCompilerGenerated(metadata)) return true; return false; } public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this TypeDefinitionHandle handle, MetadataReader metadata) { TypeDefinition type = metadata.GetTypeDefinition(handle); if (type.IsCompilerGenerated(metadata)) return true; TypeDefinitionHandle declaringTypeHandle = type.GetDeclaringType(); if (!declaringTypeHandle.IsNil && declaringTypeHandle.IsCompilerGenerated(metadata)) return true; return false; } public static bool IsCompilerGenerated(this MethodDefinition method, MetadataReader metadata) { return method.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this FieldDefinitionHandle handle, MetadataReader metadata) { return metadata.GetFieldDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGenerated(this FieldDefinition field, MetadataReader metadata) { return field.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this TypeDefinitionHandle handle, MetadataReader metadata) { return metadata.GetTypeDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGenerated(this TypeDefinition type, MetadataReader metadata) { return type.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } #endregion #region Attribute extensions /// /// Gets the type of the attribute. /// public static EntityHandle GetAttributeType(this SRM.CustomAttribute attribute, MetadataReader reader) { switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: var md = reader.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor); return md.GetDeclaringType(); case HandleKind.MemberReference: var mr = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor); return mr.Parent; default: throw new BadImageFormatException("Unexpected token kind for attribute constructor: " + attribute.Constructor.Kind); } } public static bool HasKnownAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata, KnownAttribute type) { foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, type)) return true; } return false; } internal static bool IsKnownAttribute(this SRM.CustomAttribute attr, MetadataReader metadata, KnownAttribute attrType) { return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType); } public static Nullability? GetNullableContext(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) { foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullableContext)) { // Decode CustomAttributeValue value; try { value = customAttribute.DecodeValue( Metadata.MetadataExtensions.MinimalAttributeTypeProvider); } catch (BadImageFormatException) { continue; } catch (Metadata.EnumUnderlyingTypeResolveException) { continue; } if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is byte b && b <= 2) { return (Nullability)b; } } } return null; } #endregion public static unsafe BlobReader GetInitialValue(this FieldDefinition field, MetadataFile pefile, ICompilation typeSystem) { if (!field.HasFlag(FieldAttributes.HasFieldRVA)) return default; int rva = field.GetRelativeVirtualAddress(); if (rva == 0) return default; int size = field.DecodeSignature(new FieldValueSizeDecoder(typeSystem), default); var sectionData = pefile.GetSectionData(rva); if (sectionData.Length == 0 && size != 0) throw new BadImageFormatException($"Field data (rva=0x{rva:x}) could not be found" + " in any section!"); if (size < 0 || size > sectionData.Length) throw new BadImageFormatException($"Invalid size {size} for field data!"); return sectionData.GetReader(0, size); } sealed class FieldValueSizeDecoder : ISignatureTypeProvider { readonly MetadataModule module; readonly int pointerSize; public FieldValueSizeDecoder(ICompilation typeSystem = null) { this.module = (MetadataModule)typeSystem?.MainModule; if (module?.MetadataFile is not PEFile pefile) this.pointerSize = IntPtr.Size; else this.pointerSize = pefile.Reader.PEHeaders.PEHeader.Magic == PEMagic.PE32 ? 4 : 8; } public int GetArrayType(int elementType, ArrayShape shape) => GetPrimitiveType(PrimitiveTypeCode.Object); public int GetSZArrayType(int elementType) => GetPrimitiveType(PrimitiveTypeCode.Object); public int GetByReferenceType(int elementType) => pointerSize; public int GetFunctionPointerType(MethodSignature signature) => pointerSize; public int GetGenericInstantiation(int genericType, ImmutableArray typeArguments) => genericType; public int GetGenericMethodParameter(GenericContext genericContext, int index) => 0; public int GetGenericTypeParameter(GenericContext genericContext, int index) => 0; public int GetModifiedType(int modifier, int unmodifiedType, bool isRequired) => unmodifiedType; public int GetPinnedType(int elementType) => elementType; public int GetPointerType(int elementType) => pointerSize; public int GetPrimitiveType(PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: case PrimitiveTypeCode.Byte: case PrimitiveTypeCode.SByte: return 1; case PrimitiveTypeCode.Char: case PrimitiveTypeCode.Int16: case PrimitiveTypeCode.UInt16: return 2; case PrimitiveTypeCode.Int32: case PrimitiveTypeCode.UInt32: case PrimitiveTypeCode.Single: return 4; case PrimitiveTypeCode.Int64: case PrimitiveTypeCode.UInt64: case PrimitiveTypeCode.Double: return 8; case PrimitiveTypeCode.IntPtr: case PrimitiveTypeCode.UIntPtr: return pointerSize; default: return 0; } } public int GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { var td = reader.GetTypeDefinition(handle); return td.GetLayout().Size; } public int GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var typeDef = module?.ResolveType(handle, new GenericContext()).GetDefinition(); if (typeDef == null || typeDef.MetadataToken.IsNil) return 0; reader = typeDef.ParentModule.MetadataFile.Metadata; var td = reader.GetTypeDefinition((TypeDefinitionHandle)typeDef.MetadataToken); return td.GetLayout().Size; } public int GetTypeFromSpecification(MetadataReader reader, GenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); } } public static EntityHandle GetBaseTypeOrNil(this TypeDefinition definition) { try { return definition.BaseType; } catch (BadImageFormatException) { return default; } } public static string ToILSyntax(this SignatureCallingConvention callConv) { return callConv switch { SignatureCallingConvention.Default => "default", SignatureCallingConvention.CDecl => "unmanaged cdecl", SignatureCallingConvention.StdCall => "unmanaged stdcall", SignatureCallingConvention.ThisCall => "unmanaged thiscall", SignatureCallingConvention.FastCall => "unmanaged fastcall", SignatureCallingConvention.VarArgs => "vararg", SignatureCallingConvention.Unmanaged => "unmanaged", _ => callConv.ToString().ToLowerInvariant() }; } public static UnmanagedMemoryStream AsStream(this MemoryMappedViewAccessor view) { long size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength); return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size); } } } ================================================ FILE: ICSharpCode.Decompiler/SRMHacks.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler { public static partial class SRMExtensions { internal const GenericParameterAttributes AllowByRefLike = (GenericParameterAttributes)0x0020; public static ImmutableArray GetMethodImplementations( this MethodDefinitionHandle handle, MetadataReader reader) { var resultBuilder = ImmutableArray.CreateBuilder(); var typeDefinition = reader.GetTypeDefinition(reader.GetMethodDefinition(handle) .GetDeclaringType()); foreach (var methodImplementationHandle in typeDefinition.GetMethodImplementations()) { var methodImplementation = reader.GetMethodImplementation(methodImplementationHandle); if (methodImplementation.MethodBody == handle) { resultBuilder.Add(methodImplementationHandle); } } return resultBuilder.ToImmutable(); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/AmbiguousResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents an ambiguous type resolve result. /// public class AmbiguousTypeResolveResult : TypeResolveResult { public AmbiguousTypeResolveResult(IType type) : base(type) { } public override bool IsError { get { return true; } } } /// /// Represents an ambiguous field/property/event access. /// public class AmbiguousMemberResolveResult : MemberResolveResult { public AmbiguousMemberResolveResult(ResolveResult targetResult, IMember member) : base(targetResult, member) { } public override bool IsError { get { return true; } } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ArrayAccessResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Resolve result representing an array access. /// public class ArrayAccessResolveResult : ResolveResult { public readonly ResolveResult Array; public readonly IList Indexes; public ArrayAccessResolveResult(IType elementType, ResolveResult array, IList indexes) : base(elementType) { if (array == null) throw new ArgumentNullException(nameof(array)); if (indexes == null) throw new ArgumentNullException(nameof(indexes)); this.Array = array; this.Indexes = indexes; } public override IEnumerable GetChildResults() { return new[] { Array }.Concat(Indexes); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Resolve result representing an array creation. /// public class ArrayCreateResolveResult : ResolveResult { /// /// Gets the size arguments. /// public readonly IReadOnlyList SizeArguments; /// /// Gets the initializer elements. /// This field may be null if no initializer was specified. /// public readonly IReadOnlyList InitializerElements; public ArrayCreateResolveResult(IType arrayType, IReadOnlyList sizeArguments, IReadOnlyList initializerElements) : base(arrayType) { if (sizeArguments == null) throw new ArgumentNullException(nameof(sizeArguments)); this.SizeArguments = sizeArguments; this.InitializerElements = initializerElements; } public override IEnumerable GetChildResults() { if (InitializerElements != null) return SizeArguments.Concat(InitializerElements); else return SizeArguments; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Globalization; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the resolve result of an 'ref x', 'in x' or 'out x' expression. /// public class ByReferenceResolveResult : ResolveResult { public ReferenceKind ReferenceKind { get; } public readonly ResolveResult ElementResult; public ByReferenceResolveResult(ResolveResult elementResult, ReferenceKind kind) : this(elementResult.Type, kind) { this.ElementResult = elementResult; } /// /// Should only be used for temporary ResolveResults in TypeInference and CSharpConversions, etc. /// internal ByReferenceResolveResult(IType elementType, ReferenceKind kind) : base(new ByReferenceType(elementType)) { this.ReferenceKind = kind; } public IType ElementType { get { return ((ByReferenceType)this.Type).ElementType; } } public override IEnumerable GetChildResults() { if (ElementResult != null) return new[] { ElementResult }; else return Enumerable.Empty(); } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1} {2}]", GetType().Name, ReferenceKind.ToString().ToLowerInvariant(), ElementType); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ConstantResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// ResolveResult representing a compile-time constant. /// Note: this class is mainly used for literals; there may be other ResolveResult classes /// which are compile-time constants as well. /// For example, a reference to a const field results in a . /// /// Check to determine is a resolve result is a constant. /// public class ConstantResolveResult : ResolveResult { object constantValue; public ConstantResolveResult(IType type, object constantValue) : base(type) { this.constantValue = constantValue; } public override bool IsCompileTimeConstant { get { return true; } } public override object ConstantValue { get { return constantValue; } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1} = {2}]", GetType().Name, this.Type, constantValue); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/Conversion.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Immutable; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Holds information about a conversion between two types. /// public abstract class Conversion : IEquatable { #region Conversion factory methods /// /// Not a valid conversion. /// public static readonly Conversion None = new InvalidConversion(); /// /// Identity conversion. /// public static readonly Conversion IdentityConversion = new BuiltinConversion(true, 0); public static readonly Conversion ImplicitNumericConversion = new NumericOrEnumerationConversion(true, false); public static readonly Conversion ExplicitNumericConversion = new NumericOrEnumerationConversion(false, false); public static readonly Conversion ImplicitLiftedNumericConversion = new NumericOrEnumerationConversion(true, true); public static readonly Conversion ExplicitLiftedNumericConversion = new NumericOrEnumerationConversion(false, true); public static Conversion EnumerationConversion(bool isImplicit, bool isLifted) { return new NumericOrEnumerationConversion(isImplicit, isLifted, true); } public static readonly Conversion NullLiteralConversion = new BuiltinConversion(true, 1); /// /// The numeric conversion of a constant expression. /// public static readonly Conversion ImplicitConstantExpressionConversion = new BuiltinConversion(true, 2); public static readonly Conversion ImplicitReferenceConversion = new BuiltinConversion(true, 3); public static readonly Conversion ExplicitReferenceConversion = new BuiltinConversion(false, 3); public static readonly Conversion ImplicitDynamicConversion = new BuiltinConversion(true, 4); public static readonly Conversion ExplicitDynamicConversion = new BuiltinConversion(false, 4); public static readonly Conversion ImplicitNullableConversion = new BuiltinConversion(true, 5); public static readonly Conversion ExplicitNullableConversion = new BuiltinConversion(false, 5); public static readonly Conversion ImplicitPointerConversion = new BuiltinConversion(true, 6); public static readonly Conversion ExplicitPointerConversion = new BuiltinConversion(false, 6); public static readonly Conversion BoxingConversion = new BuiltinConversion(true, 7); public static readonly Conversion UnboxingConversion = new BuiltinConversion(false, 8); /// /// C# 'as' cast. /// public static readonly Conversion TryCast = new BuiltinConversion(false, 9); /// /// C# 6 string interpolation expression implicitly being converted to or . /// public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10); /// /// C# 7 throw expression being converted to an arbitrary type. /// public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11); /// /// C# 12 inline array implicitly being converted to or . /// public static readonly Conversion InlineArrayConversion = new BuiltinConversion(true, 12); /// /// C# 14 implicit span conversion from an array type to or . /// public static readonly Conversion ImplicitSpanConversion = new BuiltinConversion(true, 13); public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) { if (operatorMethod == null) throw new ArgumentNullException(nameof(operatorMethod)); return new UserDefinedConv(isImplicit, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, isAmbiguous); } public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) { if (chosenMethod == null) throw new ArgumentNullException(nameof(chosenMethod)); return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: true); } public static Conversion InvalidMethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) { if (chosenMethod == null) throw new ArgumentNullException(nameof(chosenMethod)); return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false); } public static Conversion TupleConversion(ImmutableArray conversions) { return new TupleConv(conversions); } #endregion #region Inner classes sealed class InvalidConversion : Conversion { public override bool IsValid { get { return false; } } public override string ToString() { return "None"; } } sealed class NumericOrEnumerationConversion : Conversion { readonly bool isImplicit; readonly bool isLifted; readonly bool isEnumeration; public NumericOrEnumerationConversion(bool isImplicit, bool isLifted, bool isEnumeration = false) { this.isImplicit = isImplicit; this.isLifted = isLifted; this.isEnumeration = isEnumeration; } public override bool IsImplicit { get { return isImplicit; } } public override bool IsExplicit { get { return !isImplicit; } } public override bool IsNumericConversion { get { return !isEnumeration; } } public override bool IsEnumerationConversion { get { return isEnumeration; } } public override bool IsLifted { get { return isLifted; } } public override string ToString() { return (isImplicit ? "implicit" : "explicit") + (isLifted ? " lifted" : "") + (isEnumeration ? " enumeration" : " numeric") + " conversion"; } public override bool Equals(Conversion other) { NumericOrEnumerationConversion o = other as NumericOrEnumerationConversion; return o != null && isImplicit == o.isImplicit && isLifted == o.isLifted && isEnumeration == o.isEnumeration; } public override int GetHashCode() { return (isImplicit ? 1 : 0) + (isLifted ? 2 : 0) + (isEnumeration ? 4 : 0); } } sealed class BuiltinConversion : Conversion { readonly bool isImplicit; readonly byte type; public BuiltinConversion(bool isImplicit, byte type) { this.isImplicit = isImplicit; this.type = type; } public override bool IsImplicit { get { return isImplicit; } } public override bool IsExplicit { get { return !isImplicit; } } public override bool IsIdentityConversion { get { return type == 0; } } public override bool IsNullLiteralConversion { get { return type == 1; } } public override bool IsConstantExpressionConversion { get { return type == 2; } } public override bool IsReferenceConversion { get { return type == 3; } } public override bool IsDynamicConversion { get { return type == 4; } } public override bool IsNullableConversion { get { return type == 5; } } public override bool IsPointerConversion { get { return type == 6; } } public override bool IsBoxingConversion { get { return type == 7; } } public override bool IsUnboxingConversion { get { return type == 8; } } public override bool IsTryCast { get { return type == 9; } } public override bool IsInterpolatedStringConversion => type == 10; public override bool IsThrowExpressionConversion { get { return type == 11; } } public override bool IsInlineArrayConversion => type == 12; public override bool IsImplicitSpanConversion => type == 13; public override string ToString() { string name = null; switch (type) { case 0: return "identity conversion"; case 1: return "null-literal conversion"; case 2: name = "constant-expression"; break; case 3: name = "reference"; break; case 4: name = "dynamic"; break; case 5: name = "nullable"; break; case 6: name = "pointer"; break; case 7: return "boxing conversion"; case 8: return "unboxing conversion"; case 9: return "try cast"; case 10: return "interpolated string"; case 11: return "throw-expression conversion"; case 12: return "inline array conversion"; case 13: return "implicit span conversion"; } return (isImplicit ? "implicit " : "explicit ") + name + " conversion"; } } sealed class UserDefinedConv : Conversion { readonly IMethod method; readonly bool isLifted; readonly Conversion conversionBeforeUserDefinedOperator; readonly Conversion conversionAfterUserDefinedOperator; readonly bool isImplicit; readonly bool isValid; public UserDefinedConv(bool isImplicit, IMethod method, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted, bool isAmbiguous) { this.method = method; this.isLifted = isLifted; this.conversionBeforeUserDefinedOperator = conversionBeforeUserDefinedOperator; this.conversionAfterUserDefinedOperator = conversionAfterUserDefinedOperator; this.isImplicit = isImplicit; this.isValid = !isAmbiguous; } public override bool IsValid { get { return isValid; } } public override bool IsImplicit { get { return isImplicit; } } public override bool IsExplicit { get { return !isImplicit; } } public override bool IsLifted { get { return isLifted; } } public override bool IsUserDefined { get { return true; } } public override Conversion ConversionBeforeUserDefinedOperator { get { return conversionBeforeUserDefinedOperator; } } public override Conversion ConversionAfterUserDefinedOperator { get { return conversionAfterUserDefinedOperator; } } public override IMethod Method { get { return method; } } public override bool Equals(Conversion other) { UserDefinedConv o = other as UserDefinedConv; return o != null && isLifted == o.isLifted && isImplicit == o.isImplicit && isValid == o.isValid && method.Equals(o.method); } public override int GetHashCode() { return unchecked(method.GetHashCode() + (isLifted ? 31 : 27) + (isImplicit ? 71 : 61) + (isValid ? 107 : 109)); } public override string ToString() { return (isImplicit ? "implicit" : "explicit") + (isLifted ? " lifted" : "") + (isValid ? "" : " ambiguous") + "user-defined conversion (" + method + ")"; } } sealed class MethodGroupConv : Conversion { readonly IMethod method; readonly bool isVirtualMethodLookup; readonly bool delegateCapturesFirstArgument; readonly bool isValid; public MethodGroupConv(IMethod method, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument, bool isValid) { this.method = method; this.isVirtualMethodLookup = isVirtualMethodLookup; this.delegateCapturesFirstArgument = delegateCapturesFirstArgument; this.isValid = isValid; } public override bool IsValid { get { return isValid; } } public override bool IsImplicit { get { return true; } } public override bool IsMethodGroupConversion { get { return true; } } public override bool IsVirtualMethodLookup { get { return isVirtualMethodLookup; } } public override bool DelegateCapturesFirstArgument { get { return delegateCapturesFirstArgument; } } public override IMethod Method { get { return method; } } public override bool Equals(Conversion other) { MethodGroupConv o = other as MethodGroupConv; return o != null && method.Equals(o.method); } public override int GetHashCode() { return method.GetHashCode(); } } sealed class TupleConv : Conversion { public override bool IsImplicit { get; } public override bool IsExplicit => !IsImplicit; public override ImmutableArray ElementConversions { get; } public override bool IsTupleConversion => true; public TupleConv(ImmutableArray elementConversions) { this.ElementConversions = elementConversions; this.IsImplicit = elementConversions.All(c => c.IsImplicit); } public override bool Equals(Conversion other) { return other is TupleConv o && ElementConversions.SequenceEqual(o.ElementConversions); } public override int GetHashCode() { unchecked { int hash = 0; foreach (var conv in ElementConversions) { hash *= 31; hash += conv.GetHashCode(); } return hash; } } public override string ToString() { return (IsImplicit ? "implicit " : "explicit ") + " tuple conversion"; } } #endregion /// /// Gets whether the conversion is valid. /// public virtual bool IsValid { get { return true; } } public virtual bool IsImplicit { get { return false; } } public virtual bool IsExplicit { get { return false; } } /// /// Gets whether the conversion is an 'as' cast. /// public virtual bool IsTryCast { get { return false; } } public virtual bool IsThrowExpressionConversion { get { return false; } } public virtual bool IsIdentityConversion { get { return false; } } public virtual bool IsNullLiteralConversion { get { return false; } } public virtual bool IsConstantExpressionConversion { get { return false; } } public virtual bool IsNumericConversion { get { return false; } } /// /// Gets whether this conversion is a lifted version of another conversion. /// public virtual bool IsLifted { get { return false; } } /// /// Gets whether the conversion is dynamic. /// public virtual bool IsDynamicConversion { get { return false; } } /// /// Gets whether the conversion is a reference conversion. /// public virtual bool IsReferenceConversion { get { return false; } } /// /// Gets whether the conversion is an enumeration conversion. /// public virtual bool IsEnumerationConversion { get { return false; } } /// /// Gets whether the conversion is a nullable conversion /// (conversion between a nullable type and the regular type). /// public virtual bool IsNullableConversion { get { return false; } } /// /// Gets whether this conversion is user-defined (op_Implicit or op_Explicit). /// public virtual bool IsUserDefined { get { return false; } } /// /// The conversion that is applied to the input before the user-defined conversion operator is invoked. /// public virtual Conversion ConversionBeforeUserDefinedOperator { get { return null; } } /// /// The conversion that is applied to the result of the user-defined conversion operator. /// public virtual Conversion ConversionAfterUserDefinedOperator { get { return null; } } /// /// Gets whether this conversion is a boxing conversion. /// public virtual bool IsBoxingConversion { get { return false; } } /// /// Gets whether this conversion is an unboxing conversion. /// public virtual bool IsUnboxingConversion { get { return false; } } /// /// Gets whether this conversion is a pointer conversion. /// public virtual bool IsPointerConversion { get { return false; } } /// /// Gets whether this conversion is a method group conversion. /// public virtual bool IsMethodGroupConversion { get { return false; } } /// /// For method-group conversions, gets whether to perform a virtual method lookup at runtime. /// public virtual bool IsVirtualMethodLookup { get { return false; } } /// /// For method-group conversions, gets whether the conversion captures the first argument. /// /// For instance methods, this property always returns true for C# method-group conversions. /// For static methods, this property returns true for method-group conversions of an extension method performed on an instance (eg. Func<int> f = myEnumerable.Single). /// public virtual bool DelegateCapturesFirstArgument { get { return false; } } /// /// Gets whether this conversion is an anonymous function conversion. /// public virtual bool IsAnonymousFunctionConversion { get { return false; } } /// /// Gets the method associated with this conversion. /// For user-defined conversions, this is the method being called. /// For method-group conversions, this is the method that was chosen from the group. /// public virtual IMethod Method { get { return null; } } /// /// Gets whether this conversion is a tuple conversion. /// public virtual bool IsTupleConversion => false; /// /// Gets whether this is an interpolated string conversion to or . /// public virtual bool IsInterpolatedStringConversion => false; /// /// Gets whether this is an inline array conversion to or . /// public virtual bool IsInlineArrayConversion => false; /// /// Gets whether this is an implicit span conversion from an array type to or . /// public virtual bool IsImplicitSpanConversion => false; /// /// For a tuple conversion, gets the individual tuple element conversions. /// public virtual ImmutableArray ElementConversions => default(ImmutableArray); public override sealed bool Equals(object obj) { return Equals(obj as Conversion); } public override int GetHashCode() { return base.GetHashCode(); } public virtual bool Equals(Conversion other) { return this == other; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ConversionResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents an implicit or explicit type conversion. /// conversionResolveResult.Input.Type is the source type; /// conversionResolveResult.Type is the target type. /// The property provides details about the kind of conversion. /// public class ConversionResolveResult : ResolveResult { public readonly ResolveResult Input; public readonly Conversion Conversion; /// /// For numeric conversions, specifies whether overflow checking is enabled. /// public readonly bool CheckForOverflow; public ConversionResolveResult(IType targetType, ResolveResult input, Conversion conversion) : base(targetType) { if (input == null) throw new ArgumentNullException(nameof(input)); if (conversion == null) throw new ArgumentNullException(nameof(conversion)); this.Input = input; this.Conversion = conversion; } public ConversionResolveResult(IType targetType, ResolveResult input, Conversion conversion, bool checkForOverflow) : this(targetType, input, conversion) { this.CheckForOverflow = checkForOverflow; } public override bool IsError { get { return !Conversion.IsValid; } } public override IEnumerable GetChildResults() { return new[] { Input }; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ErrorResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents a resolve error. /// /// Note: some errors are represented by other classes; for example a may /// be erroneous if the conversion is invalid. /// /// . public class ErrorResolveResult : ResolveResult { /// /// Gets an ErrorResolveResult instance with Type = SpecialType.UnknownType. /// public static readonly ErrorResolveResult UnknownError = new ErrorResolveResult(SpecialType.UnknownType); public ErrorResolveResult(IType type) : base(type) { } public ErrorResolveResult(IType type, string message, TextLocation location) : base(type) { this.Message = message; this.Location = location; } public override bool IsError { get { return true; } } public string Message { get; private set; } public TextLocation Location { get; private set; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ForEachResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Resolve result representing a 'foreach' loop. /// public class ForEachResolveResult : ResolveResult { /// /// Gets the semantic tree for the call to GetEnumerator. /// public readonly ResolveResult GetEnumeratorCall; /// /// Gets the collection type. /// public readonly IType CollectionType; /// /// Gets the enumerator type. /// public readonly IType EnumeratorType; /// /// Gets the element type. /// This is the type that would be inferred for an implicitly-typed element variable. /// For explicitly-typed element variables, this type may differ from ElementVariable.Type. /// public readonly IType ElementType; /// /// Gets the Current property on the IEnumerator. /// Returns null if the property is not found. /// public readonly IProperty CurrentProperty; /// /// Gets the MoveNext() method on the IEnumerator. /// Returns null if the method is not found. /// public readonly IMethod MoveNextMethod; public ForEachResolveResult(ResolveResult getEnumeratorCall, IType collectionType, IType enumeratorType, IType elementType, IProperty currentProperty, IMethod moveNextMethod, IType voidType) : base(voidType) { if (getEnumeratorCall == null) throw new ArgumentNullException(nameof(getEnumeratorCall)); if (collectionType == null) throw new ArgumentNullException(nameof(collectionType)); if (enumeratorType == null) throw new ArgumentNullException(nameof(enumeratorType)); if (elementType == null) throw new ArgumentNullException(nameof(elementType)); this.GetEnumeratorCall = getEnumeratorCall; this.CollectionType = collectionType; this.EnumeratorType = enumeratorType; this.ElementType = elementType; this.CurrentProperty = currentProperty; this.MoveNextMethod = moveNextMethod; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/InitializedObjectResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Refers to the object that is currently being initialized. /// Used within . /// public class InitializedObjectResolveResult : ResolveResult { public InitializedObjectResolveResult(IType type) : base(type) { } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/InterpolatedStringResolveResult.cs ================================================ // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { public class InterpolatedStringResolveResult : ResolveResult { public readonly string FormatString; public readonly ResolveResult[] Arguments; public InterpolatedStringResolveResult(IType stringType, string formatString, params ResolveResult[] arguments) : base(stringType) { FormatString = formatString; Arguments = arguments; } public override IEnumerable GetChildResults() { return Arguments; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/InvocationResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the result of a method, constructor or indexer invocation. /// public class InvocationResolveResult : MemberResolveResult { /// /// Gets the arguments that are being passed to the method, in the order the arguments are being evaluated. /// public readonly IList Arguments; /// /// Gets the list of initializer statements that are appplied to the result of this invocation. /// This is used to represent object and collection initializers. /// With the initializer statements, the is used /// to refer to the result of this invocation. /// public readonly IList InitializerStatements; public InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member, IList arguments = null, IList initializerStatements = null, IType returnTypeOverride = null) : base(targetResult, member, returnTypeOverride) { this.Arguments = arguments ?? EmptyList.Instance; this.InitializerStatements = initializerStatements ?? EmptyList.Instance; } public new IParameterizedMember Member { get { return (IParameterizedMember)base.Member; } } /// /// Gets the arguments in the order they are being passed to the method. /// For parameter arrays (params), this will return an ArrayCreateResolveResult. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Derived methods may be expensive and create new lists")] public virtual IList GetArgumentsForCall() { return Arguments; } public override IEnumerable GetChildResults() { return base.GetChildResults().Concat(this.Arguments).Concat(this.InitializerStatements); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents a local variable or parameter. /// public class LocalResolveResult : ResolveResult { readonly IVariable variable; public LocalResolveResult(IVariable variable) : base(UnpackTypeIfByRefParameter(variable)) { this.variable = variable; } static IType UnpackTypeIfByRefParameter(IVariable variable) { if (variable == null) throw new ArgumentNullException(nameof(variable)); IType type = variable.Type; if (type.Kind == TypeKind.ByReference) { IParameter p = variable as IParameter; if (p != null && p.ReferenceKind != ReferenceKind.None) return ((ByReferenceType)type).ElementType; } return type; } public IVariable Variable { get { return variable; } } public bool IsParameter { get { return variable is IParameter; } } public override bool IsCompileTimeConstant { get { return variable.IsConst; } } public override object ConstantValue { get { return IsParameter ? null : variable.GetConstantValue(); } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[LocalResolveResult {0}]", variable); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/MemberResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Globalization; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the result of a member invocation. /// Used for field/property/event access. /// Also, derives from MemberResolveResult. /// public class MemberResolveResult : ResolveResult { readonly IMember member; readonly bool isConstant; readonly object constantValue; readonly ResolveResult targetResult; readonly bool isVirtualCall; public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnTypeOverride = null) : base(returnTypeOverride ?? ComputeType(member)) { this.targetResult = targetResult; this.member = member; var thisRR = targetResult as ThisResolveResult; this.isVirtualCall = member.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation); IField field = member as IField; if (field != null) { isConstant = field.IsConst; if (isConstant) constantValue = field.GetConstantValue(); } } public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall, IType returnTypeOverride = null) : base(returnTypeOverride ?? ComputeType(member)) { this.targetResult = targetResult; this.member = member; this.isVirtualCall = isVirtualCall; IField field = member as IField; if (field != null) { isConstant = field.IsConst; if (isConstant) constantValue = field.GetConstantValue(); } } static IType ComputeType(IMember member) { switch (member.SymbolKind) { case SymbolKind.Constructor: return member.DeclaringType ?? SpecialType.UnknownType; case SymbolKind.Field: //if (((IField)member).IsFixed) // return new PointerType(member.ReturnType); break; } if (member.ReturnType.Kind == TypeKind.ByReference) return ((ByReferenceType)member.ReturnType).ElementType; return member.ReturnType; } public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnType, bool isConstant, object constantValue) : base(returnType) { this.targetResult = targetResult; this.member = member; this.isConstant = isConstant; this.constantValue = constantValue; } public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnType, bool isConstant, object constantValue, bool isVirtualCall) : base(returnType) { this.targetResult = targetResult; this.member = member; this.isConstant = isConstant; this.constantValue = constantValue; this.isVirtualCall = isVirtualCall; } public ResolveResult TargetResult { get { return targetResult; } } /// /// Gets the member. /// This property never returns null. /// public IMember Member { get { return member; } } /// /// Gets whether this MemberResolveResult is a virtual call. /// public bool IsVirtualCall { get { return isVirtualCall; } } public override bool IsCompileTimeConstant { get { return isConstant; } } public override object ConstantValue { get { return constantValue; } } public override IEnumerable GetChildResults() { if (targetResult != null) return new[] { targetResult }; else return Enumerable.Empty(); } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1}]", GetType().Name, member); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/NamedArgumentResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents a named argument. /// public class NamedArgumentResolveResult : ResolveResult { /// /// Gets the member to which the parameter belongs. /// This field can be null. /// public readonly IParameterizedMember Member; /// /// Gets the parameter. /// This field can be null. /// public readonly IParameter Parameter; /// /// Gets the parameter name. /// public readonly string ParameterName; /// /// Gets the argument passed to the parameter. /// public readonly ResolveResult Argument; public NamedArgumentResolveResult(IParameter parameter, ResolveResult argument, IParameterizedMember member = null) : base(argument.Type) { if (parameter == null) throw new ArgumentNullException(nameof(parameter)); if (argument == null) throw new ArgumentNullException(nameof(argument)); this.Member = member; this.Parameter = parameter; this.ParameterName = parameter.Name; this.Argument = argument; } public NamedArgumentResolveResult(string parameterName, ResolveResult argument) : base(argument.Type) { if (parameterName == null) throw new ArgumentNullException(nameof(parameterName)); if (argument == null) throw new ArgumentNullException(nameof(argument)); this.ParameterName = parameterName; this.Argument = argument; } public override IEnumerable GetChildResults() { return new[] { Argument }; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/NamespaceResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents that an expression resolved to a namespace. /// public class NamespaceResolveResult : ResolveResult { readonly INamespace ns; public NamespaceResolveResult(INamespace ns) : base(SpecialType.NoType) { this.ns = ns; } public INamespace Namespace { get { return ns; } } public string NamespaceName { get { return ns.FullName; } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1}]", GetType().Name, ns); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/OperatorResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents a unary/binary/ternary operator invocation. /// public class OperatorResolveResult : ResolveResult { readonly ExpressionType operatorType; readonly IMethod userDefinedOperatorMethod; readonly IList operands; readonly bool isLiftedOperator; public OperatorResolveResult(IType resultType, ExpressionType operatorType, params ResolveResult[] operands) : base(resultType) { if (operands == null) throw new ArgumentNullException(nameof(operands)); this.operatorType = operatorType; this.operands = operands; } public OperatorResolveResult(IType resultType, ExpressionType operatorType, IMethod userDefinedOperatorMethod, bool isLiftedOperator, IList operands) : base(resultType) { if (operands == null) throw new ArgumentNullException(nameof(operands)); this.operatorType = operatorType; this.userDefinedOperatorMethod = userDefinedOperatorMethod; this.isLiftedOperator = isLiftedOperator; this.operands = operands; } /// /// Gets the operator type. /// public ExpressionType OperatorType { get { return operatorType; } } /// /// Gets the operands. /// public IList Operands { get { return operands; } } /// /// Gets the user defined operator method. /// Returns null if this is a predefined operator. /// public IMethod UserDefinedOperatorMethod { get { return userDefinedOperatorMethod; } } /// /// Gets whether this is a lifted operator. /// public bool IsLiftedOperator { get { return isLiftedOperator; } } public override IEnumerable GetChildResults() { return operands; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/OutVarResolveResult.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the implicitly-typed "out var". /// Special-cased in overload resolution to be compatible with any out-parameter. /// class OutVarResolveResult : ResolveResult { /// /// Type of the variable originally used in IL. It will be used, if "out var" cannot be used. /// public readonly IType OriginalVariableType; public OutVarResolveResult(IType originalVariableType) : base(SpecialType.NoType) { OriginalVariableType = originalVariableType; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the result of resolving an expression. /// public class ResolveResult { readonly IType type; public ResolveResult(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); this.type = type; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Unrelated to object.GetType()")] public IType Type { get { return type; } } public virtual bool IsCompileTimeConstant { get { return false; } } public virtual object ConstantValue { get { return null; } } public virtual bool IsError { get { return false; } } public override string ToString() { return "[" + GetType().Name + " " + type + "]"; } public virtual IEnumerable GetChildResults() { return Enumerable.Empty(); } public virtual ResolveResult ShallowClone() { return (ResolveResult)MemberwiseClone(); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/SizeOfResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the 'sizeof'. /// public class SizeOfResolveResult : ResolveResult { readonly IType referencedType; readonly int? constantValue; public SizeOfResolveResult(IType int32, IType referencedType, int? constantValue) : base(int32) { if (referencedType == null) throw new ArgumentNullException(nameof(referencedType)); this.referencedType = referencedType; this.constantValue = constantValue; } /// /// The type referenced by the 'sizeof'. /// public IType ReferencedType { get { return referencedType; } } public override bool IsCompileTimeConstant { get { return constantValue != null; } } public override object ConstantValue { get { return constantValue; } } public override bool IsError { get { return referencedType.IsReferenceType != false; } } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ThisResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the 'this' reference. /// Also used for the 'base' reference. /// public class ThisResolveResult : ResolveResult { bool causesNonVirtualInvocation; public ThisResolveResult(IType type, bool causesNonVirtualInvocation = false) : base(type) { this.causesNonVirtualInvocation = causesNonVirtualInvocation; } /// /// Gets whether this resolve result causes member invocations to be non-virtual. /// public bool CausesNonVirtualInvocation { get { return causesNonVirtualInvocation; } } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { class ThrowResolveResult : ResolveResult { public ThrowResolveResult() : base(SpecialType.NoType) { } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/TupleResolveResult.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Immutable; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Semantics { /// /// Resolve result for a C# 7 tuple literal. /// public class TupleResolveResult : ResolveResult { public ImmutableArray Elements { get; } public TupleResolveResult(ICompilation compilation, ImmutableArray elements, ImmutableArray elementNames = default(ImmutableArray), IModule valueTupleAssembly = null) : base(GetTupleType(compilation, elements, elementNames, valueTupleAssembly)) { this.Elements = elements; } public override IEnumerable GetChildResults() { return Elements; } static IType GetTupleType(ICompilation compilation, ImmutableArray elements, ImmutableArray elementNames, IModule valueTupleAssembly) { if (elements.Any(e => e.Type.Kind == TypeKind.None || e.Type.Kind == TypeKind.Null)) return SpecialType.NoType; else return new TupleType(compilation, elements.Select(e => e.Type).ToImmutableArray(), elementNames, valueTupleAssembly); } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/TypeIsResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Resolve result for a C# 'is' expression. /// "Input is TargetType". /// public class TypeIsResolveResult : ResolveResult { public readonly ResolveResult Input; /// /// Type that is being compared with. /// public readonly IType TargetType; public TypeIsResolveResult(ResolveResult input, IType targetType, IType booleanType) : base(booleanType) { if (input == null) throw new ArgumentNullException(nameof(input)); if (targetType == null) throw new ArgumentNullException(nameof(targetType)); this.Input = input; this.TargetType = targetType; } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/TypeOfResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents the 'typeof'. /// public class TypeOfResolveResult : ResolveResult { readonly IType referencedType; public TypeOfResolveResult(IType systemType, IType referencedType) : base(systemType) { if (referencedType == null) throw new ArgumentNullException(nameof(referencedType)); this.referencedType = referencedType; } /// /// The type referenced by the 'typeof'. /// public IType ReferencedType { get { return referencedType; } } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/TypeResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// The resolved expression refers to a type name. /// public class TypeResolveResult : ResolveResult { public TypeResolveResult(IType type) : base(type) { } public override bool IsError { get { return this.Type.Kind == TypeKind.Unknown; } } } } ================================================ FILE: ICSharpCode.Decompiler/Semantics/UnknownMemberResolveResult.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.ObjectModel; using System.Globalization; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// /// Represents an unknown member. /// public class UnknownMemberResolveResult : ResolveResult { readonly IType targetType; readonly string memberName; readonly ReadOnlyCollection typeArguments; public UnknownMemberResolveResult(IType targetType, string memberName, IEnumerable typeArguments) : base(SpecialType.UnknownType) { if (targetType == null) throw new ArgumentNullException(nameof(targetType)); this.targetType = targetType; this.memberName = memberName; this.typeArguments = new ReadOnlyCollection(typeArguments.ToArray()); } /// /// The type on which the method is being called. /// public IType TargetType { get { return targetType; } } public string MemberName { get { return memberName; } } public ReadOnlyCollection TypeArguments { get { return typeArguments; } } public override bool IsError { get { return true; } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1}.{2}]", GetType().Name, targetType, memberName); } } /// /// Represents an unknown method. /// public class UnknownMethodResolveResult : UnknownMemberResolveResult { readonly ReadOnlyCollection parameters; public UnknownMethodResolveResult(IType targetType, string methodName, IEnumerable typeArguments, IEnumerable parameters) : base(targetType, methodName, typeArguments) { this.parameters = new ReadOnlyCollection(parameters.ToArray()); } public ReadOnlyCollection Parameters { get { return parameters; } } } /// /// Represents an unknown identifier. /// public class UnknownIdentifierResolveResult : ResolveResult { readonly string identifier; readonly int typeArgumentCount; public UnknownIdentifierResolveResult(string identifier, int typeArgumentCount = 0) : base(SpecialType.UnknownType) { this.identifier = identifier; this.typeArgumentCount = typeArgumentCount; } public string Identifier { get { return identifier; } } public int TypeArgumentCount { get { return typeArgumentCount; } } public override bool IsError { get { return true; } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[{0} {1}]", GetType().Name, identifier); } } } ================================================ FILE: ICSharpCode.Decompiler/SingleFileBundle.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Immutable; using System.IO; using System.IO.MemoryMappedFiles; using System.Runtime.CompilerServices; using System.Text; namespace ICSharpCode.Decompiler { /// /// Class for dealing with .NET 5 single-file bundles. /// /// Based on code from Microsoft.NET.HostModel. /// public static class SingleFileBundle { /// /// Check if the memory-mapped data is a single-file bundle /// public static unsafe bool IsBundle(MemoryMappedViewAccessor view, out long bundleHeaderOffset) { var buffer = view.SafeMemoryMappedViewHandle; byte* ptr = null; buffer.AcquirePointer(ref ptr); try { return IsBundle(ptr, checked((long)buffer.ByteLength), out bundleHeaderOffset); } finally { buffer.ReleasePointer(); } } public static unsafe bool IsBundle(byte* data, long size, out long bundleHeaderOffset) { ReadOnlySpan bundleSignature = new byte[] { // 32 bytes represent the bundle signature: SHA-256 for ".net core bundle" 0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38, 0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32, 0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18, 0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae }; byte* end = data + (size - bundleSignature.Length); for (byte* ptr = data; ptr < end; ptr++) { if (*ptr == 0x8b && bundleSignature.SequenceEqual(new ReadOnlySpan(ptr, bundleSignature.Length))) { bundleHeaderOffset = Unsafe.ReadUnaligned(ptr - sizeof(long)); if (bundleHeaderOffset > 0 && bundleHeaderOffset < size) { return true; } } } bundleHeaderOffset = 0; return false; } public struct Header { public uint MajorVersion; public uint MinorVersion; public int FileCount; public string BundleID; // Fields introduced with v2: public long DepsJsonOffset; public long DepsJsonSize; public long RuntimeConfigJsonOffset; public long RuntimeConfigJsonSize; public ulong Flags; public ImmutableArray Entries; } /// /// FileType: Identifies the type of file embedded into the bundle. /// /// The bundler differentiates a few kinds of files via the manifest, /// with respect to the way in which they'll be used by the runtime. /// public enum FileType : byte { Unknown, // Type not determined. Assembly, // IL and R2R Assemblies NativeBinary, // NativeBinaries DepsJson, // .deps.json configuration file RuntimeConfigJson, // .runtimeconfig.json configuration file Symbols // PDB Files }; public struct Entry { public long Offset; public long Size; public long CompressedSize; // 0 if not compressed, otherwise the compressed size in the bundle public FileType Type; public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. } /// /// Reads the manifest header from the memory mapping. /// public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset) { using var stream = view.AsStream(); stream.Seek(bundleHeaderOffset, SeekOrigin.Begin); return ReadManifest(stream); } /// /// Reads the manifest header from the stream. /// public static Header ReadManifest(Stream stream) { var header = new Header(); using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); header.MajorVersion = reader.ReadUInt32(); header.MinorVersion = reader.ReadUInt32(); // Major versions 3, 4 and 5 were skipped to align bundle versioning with .NET versioning scheme if (header.MajorVersion < 1 || header.MajorVersion > 6) { throw new InvalidDataException($"Unsupported manifest version: {header.MajorVersion}.{header.MinorVersion}"); } header.FileCount = reader.ReadInt32(); header.BundleID = reader.ReadString(); if (header.MajorVersion >= 2) { header.DepsJsonOffset = reader.ReadInt64(); header.DepsJsonSize = reader.ReadInt64(); header.RuntimeConfigJsonOffset = reader.ReadInt64(); header.RuntimeConfigJsonSize = reader.ReadInt64(); header.Flags = reader.ReadUInt64(); } var entries = ImmutableArray.CreateBuilder(header.FileCount); for (int i = 0; i < header.FileCount; i++) { entries.Add(ReadEntry(reader, header.MajorVersion)); } header.Entries = entries.MoveToImmutable(); return header; } private static Entry ReadEntry(BinaryReader reader, uint bundleMajorVersion) { Entry entry; entry.Offset = reader.ReadInt64(); entry.Size = reader.ReadInt64(); entry.CompressedSize = bundleMajorVersion >= 6 ? reader.ReadInt64() : 0; entry.Type = (FileType)reader.ReadByte(); entry.RelativePath = reader.ReadString(); return entry; } } } ================================================ FILE: ICSharpCode.Decompiler/Solution/ProjectId.cs ================================================ // Copyright (c) 2019 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 // 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; namespace ICSharpCode.Decompiler.Solution { /// /// A container class that holds platform and GUID information about a Visual Studio project. /// public class ProjectId { /// /// Initializes a new instance of the class. /// /// The project platform. /// The project GUID. /// /// Thrown when is null or empty. public ProjectId(string projectPlatform, Guid projectGuid, Guid typeGuid) { if (string.IsNullOrWhiteSpace(projectPlatform)) { throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); } Guid = projectGuid; TypeGuid = typeGuid; PlatformName = projectPlatform; } /// /// Gets the GUID of this project. /// This is usually a newly generated GUID for each decompiled project. /// public Guid Guid { get; } /// /// Gets the primary type GUID of this project. /// This is one of the GUIDs from . /// public Guid TypeGuid { get; } /// /// Gets the platform name of this project. Only single platform per project is supported. /// public string PlatformName { get; } } public static class ProjectTypeGuids { public static readonly Guid SolutionFolder = Guid.Parse("{2150E333-8FDC-42A3-9474-1A3956D46DE8}"); public static readonly Guid CSharpWindows = Guid.Parse("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"); public static readonly Guid CSharpCore = Guid.Parse("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"); public static readonly Guid Silverlight = Guid.Parse("{A1591282-1198-4647-A2B1-27E5FF5F6F3B}"); public static readonly Guid PortableLibrary = Guid.Parse("{786C830F-07A1-408B-BD7F-6EE04809D6DB}"); } } ================================================ FILE: ICSharpCode.Decompiler/Solution/ProjectItem.cs ================================================ // Copyright (c) 2019 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 // 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.IO; namespace ICSharpCode.Decompiler.Solution { /// /// A container class that holds information about a Visual Studio project. /// public sealed class ProjectItem : ProjectId { /// /// Initializes a new instance of the class. /// /// The full path of the project file. /// The project platform. /// The project GUID. /// /// Thrown when /// or is null or empty. public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid, Guid typeGuid) : base(projectPlatform, projectGuid, typeGuid) { ProjectName = Path.GetFileNameWithoutExtension(projectFile); FilePath = projectFile; } /// /// Gets the name of the project. /// public string ProjectName { get; } /// /// Gets the full path to the project file. /// public string FilePath { get; } } } ================================================ FILE: ICSharpCode.Decompiler/Solution/SolutionCreator.cs ================================================ // Copyright (c) 2019 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 // 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.IO; using System.Linq; using System.Xml.Linq; namespace ICSharpCode.Decompiler.Solution { /// /// A helper class that can write a Visual Studio Solution file for the provided projects. /// public static class SolutionCreator { static readonly XNamespace NonSDKProjectFileNamespace = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003"); /// /// Writes a solution file to the specified . /// Also fixes intra-solution project references in the project files. /// /// The full path of the file to write. /// The projects contained in this solution. /// /// Thrown when is null or empty. /// Thrown when is null. /// Thrown when contains no items. public static void WriteSolutionFile(string targetFile, List projects) { if (string.IsNullOrWhiteSpace(targetFile)) { throw new ArgumentException("The target file cannot be null or empty.", nameof(targetFile)); } if (projects == null) { throw new ArgumentNullException(nameof(projects)); } if (!projects.Any()) { throw new InvalidOperationException("At least one project is expected."); } using (var writer = new StreamWriter(targetFile)) { WriteSolutionFile(writer, projects, targetFile); } FixAllProjectReferences(projects); } static void WriteSolutionFile(TextWriter writer, List projects, string solutionFilePath) { WriteHeader(writer); WriteProjects(writer, projects, solutionFilePath); writer.WriteLine("Global"); var platforms = WriteSolutionConfigurations(writer, projects); WriteProjectConfigurations(writer, projects, platforms); writer.WriteLine("\tGlobalSection(SolutionProperties) = preSolution"); writer.WriteLine("\t\tHideSolutionNode = FALSE"); writer.WriteLine("\tEndGlobalSection"); writer.WriteLine("EndGlobal"); } private static void WriteHeader(TextWriter writer) { writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00"); writer.WriteLine("# Visual Studio 14"); writer.WriteLine("VisualStudioVersion = 14.0.24720.0"); writer.WriteLine("MinimumVisualStudioVersion = 10.0.40219.1"); } static void WriteProjects(TextWriter writer, List projects, string solutionFilePath) { foreach (var project in projects) { var projectRelativePath = GetRelativePath(solutionFilePath, project.FilePath); var typeGuid = project.TypeGuid.ToString("B").ToUpperInvariant(); var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); writer.WriteLine($"Project(\"{typeGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); writer.WriteLine("EndProject"); } } static List WriteSolutionConfigurations(TextWriter writer, List projects) { var platforms = projects.GroupBy(p => p.PlatformName).Select(g => g.Key).ToList(); platforms.Sort(); writer.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); foreach (var platform in platforms) { writer.WriteLine($"\t\tDebug|{platform} = Debug|{platform}"); } foreach (var platform in platforms) { writer.WriteLine($"\t\tRelease|{platform} = Release|{platform}"); } writer.WriteLine("\tEndGlobalSection"); return platforms; } static void WriteProjectConfigurations( TextWriter writer, List projects, List solutionPlatforms) { writer.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (var project in projects) { var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); foreach (var platform in solutionPlatforms) { writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.ActiveCfg = Debug|{project.PlatformName}"); writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.Build.0 = Debug|{project.PlatformName}"); } foreach (var platform in solutionPlatforms) { writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.ActiveCfg = Release|{project.PlatformName}"); writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.Build.0 = Release|{project.PlatformName}"); } } writer.WriteLine("\tEndGlobalSection"); } static void FixAllProjectReferences(List projects) { var projectsMap = projects.ToDictionary( p => p.ProjectName, p => p); foreach (var project in projects) { XDocument projectDoc = XDocument.Load(project.FilePath); if (projectDoc.Root?.Name.LocalName != "Project") { throw new InvalidOperationException( $"The file {project.FilePath} is not a valid project file, " + $"no at the root; could not fix project references."); } // sdk style projects don't use a namespace for the elements, // but we still need to use the namespace for non-sdk style projects. var sdkStyle = projectDoc.Root.Attribute("Sdk") != null; var itemGroupTagName = sdkStyle ? "ItemGroup" : NonSDKProjectFileNamespace + "ItemGroup"; var referenceTagName = sdkStyle ? "Reference" : NonSDKProjectFileNamespace + "Reference"; var referencesItemGroups = projectDoc.Root .Elements(itemGroupTagName) .Where(e => e.Elements(referenceTagName).Any()) .ToList(); foreach (var itemGroup in referencesItemGroups) { FixProjectReferences(project.FilePath, itemGroup, projectsMap, sdkStyle); } projectDoc.Save(project.FilePath); } } static void FixProjectReferences(string projectFilePath, XElement itemGroup, Dictionary projects, bool sdkStyle) { XName GetElementName(string name) => sdkStyle ? name : NonSDKProjectFileNamespace + name; var referenceTagName = GetElementName("Reference"); var projectReferenceTagName = GetElementName("ProjectReference"); foreach (var item in itemGroup.Elements(referenceTagName).ToList()) { var assemblyName = item.Attribute("Include")?.Value; if (assemblyName != null && projects.TryGetValue(assemblyName, out var referencedProject)) { item.Remove(); var projectReference = new XElement( projectReferenceTagName, new XAttribute("Include", GetRelativePath(projectFilePath, referencedProject.FilePath))); // SDK-style projects do not use the and elements for project references. // (Instead, those get read from the .csproj file in "Include".) if (!sdkStyle) { projectReference.Add( // no ToUpper() for uuids, most Microsoft tools seem to emit them in lowercase // (no .ToLower() as .ToString("B") already outputs lowercase) new XElement(NonSDKProjectFileNamespace + "Project", referencedProject.Guid.ToString("B")), new XElement(NonSDKProjectFileNamespace + "Name", referencedProject.ProjectName)); } itemGroup.Add(projectReference); } } } static string GetRelativePath(string fromFilePath, string toFilePath) { Uri fromUri = new Uri(fromFilePath); Uri toUri = new Uri(toFilePath); if (fromUri.Scheme != toUri.Scheme) { return toFilePath; } Uri relativeUri = fromUri.MakeRelativeUri(toUri); string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase)) { relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); } return relativePath; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Accessibility.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Diagnostics; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Enum that describes the accessibility of an entity. /// public enum Accessibility : byte { // Note: some code depends on the fact that these values are within the range 0-7 /// /// The entity is completely inaccessible. This is used for C# explicit interface implementations. /// None, /// /// The entity is only accessible within the same class. /// Private, /// /// The entity is accessible in derived classes within the same assembly. /// This corresponds to C# private protected. /// ProtectedAndInternal, /// /// The entity is only accessible within the same class and in derived classes. /// Protected, /// /// The entity is accessible within the same assembly. /// Internal, /// /// The entity is accessible both everywhere in the assembly, and in all derived classes. /// This corresponds to C# protected internal. /// ProtectedOrInternal, /// /// The entity is accessible everywhere. /// Public, } public static class AccessibilityExtensions { // This code depends on the fact that the enum values are sorted similar to the partial order // where an accessibility is smaller than another if it makes an entity visibible to a subset of the code: // digraph Accessibilities { // none -> private -> protected_and_internal -> protected -> protected_or_internal -> public; // none -> private -> protected_and_internal -> internal -> protected_or_internal -> public; // } /// /// Gets whether a <= b in the partial order of accessibilities: /// return true if b is accessible everywhere where a is accessible. /// public static bool LessThanOrEqual(this Accessibility a, Accessibility b) { // Exploit the enum order being similar to the partial order to dramatically simplify the logic here: // protected vs. internal is the only pair for which the enum value order doesn't match the partial order return a <= b && !(a == Accessibility.Protected && b == Accessibility.Internal); } /// /// Computes the intersection of the two accessibilities: /// The result is accessible from any given point in the code /// iff both a and b are accessible from that point. /// public static Accessibility Intersect(this Accessibility a, Accessibility b) { if (a > b) { ExtensionMethods.Swap(ref a, ref b); } if (a == Accessibility.Protected && b == Accessibility.Internal) { return Accessibility.ProtectedAndInternal; } else { Debug.Assert(!(a == Accessibility.Internal && b == Accessibility.Protected)); return a; } } /// /// Computes the union of the two accessibilities: /// The result is accessible from any given point in the code /// iff at least one of a or b is accessible from that point. /// public static Accessibility Union(this Accessibility a, Accessibility b) { if (a > b) { ExtensionMethods.Swap(ref a, ref b); } if (a == Accessibility.Protected && b == Accessibility.Internal) { return Accessibility.ProtectedOrInternal; } else { Debug.Assert(!(a == Accessibility.Internal && b == Accessibility.Protected)); return b; } } /// /// Gets the effective accessibility of the entity. /// For example, a public method in an internal class returns "internal". /// public static Accessibility EffectiveAccessibility(this IEntity entity) { Accessibility accessibility = entity.Accessibility; for (ITypeDefinition typeDef = entity.DeclaringTypeDefinition; typeDef != null; typeDef = typeDef.DeclaringTypeDefinition) { accessibility = Intersect(accessibility, typeDef.Accessibility); } return accessibility; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Introduces 'dynamic' and tuple types based on attribute values. /// sealed class ApplyAttributeTypeVisitor : TypeVisitor { public static IType ApplyAttributesToType( IType inputType, ICompilation compilation, SRM.CustomAttributeHandleCollection? attributes, SRM.MetadataReader metadata, TypeSystemOptions options, Nullability nullableContext, bool typeChildrenOnly = false, SRM.CustomAttributeHandleCollection? additionalAttributes = null) { bool hasDynamicAttribute = false; bool[] dynamicAttributeData = null; bool hasNativeIntegersAttribute = (options & TypeSystemOptions.NativeIntegersWithoutAttribute) != 0; bool[] nativeIntegersAttributeData = null; string[] tupleElementNames = null; Nullability nullability; Nullability[] nullableAttributeData = null; if ((options & TypeSystemOptions.NullabilityAnnotations) != 0) { nullability = nullableContext; } else { nullability = Nullability.Oblivious; } void ProcessAttribute(SRM.CustomAttributeHandle attrHandle) { var attr = metadata.GetCustomAttribute(attrHandle); var attrType = attr.GetAttributeType(metadata); if ((options & TypeSystemOptions.Dynamic) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Dynamic)) { hasDynamicAttribute = true; var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; if (arg.Value is ImmutableArray> values && values.All(v => v.Value is bool)) { dynamicAttributeData = values.SelectArray(v => (bool)v.Value); } } } else if ((options & TypeSystemOptions.NativeIntegers) != 0 && attrType.IsKnownType(metadata, KnownAttribute.NativeInteger)) { hasNativeIntegersAttribute = true; var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; if (arg.Value is ImmutableArray> values && values.All(v => v.Value is bool)) { nativeIntegersAttributeData = values.SelectArray(v => (bool)v.Value); } } } else if ((options & TypeSystemOptions.Tuple) != 0 && attrType.IsKnownType(metadata, KnownAttribute.TupleElementNames)) { var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; if (arg.Value is ImmutableArray> values && values.All(v => v.Value is string || v.Value == null)) { tupleElementNames = values.SelectArray(v => (string)v.Value); } } } else if ((options & TypeSystemOptions.NullabilityAnnotations) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Nullable)) { var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; if (arg.Value is ImmutableArray> values && values.All(v => v.Value is byte b && b <= 2)) { nullableAttributeData = values.SelectArray(v => (Nullability)(byte)v.Value); } else if (arg.Value is byte b && b <= 2) { nullability = (Nullability)b; } } } } const TypeSystemOptions relevantOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations | TypeSystemOptions.NativeIntegers; if (attributes != null && (options & relevantOptions) != 0) { foreach (var attrHandle in attributes.Value) { ProcessAttribute(attrHandle); } } if (additionalAttributes != null && (options & relevantOptions) != 0) { // Note: additional attributes will override the values from the normal attributes. foreach (var attrHandle in additionalAttributes.Value) { ProcessAttribute(attrHandle); } } if (hasDynamicAttribute || hasNativeIntegersAttribute || nullability != Nullability.Oblivious || nullableAttributeData != null || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) { var visitor = new ApplyAttributeTypeVisitor( compilation, hasDynamicAttribute, dynamicAttributeData, hasNativeIntegersAttribute, nativeIntegersAttributeData, options, tupleElementNames, nullability, nullableAttributeData ); if (typeChildrenOnly) { return inputType.VisitChildren(visitor); } else { return inputType.AcceptVisitor(visitor); } } else { return inputType; } } public static IType ApplyAttributesToType(IType inputType, ICompilation compilation, TypeSystemOptions options, PdbExtraTypeInfo pdbExtraTypeInfo) { if (pdbExtraTypeInfo.DynamicFlags is null && pdbExtraTypeInfo.TupleElementNames is null) return inputType; return inputType.AcceptVisitor(new ApplyAttributeTypeVisitor(compilation, pdbExtraTypeInfo.DynamicFlags != null, pdbExtraTypeInfo.DynamicFlags, false, null, options, pdbExtraTypeInfo.TupleElementNames, Nullability.Oblivious, null)); } readonly ICompilation compilation; readonly bool hasDynamicAttribute; readonly bool[] dynamicAttributeData; readonly bool hasNativeIntegersAttribute; readonly bool[] nativeIntegersAttributeData; readonly TypeSystemOptions options; readonly string[] tupleElementNames; readonly Nullability defaultNullability; readonly Nullability[] nullableAttributeData; int dynamicTypeIndex = 0; int tupleTypeIndex = 0; int nullabilityTypeIndex = 0; int nativeIntTypeIndex = 0; private ApplyAttributeTypeVisitor(ICompilation compilation, bool hasDynamicAttribute, bool[] dynamicAttributeData, bool hasNativeIntegersAttribute, bool[] nativeIntegersAttributeData, TypeSystemOptions options, string[] tupleElementNames, Nullability defaultNullability, Nullability[] nullableAttributeData) { this.compilation = compilation ?? throw new ArgumentNullException(nameof(compilation)); this.hasDynamicAttribute = hasDynamicAttribute; this.dynamicAttributeData = dynamicAttributeData; this.hasNativeIntegersAttribute = hasNativeIntegersAttribute; this.nativeIntegersAttributeData = nativeIntegersAttributeData; this.options = options; this.tupleElementNames = tupleElementNames; this.defaultNullability = defaultNullability; this.nullableAttributeData = nullableAttributeData; } public override IType VisitModOpt(ModifiedType type) { dynamicTypeIndex++; if ((options & TypeSystemOptions.KeepModifiers) != 0) return base.VisitModOpt(type); else return type.ElementType.AcceptVisitor(this); } public override IType VisitModReq(ModifiedType type) { dynamicTypeIndex++; if ((options & TypeSystemOptions.KeepModifiers) != 0) return base.VisitModReq(type); else return type.ElementType.AcceptVisitor(this); } public override IType VisitPointerType(PointerType type) { dynamicTypeIndex++; return base.VisitPointerType(type); } Nullability GetNullability() { if (nullabilityTypeIndex < nullableAttributeData?.Length) return nullableAttributeData[nullabilityTypeIndex++]; else return defaultNullability; } void ExpectDummyNullabilityForGenericValueType() { var n = GetNullability(); Debug.Assert(n == Nullability.Oblivious); } public override IType VisitArrayType(ArrayType type) { var nullability = GetNullability(); dynamicTypeIndex++; return base.VisitArrayType(type).ChangeNullability(nullability); } public override IType VisitByReferenceType(ByReferenceType type) { dynamicTypeIndex++; return base.VisitByReferenceType(type); } public override IType VisitParameterizedType(ParameterizedType type) { bool useTupleTypes = (options & TypeSystemOptions.Tuple) != 0; if (useTupleTypes && TupleType.IsTupleCompatible(type, out int tupleCardinality)) { if (tupleCardinality > 1) { var valueTupleAssembly = type.GetDefinition()?.ParentModule; ImmutableArray elementNames = default; if (tupleElementNames != null && tupleTypeIndex < tupleElementNames.Length) { string[] extractedValues = new string[tupleCardinality]; Array.Copy(tupleElementNames, tupleTypeIndex, extractedValues, 0, Math.Min(tupleCardinality, tupleElementNames.Length - tupleTypeIndex)); elementNames = ImmutableArray.CreateRange(extractedValues); } tupleTypeIndex += tupleCardinality; ExpectDummyNullabilityForGenericValueType(); var elementTypes = ImmutableArray.CreateBuilder(tupleCardinality); do { int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1); for (int i = 0; i < normalArgCount; i++) { dynamicTypeIndex++; elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this)); } if (type.TypeArguments.Count == TupleType.RestPosition) { type = type.TypeArguments.Last() as ParameterizedType; ExpectDummyNullabilityForGenericValueType(); dynamicTypeIndex++; if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) { tupleTypeIndex += nestedCardinality; } else { Debug.Fail("TRest should be another value tuple"); type = null; } } else { type = null; } } while (type != null); Debug.Assert(elementTypes.Count == tupleCardinality); return new TupleType( compilation, elementTypes.MoveToImmutable(), elementNames, valueTupleAssembly ); } else { // C# doesn't have syntax for tuples of cardinality <= 1 tupleTypeIndex += tupleCardinality; } } // Visit generic type and type arguments. // Like base implementation, except that it increments dynamicTypeIndex. var genericType = type.GenericType.AcceptVisitor(this); if (genericType.IsReferenceType != true && !genericType.IsKnownType(KnownTypeCode.NullableOfT)) { ExpectDummyNullabilityForGenericValueType(); } bool changed = type.GenericType != genericType; var arguments = new IType[type.TypeArguments.Count]; for (int i = 0; i < type.TypeArguments.Count; i++) { dynamicTypeIndex++; arguments[i] = type.TypeArguments[i].AcceptVisitor(this); changed = changed || arguments[i] != type.TypeArguments[i]; } if (!changed) return type; return new ParameterizedType(genericType, arguments); } public override IType VisitFunctionPointerType(FunctionPointerType type) { dynamicTypeIndex++; if (type.ReturnIsRefReadOnly) { dynamicTypeIndex++; } var returnType = type.ReturnType.AcceptVisitor(this); bool changed = type.ReturnType != returnType; var parameters = new IType[type.ParameterTypes.Length]; for (int i = 0; i < parameters.Length; i++) { dynamicTypeIndex += type.ParameterReferenceKinds[i] switch { ReferenceKind.None => 1, ReferenceKind.Ref => 1, ReferenceKind.Out => 2, // in/out also count the modreq ReferenceKind.In => 2, ReferenceKind.RefReadOnly => 2, // counts the modopt _ => throw new NotSupportedException() }; parameters[i] = type.ParameterTypes[i].AcceptVisitor(this); changed = changed || parameters[i] != type.ParameterTypes[i]; } if (!changed) return type; return type.WithSignature(returnType, parameters.ToImmutableArray()); } public override IType VisitTypeDefinition(ITypeDefinition type) { IType newType = type; var ktc = type.KnownTypeCode; if (ktc == KnownTypeCode.Object && hasDynamicAttribute) { if (dynamicAttributeData == null || dynamicTypeIndex >= dynamicAttributeData.Length) newType = SpecialType.Dynamic; else if (dynamicAttributeData[dynamicTypeIndex]) newType = SpecialType.Dynamic; } else if ((ktc == KnownTypeCode.IntPtr || ktc == KnownTypeCode.UIntPtr) && hasNativeIntegersAttribute) { // native integers use the same indexing logic as 'dynamic' if (nativeIntegersAttributeData == null || nativeIntTypeIndex >= nativeIntegersAttributeData.Length) newType = (ktc == KnownTypeCode.IntPtr ? SpecialType.NInt : SpecialType.NUInt); else if (nativeIntegersAttributeData[nativeIntTypeIndex]) newType = (ktc == KnownTypeCode.IntPtr ? SpecialType.NInt : SpecialType.NUInt); nativeIntTypeIndex++; } if (type.IsReferenceType == true) { Nullability nullability = GetNullability(); return newType.ChangeNullability(nullability); } else { return newType; } } public override IType VisitOtherType(IType type) { type = base.VisitOtherType(type); if (type.Kind == TypeKind.Unknown && type.IsReferenceType == true) { Nullability nullability = GetNullability(); type = type.ChangeNullability(nullability); } return type; } public override IType VisitTypeParameter(ITypeParameter type) { Nullability nullability = GetNullability(); return type.ChangeNullability(nullability); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ArrayType.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents an array type. /// public sealed class ArrayType : TypeWithElementType, ICompilationProvider { readonly int dimensions; readonly ICompilation compilation; readonly Nullability nullability; public ArrayType(ICompilation compilation, IType elementType, int dimensions = 1, Nullability nullability = Nullability.Oblivious) : base(elementType) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (dimensions <= 0) throw new ArgumentOutOfRangeException(nameof(dimensions), dimensions, "dimensions must be positive"); this.compilation = compilation; this.dimensions = dimensions; this.nullability = nullability; ICompilationProvider p = elementType as ICompilationProvider; if (p != null && p.Compilation != compilation) throw new InvalidOperationException("Cannot create an array type using a different compilation from the element type."); } public override TypeKind Kind { get { return TypeKind.Array; } } public ICompilation Compilation { get { return compilation; } } public int Dimensions { get { return dimensions; } } public override Nullability Nullability => nullability; public override IType ChangeNullability(Nullability nullability) { if (nullability == this.nullability) return this; else return new ArrayType(compilation, elementType, dimensions, nullability); } public override string NameSuffix { get { return "[" + new string(',', dimensions - 1) + "]"; } } public override bool? IsReferenceType { get { return true; } } public override int GetHashCode() { return unchecked(elementType.GetHashCode() * 71681 + dimensions); } public override bool Equals(IType other) { ArrayType a = other as ArrayType; return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions && a.nullability == nullability; } public override string ToString() { switch (nullability) { case Nullability.Nullable: return elementType.ToString() + NameSuffix + "?"; case Nullability.NotNullable: return elementType.ToString() + NameSuffix + "!"; default: return elementType.ToString() + NameSuffix; } } public override IEnumerable DirectBaseTypes { get { List baseTypes = new List(); IType t = compilation.FindType(KnownTypeCode.Array); if (t.Kind != TypeKind.Unknown) baseTypes.Add(t); if (dimensions == 1 && elementType.Kind != TypeKind.Pointer) { // single-dimensional arrays implement IList ITypeDefinition def = compilation.FindType(KnownTypeCode.IListOfT) as ITypeDefinition; if (def != null) baseTypes.Add(new ParameterizedType(def, new[] { elementType })); // And in .NET 4.5 they also implement IReadOnlyList def = compilation.FindType(KnownTypeCode.IReadOnlyListOfT) as ITypeDefinition; if (def != null) baseTypes.Add(new ParameterizedType(def, new[] { elementType })); } return baseTypes; } } public override IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return compilation.FindType(KnownTypeCode.Array).GetMethods(filter, options); } public override IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return compilation.FindType(KnownTypeCode.Array).GetMethods(typeArguments, filter, options); } public override IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return compilation.FindType(KnownTypeCode.Array).GetAccessors(filter, options); } public override IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return compilation.FindType(KnownTypeCode.Array).GetProperties(filter, options); } // NestedTypes, Events, Fields: System.Array doesn't have any; so we can use the AbstractType default implementation // that simply returns an empty list public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitArrayType(this); } public override IType VisitChildren(TypeVisitor visitor) { IType e = elementType.AcceptVisitor(visitor); if (e == elementType) return this; else return new ArrayType(compilation, e, dimensions, nullability); } } [Serializable] public sealed class ArrayTypeReference : ITypeReference, ISupportsInterning { readonly ITypeReference elementType; readonly int dimensions; public ArrayTypeReference(ITypeReference elementType, int dimensions = 1) { if (elementType == null) throw new ArgumentNullException(nameof(elementType)); if (dimensions <= 0) throw new ArgumentOutOfRangeException(nameof(dimensions), dimensions, "dimensions must be positive"); this.elementType = elementType; this.dimensions = dimensions; } public ITypeReference ElementType { get { return elementType; } } public int Dimensions { get { return dimensions; } } public IType Resolve(ITypeResolveContext context) { return new ArrayType(context.Compilation, elementType.Resolve(context), dimensions); } public override string ToString() { return elementType.ToString() + "[" + new string(',', dimensions - 1) + "]"; } int ISupportsInterning.GetHashCodeForInterning() { return elementType.GetHashCode() ^ dimensions; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { ArrayTypeReference o = other as ArrayTypeReference; return o != null && elementType == o.elementType && dimensions == o.dimensions; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/AssemblyQualifiedTypeName.cs ================================================ // Copyright (c) 2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { public struct AssemblyQualifiedTypeName : IEquatable { public readonly string AssemblyName; public readonly FullTypeName TypeName; public AssemblyQualifiedTypeName(FullTypeName typeName, string assemblyName) { this.AssemblyName = assemblyName; this.TypeName = typeName; } public AssemblyQualifiedTypeName(ITypeDefinition typeDefinition) { this.AssemblyName = typeDefinition.ParentModule.AssemblyName; this.TypeName = typeDefinition.FullTypeName; } public override string ToString() { if (string.IsNullOrEmpty(AssemblyName)) return TypeName.ToString(); else return TypeName.ToString() + ", " + AssemblyName; } public override bool Equals(object obj) { return (obj is AssemblyQualifiedTypeName) && Equals((AssemblyQualifiedTypeName)obj); } public bool Equals(AssemblyQualifiedTypeName other) { return this.AssemblyName == other.AssemblyName && this.TypeName == other.TypeName; } public override int GetHashCode() { int hashCode = 0; unchecked { if (AssemblyName != null) hashCode += 1000000007 * AssemblyName.GetHashCode(); hashCode += TypeName.GetHashCode(); } return hashCode; } public static bool operator ==(AssemblyQualifiedTypeName lhs, AssemblyQualifiedTypeName rhs) { return lhs.Equals(rhs); } public static bool operator !=(AssemblyQualifiedTypeName lhs, AssemblyQualifiedTypeName rhs) { return !lhs.Equals(rhs); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { public sealed class ByReferenceType : TypeWithElementType { public ByReferenceType(IType elementType) : base(elementType) { } public override TypeKind Kind { get { return TypeKind.ByReference; } } public override string NameSuffix { get { return "&"; } } public override bool? IsReferenceType { get { return null; } } public override bool IsByRefLike => true; public override int GetHashCode() { return elementType.GetHashCode() ^ 91725813; } public override bool Equals(IType other) { ByReferenceType a = other as ByReferenceType; return a != null && elementType.Equals(a.elementType); } public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitByReferenceType(this); } public override IType VisitChildren(TypeVisitor visitor) { IType e = elementType.AcceptVisitor(visitor); if (e == elementType) return this; else return new ByReferenceType(e); } } [Serializable] public sealed class ByReferenceTypeReference : ITypeReference, ISupportsInterning { readonly ITypeReference elementType; public ByReferenceTypeReference(ITypeReference elementType) { if (elementType == null) throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } public ITypeReference ElementType { get { return elementType; } } public IType Resolve(ITypeResolveContext context) { return new ByReferenceType(elementType.Resolve(context)); } public override string ToString() { return elementType.ToString() + "&"; } int ISupportsInterning.GetHashCodeForInterning() { return elementType.GetHashCode() ^ 91725814; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { ByReferenceTypeReference brt = other as ByReferenceTypeReference; return brt != null && this.elementType == brt.elementType; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ComHelper.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Linq; using ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Helper methods for COM. /// public static class ComHelper { /// /// Gets whether the specified type is imported from COM. /// public static bool IsComImport(ITypeDefinition typeDefinition) { return typeDefinition != null && typeDefinition.Kind == TypeKind.Interface && typeDefinition.HasAttribute(KnownAttribute.ComImport); } /// /// Gets the CoClass of the specified COM interface. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Co", Justification = "Consistent with CoClassAttribute")] public static IType GetCoClass(ITypeDefinition typeDefinition) { if (typeDefinition == null) return SpecialType.UnknownType; var coClassAttribute = typeDefinition.GetAttribute(KnownAttribute.CoClass); if (coClassAttribute != null && coClassAttribute.FixedArguments.Length == 1) { if (coClassAttribute.FixedArguments[0].Value is IType ty) return ty; } return SpecialType.UnknownType; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Options that control how metadata is represented in the type system. /// [Flags] public enum TypeSystemOptions { /// /// No options enabled; stay as close to the metadata as possible. /// None = 0, /// /// [DynamicAttribute] is used to replace 'object' types with the 'dynamic' type. /// /// If this option is not active, the 'dynamic' type is not used, and the attribute is preserved. /// Dynamic = 1, /// /// Tuple types are represented using the TupleType class. /// [TupleElementNames] is used to name the tuple elements. /// /// If this option is not active, the tuples are represented using their underlying type, and the attribute is preserved. /// Tuple = 2, /// /// If this option is active, [ExtensionAttribute] is removed and methods are marked as IsExtensionMethod. /// Otherwise, the attribute is preserved but the methods are not marked. /// ExtensionMethods = 4, /// /// Only load the public API into the type system. /// OnlyPublicAPI = 8, /// /// Do not cache accessed entities. /// In a normal type system (without this option), every type or member definition has exactly one ITypeDefinition/IMember /// instance. This instance is kept alive until the whole type system can be garbage-collected. /// When this option is specified, the type system avoids these caches. /// This reduces the memory usage in many cases, but increases the number of allocations. /// Also, some code in the decompiler expects to be able to compare type/member definitions by reference equality, /// and thus will fail with uncached type systems. /// Uncached = 0x10, /// /// If this option is active, [DecimalConstantAttribute] is removed and constant values are transformed into simple decimal literals. /// DecimalConstants = 0x20, /// /// If this option is active, modopt and modreq types are preserved in the type system. /// /// Note: the decompiler currently does not support handling modified types; /// activating this option may lead to incorrect decompilation or internal errors. /// KeepModifiers = 0x40, /// /// If this option is active, [IsReadOnlyAttribute] on parameters+structs is removed /// and parameters are marked as in, structs as readonly. /// Otherwise, the attribute is preserved but the parameters and structs are not marked. /// ReadOnlyStructsAndParameters = 0x80, /// /// If this option is active, [IsByRefLikeAttribute] is removed and structs are marked as ref. /// Otherwise, the attribute is preserved but the structs are not marked. /// RefStructs = 0x100, /// /// If this option is active, [IsUnmanagedAttribute] is removed from type parameters, /// and HasUnmanagedConstraint is set instead. /// UnmanagedConstraints = 0x200, /// /// If this option is active, [NullableAttribute] is removed and reference types with /// nullability annotations are used instead. /// NullabilityAnnotations = 0x400, /// /// If this option is active, [IsReadOnlyAttribute] on methods is removed /// and the method marked as ThisIsRefReadOnly. /// ReadOnlyMethods = 0x800, /// /// [NativeIntegerAttribute] is used to replace 'IntPtr' types with the 'nint' type. /// NativeIntegers = 0x1000, /// /// Allow function pointer types. If this option is not enabled, function pointers are /// replaced with the 'IntPtr' type. /// FunctionPointers = 0x2000, /// /// Allow C# 11 scoped annotation. If this option is not enabled, ScopedRefAttribute /// will be reported as custom attribute. /// ScopedRef = 0x4000, /// /// Replace 'IntPtr' types with the 'nint' type even in absence of [NativeIntegerAttribute]. /// Note: DecompilerTypeSystem constructor removes this setting from the options if /// not targeting .NET 7 or later. /// NativeIntegersWithoutAttribute = 0x8000, /// /// If this option is active, [RequiresLocationAttribute] on parameters is removed /// and parameters are marked as ref readonly. /// Otherwise, the attribute is preserved but the parameters are not marked /// as if it was a ref parameter without any attributes. /// RefReadOnlyParameters = 0x10000, /// /// If this option is active, [ParamCollectionAttribute] on parameters is removed /// and parameters are marked as params. /// Otherwise, the attribute is preserved but the parameters are not marked /// as if it was a normal parameter without any attributes. /// ParamsCollections = 0x20000, /// /// If this option is active, span types (Span<T> and ReadOnlySpan<T>) are treated like /// built-in types and language rules of C# 14 and later are applied. /// FirstClassSpanTypes = 0x40000, /// /// Default settings: typical options for the decompiler, with all C# language features enabled. /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods | NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute | RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes } /// /// Manages the NRefactory type system for the decompiler. /// /// /// This class is thread-safe. /// public class DecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem { public static TypeSystemOptions GetOptions(DecompilerSettings settings) { var typeSystemOptions = TypeSystemOptions.None; if (settings.Dynamic) typeSystemOptions |= TypeSystemOptions.Dynamic; if (settings.TupleTypes) typeSystemOptions |= TypeSystemOptions.Tuple; if (settings.ExtensionMethods) typeSystemOptions |= TypeSystemOptions.ExtensionMethods; if (settings.DecimalConstants) typeSystemOptions |= TypeSystemOptions.DecimalConstants; if (settings.IntroduceRefModifiersOnStructs) typeSystemOptions |= TypeSystemOptions.RefStructs; if (settings.IntroduceReadonlyAndInModifiers) typeSystemOptions |= TypeSystemOptions.ReadOnlyStructsAndParameters; if (settings.IntroduceUnmanagedConstraint) typeSystemOptions |= TypeSystemOptions.UnmanagedConstraints; if (settings.NullableReferenceTypes) typeSystemOptions |= TypeSystemOptions.NullabilityAnnotations; if (settings.ReadOnlyMethods) typeSystemOptions |= TypeSystemOptions.ReadOnlyMethods; if (settings.NativeIntegers) typeSystemOptions |= TypeSystemOptions.NativeIntegers; if (settings.FunctionPointers) typeSystemOptions |= TypeSystemOptions.FunctionPointers; if (settings.ScopedRef) typeSystemOptions |= TypeSystemOptions.ScopedRef; if (settings.NumericIntPtr) typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute; if (settings.RefReadOnlyParameters) typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters; if (settings.ParamsCollections) typeSystemOptions |= TypeSystemOptions.ParamsCollections; if (settings.FirstClassSpanTypes) typeSystemOptions |= TypeSystemOptions.FirstClassSpanTypes; return typeSystemOptions; } public static Task CreateAsync(PEFile mainModule, IAssemblyResolver assemblyResolver) { return CreateAsync(mainModule, assemblyResolver, TypeSystemOptions.Default); } public static Task CreateAsync(PEFile mainModule, IAssemblyResolver assemblyResolver, DecompilerSettings settings) { return CreateAsync(mainModule, assemblyResolver, GetOptions(settings ?? throw new ArgumentNullException(nameof(settings)))); } public static async Task CreateAsync(PEFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions) { if (mainModule == null) throw new ArgumentNullException(nameof(mainModule)); if (assemblyResolver == null) throw new ArgumentNullException(nameof(assemblyResolver)); var ts = new DecompilerTypeSystem(typeSystemOptions); await ts.InitializeAsync(mainModule, assemblyResolver) .ConfigureAwait(false); return ts; } private MetadataModule mainModule; private TypeSystemOptions typeSystemOptions; private DecompilerTypeSystem(TypeSystemOptions typeSystemOptions) { this.typeSystemOptions = typeSystemOptions; } public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver) : this(mainModule, assemblyResolver, TypeSystemOptions.Default) { } public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver, DecompilerSettings settings) : this(mainModule, assemblyResolver, GetOptions(settings ?? throw new ArgumentNullException(nameof(settings)))) { } public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions) : this(typeSystemOptions) { if (mainModule == null) throw new ArgumentNullException(nameof(mainModule)); if (assemblyResolver == null) throw new ArgumentNullException(nameof(assemblyResolver)); InitializeAsync(mainModule, assemblyResolver).GetAwaiter().GetResult(); } static readonly string[] implicitReferences = new[] { "System.Runtime.InteropServices", "System.Runtime.CompilerServices.Unsafe" }; private async Task InitializeAsync(MetadataFile mainModule, IAssemblyResolver assemblyResolver) { // Load referenced assemblies and type-forwarder references. // This is necessary to make .NET Core/PCL binaries work better. var referencedAssemblies = new List(); var assemblyReferenceQueue = new Queue<(bool IsAssembly, MetadataFile MainModule, object Reference, Task ResolveTask)>(); var comparer = KeyComparer.Create(((bool IsAssembly, MetadataFile MainModule, object Reference) reference) => reference.IsAssembly ? "A:" + ((IAssemblyReference)reference.Reference).FullName : "M:" + reference.Reference); var assemblyReferencesInQueue = new HashSet<(bool IsAssembly, MetadataFile Parent, object Reference)>(comparer); var mainMetadata = mainModule.Metadata; var tfm = mainModule.DetectTargetFrameworkId(); var (identifier, version) = UniversalAssemblyResolver.ParseTargetFramework(tfm); foreach (var h in mainMetadata.GetModuleReferences()) { try { var moduleRef = mainMetadata.GetModuleReference(h); var moduleName = mainMetadata.GetString(moduleRef.Name); foreach (var fileHandle in mainMetadata.AssemblyFiles) { var file = mainMetadata.GetAssemblyFile(fileHandle); if (mainMetadata.StringComparer.Equals(file.Name, moduleName) && file.ContainsMetadata) { AddToQueue(false, mainModule, moduleName); break; } } } catch (BadImageFormatException) { } } foreach (var refs in mainModule.AssemblyReferences) { AddToQueue(true, mainModule, refs); } while (assemblyReferenceQueue.Count > 0) { var asmRef = assemblyReferenceQueue.Dequeue(); var asm = await asmRef.ResolveTask.ConfigureAwait(false); if (asm != null) { referencedAssemblies.Add(asm); var metadata = asm.Metadata; foreach (var h in metadata.ExportedTypes) { var exportedType = metadata.GetExportedType(h); switch (exportedType.Implementation.Kind) { case SRM.HandleKind.AssemblyReference: AddToQueue(true, asm, new AssemblyReference(asm, (SRM.AssemblyReferenceHandle)exportedType.Implementation)); break; case SRM.HandleKind.AssemblyFile: var file = metadata.GetAssemblyFile((SRM.AssemblyFileHandle)exportedType.Implementation); AddToQueue(false, asm, metadata.GetString(file.Name)); break; } } } if (assemblyReferenceQueue.Count == 0) { // For .NET Core and .NET 5 and newer, we need to pull in implicit references which are not included in the metadata, // as they contain compile-time-only types, such as System.Runtime.InteropServices.dll (for DllImport, MarshalAs, etc.) switch (identifier) { case TargetFrameworkIdentifier.NETCoreApp: case TargetFrameworkIdentifier.NETStandard: case TargetFrameworkIdentifier.NET: foreach (var item in implicitReferences) { var existing = referencedAssemblies.FirstOrDefault(asm => asm.Name == item); if (existing == null) { AddToQueue(true, mainModule, AssemblyNameReference.Parse(item + ", Version=" + version.ToString(3) + ".0, Culture=neutral")); } } break; } } } if (!(identifier == TargetFrameworkIdentifier.NET && version >= new Version(7, 0))) { typeSystemOptions &= ~TypeSystemOptions.NativeIntegersWithoutAttribute; } var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions); // create IModuleReferences for all references var referencedAssembliesWithOptions = new List(referencedAssemblies.Count); Dictionary referenceAssemblyVersionMap = new(); foreach (var file in referencedAssemblies) { // if the file is an assembly, we need to make sure to deduplicate all assemblies, // with the same name, but different version. We keep the highest version number. if (file.IsAssembly) { var newFileVersion = file.Metadata.GetAssemblyDefinition().Version; if (referenceAssemblyVersionMap.TryGetValue(file.Name, out var info)) { if (newFileVersion >= info.version) { referencedAssembliesWithOptions[info.insertionIndex] = file.WithOptions(typeSystemOptions); referenceAssemblyVersionMap[file.Name] = (newFileVersion, info.insertionIndex); } continue; } else { referenceAssemblyVersionMap[file.Name] = (file.Metadata.GetAssemblyDefinition().Version, referencedAssembliesWithOptions.Count); } } referencedAssembliesWithOptions.Add(file.WithOptions(typeSystemOptions)); } // Primitive types are necessary to avoid assertions in ILReader. // Other known types are necessary in order for transforms to work (e.g. Task for async transform). // Figure out which known types are missing from our type system so far: var missingKnownTypes = KnownTypeReference.AllKnownTypes.Where(IsMissing).ToList(); if (missingKnownTypes.Count > 0) { Init(mainModuleWithOptions, referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.CreateWithTypes(missingKnownTypes) })); } else { Init(mainModuleWithOptions, referencedAssembliesWithOptions); } this.mainModule = (MetadataModule)base.MainModule; void AddToQueue(bool isAssembly, MetadataFile mainModule, object reference) { if (assemblyReferencesInQueue.Add((isAssembly, mainModule, reference))) { // Immediately start loading the referenced module as we add the entry to the queue. // This allows loading multiple modules in parallel. Task asm; if (isAssembly) { asm = assemblyResolver.ResolveAsync((IAssemblyReference)reference); } else { asm = assemblyResolver.ResolveModuleAsync(mainModule, (string)reference); } assemblyReferenceQueue.Enqueue((isAssembly, mainModule, reference, asm)); } } bool IsMissing(KnownTypeReference knownType) { var name = knownType.TypeName; if (!mainModule.GetTypeDefinition(name).IsNil) return false; foreach (var file in referencedAssemblies) { if (!file.GetTypeDefinition(name).IsNil) return false; } return true; } } public new MetadataModule MainModule => mainModule; public override TypeSystemOptions TypeSystemOptions => typeSystemOptions; } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs ================================================ // Copyright (c) 2025 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { public class ExtensionInfo { readonly Dictionary extensionMemberMap; readonly Dictionary implementationMemberMap; public ExtensionInfo(MetadataModule module, ITypeDefinition extensionContainer) { this.extensionMemberMap = new(); this.implementationMemberMap = new(); var metadata = module.MetadataFile.Metadata; foreach (var nestedType in extensionContainer.NestedTypes) { if (TryEncodingV1(nestedType)) { continue; } TryEncodingV2(nestedType); } bool TryEncodingV1(ITypeDefinition extGroup) { if (!(extGroup is { Kind: TypeKind.Class, IsSealed: true } && extGroup.Name.StartsWith("<>E__", System.StringComparison.Ordinal))) { return false; } TypeDefinition td = metadata.GetTypeDefinition((TypeDefinitionHandle)extGroup.MetadataToken); IMethod? marker = null; bool hasMultipleMarkers = false; List extensionMethods = []; ITypeParameter[] extensionGroupTypeParameters = extGroup.TypeParameters.ToArray(); // For easier access to accessors we use SRM foreach (var h in td.GetMethods()) { var method = module.GetDefinition(h); if (method.SymbolKind is SymbolKind.Constructor) continue; if (method is { Name: "$", IsStatic: true, Parameters.Count: 1 }) { if (marker == null) marker = method; else hasMultipleMarkers = true; continue; } extensionMethods.Add(method); } if (marker == null || hasMultipleMarkers) return false; CollectImplementationMethods(extGroup, marker, extensionMethods, extensionGroupTypeParameters); return true; } bool TryEncodingV2(ITypeDefinition extensionGroupsContainer) { // there exists one nested type per extension target type if (!(extensionGroupsContainer is { Kind: TypeKind.Class, IsSealed: true } && extensionGroupsContainer.Name.StartsWith("$", StringComparison.Ordinal))) { return false; } // if there are multiple extension-blocks with the same target type, // but different names for the extension parameter, // there is a separate markerType, so there are multiple marker types per // target type foreach (var markerType in extensionGroupsContainer.NestedTypes) { if (!(markerType.Name.StartsWith("$", StringComparison.Ordinal) && markerType.IsStatic)) continue; var marker = markerType.Methods.SingleOrDefault(m => m.Name == "$" && m.IsStatic && m.Parameters.Count == 1); if (marker == null) continue; TypeDefinition td = metadata.GetTypeDefinition((TypeDefinitionHandle)extensionGroupsContainer.MetadataToken); List extensionMethods = []; ITypeParameter[] extensionGroupTypeParameters = new ITypeParameter[extensionGroupsContainer.TypeParameterCount]; // For easier access to accessors we use SRM foreach (var h in td.GetMethods()) { var method = module.GetDefinition(h); if (method.SymbolKind is SymbolKind.Constructor) continue; var attribute = method.GetAttribute(KnownAttribute.ExtensionMarker); if (attribute == null) continue; if (attribute.FixedArguments[0].Value?.ToString() != markerType.Name) continue; extensionMethods.Add(method); } CollectImplementationMethods(extensionGroupsContainer, marker, extensionMethods, extensionGroupTypeParameters); } return true; } void CollectImplementationMethods(ITypeDefinition extGroup, IMethod marker, List extensionMethods, ITypeParameter[] extensionGroupTypeParameters) { List<(IMethod extension, IMethod implementation)> implementations = []; string[] typeParameterNames = new string[extGroup.TypeParameterCount]; foreach (var extension in extensionMethods) { int expectedTypeParameterCount = extension.TypeParameters.Count + extGroup.TypeParameterCount; bool hasInstance = !extension.IsStatic; int parameterOffset = hasInstance ? 1 : 0; int expectedParameterCount = extension.Parameters.Count + parameterOffset; TypeParameterSubstitution subst = new TypeParameterSubstitution([], [.. extGroup.TypeArguments, .. extension.TypeArguments]); bool IsMatchingImplementation(IMethod impl) { if (!impl.IsStatic) return false; if (extension.Name != impl.Name) return false; if (expectedTypeParameterCount != impl.TypeParameters.Count) return false; if (expectedParameterCount != impl.Parameters.Count) return false; if (hasInstance) { IType ti = impl.Parameters[0].Type.AcceptVisitor(subst); IType tm = marker.Parameters.Single().Type; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(ti, tm)) return false; } for (int i = 0; i < extension.Parameters.Count; i++) { IType ti = impl.Parameters[i + parameterOffset].Type.AcceptVisitor(subst); IType tm = extension.Parameters[i].Type; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(ti, tm)) return false; } return NormalizeTypeVisitor.TypeErasure.EquivalentTypes( impl.ReturnType.AcceptVisitor(subst), extension.ReturnType ); } IMethod? foundImpl = null; foreach (var impl in extensionContainer.Methods) { if (!IsMatchingImplementation(impl)) continue; Debug.Assert(foundImpl == null, "Multiple matching implementations found"); foundImpl = impl; } Debug.Assert(foundImpl != null, "No matching implementation found"); implementations.Add((extension, foundImpl)); } foreach (var (extension, implementation) in implementations) { for (int i = 0; i < extensionGroupTypeParameters.Length; i++) { if (typeParameterNames[i] == null) { typeParameterNames[i] = implementation.TypeParameters[i].Name; } else if (typeParameterNames[i] != implementation.TypeParameters[i].Name) { // TODO: Handle name conflicts properly typeParameterNames[i] = $"T{i + 1}"; } } } for (int i = 0; i < extensionGroupTypeParameters.Length; i++) { var originalTypeParameter = extGroup.TypeParameters[i]; if (extensionGroupTypeParameters[i] == null) { extensionGroupTypeParameters[i] = new DefaultTypeParameter( extGroup, i, typeParameterNames[i], VarianceModifier.Invariant, attributes: originalTypeParameter.GetAttributes().ToArray(), originalTypeParameter.HasValueTypeConstraint, originalTypeParameter.HasReferenceTypeConstraint, originalTypeParameter.HasDefaultConstructorConstraint, originalTypeParameter.TypeConstraints.Select(c => c.Type).ToArray(), originalTypeParameter.NullabilityConstraint ); } } foreach (var (extension, implementation) in implementations) { var info = new ExtensionMemberInfo(marker, extension, implementation, extensionGroupTypeParameters); this.extensionMemberMap[extension] = info; this.implementationMemberMap[implementation] = info; } } } public ExtensionMemberInfo? InfoOfExtensionMember(IMethod method) { return this.extensionMemberMap.TryGetValue(method, out var value) ? value : null; } public ExtensionMemberInfo? InfoOfImplementationMember(IMethod method) { return this.implementationMemberMap.TryGetValue(method, out var value) ? value : null; } public IEnumerable> GetGroups() { return this.extensionMemberMap.Values.GroupBy(x => (x.ExtensionMarkerMethod, x.ExtensionGroupingTypeParameters)); } public bool IsExtensionGroupingType(ITypeDefinition type) { return this.extensionMemberMap.Values.Any(x => x.ExtensionGroupingType.Equals(type)); } } public readonly struct ExtensionMemberInfo(IMethod marker, IMethod extension, IMethod implementation, ITypeParameter[] extensionGroupingTypeParameters) { /// /// Metadata-only method called '<Extension>$'. Has the C# signature for the extension declaration. /// /// extension(ReceiverType name) {} -> void <Extension>$(ReceiverType name) {} /// public readonly IMethod ExtensionMarkerMethod = marker; /// /// Metadata-only method with a signature as declared in C# within the extension declaration. /// This could also be an accessor of an extension property. /// public readonly IMethod ExtensionMember = extension; /// /// The actual implementation method in the outer class. The signature is a concatenation /// of the extension marker and the extension member's signatures. /// public readonly IMethod ImplementationMethod = implementation; /// /// This is the enclosing static class. /// public ITypeDefinition ExtensionContainer => ImplementationMethod.DeclaringTypeDefinition!; /// /// This is the compiler-generated class containing the extension members. Has type parameters /// from the extension declaration with minimal constraints. /// public ITypeDefinition ExtensionGroupingType => ExtensionMember.DeclaringTypeDefinition!; /// /// This is the array of type parameters for the extension declaration. /// public ITypeParameter[] ExtensionGroupingTypeParameters => extensionGroupingTypeParameters; /// /// This class holds the type parameters for the extension declaration with full fidelity of C# constraints. /// public ITypeDefinition ExtensionMarkerType => ExtensionMarkerMethod.DeclaringTypeDefinition!; } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Holds the full name of a type definition. /// A full type name uniquely identifies a type definition within a single assembly. /// /// /// A full type name can only represent type definitions, not arbitrary types. /// It does not include any type arguments, and can not refer to array or pointer types. /// /// A full type name represented as reflection name has the syntax: /// NamespaceName '.' TopLevelTypeName ['`'#] { '+' NestedTypeName ['`'#] } /// [Serializable] public readonly struct FullTypeName : IEquatable { [Serializable] readonly struct NestedTypeName { public readonly string Name; public readonly int AdditionalTypeParameterCount; public NestedTypeName(string name, int additionalTypeParameterCount) { if (name == null) throw new ArgumentNullException(nameof(name)); this.Name = name; this.AdditionalTypeParameterCount = additionalTypeParameterCount; } } readonly TopLevelTypeName topLevelType; readonly NestedTypeName[] nestedTypes; FullTypeName(TopLevelTypeName topLevelTypeName, NestedTypeName[] nestedTypes) { this.topLevelType = topLevelTypeName; this.nestedTypes = nestedTypes; } /// /// Constructs a FullTypeName representing the given top-level type. /// /// /// FullTypeName has an implicit conversion operator from TopLevelTypeName, /// so you can simply write: /// FullTypeName f = new TopLevelTypeName(...); /// public FullTypeName(TopLevelTypeName topLevelTypeName) { this.topLevelType = topLevelTypeName; this.nestedTypes = null; } /// /// Constructs a FullTypeName by parsing the given reflection name. /// Note that FullTypeName can only represent type definition names. If the reflection name /// might refer to a parameterized type or array etc., use /// instead. /// /// /// Expected syntax: NamespaceName '.' TopLevelTypeName ['`'#] { '+' NestedTypeName ['`'#] } /// where # are type parameter counts /// public FullTypeName(string reflectionName) { int pos = reflectionName.IndexOf('+'); if (pos < 0) { // top-level type this.topLevelType = new TopLevelTypeName(reflectionName); this.nestedTypes = null; } else { // nested type string[] parts = reflectionName.Split('+'); this.topLevelType = new TopLevelTypeName(parts[0]); this.nestedTypes = new NestedTypeName[parts.Length - 1]; for (int i = 0; i < nestedTypes.Length; i++) { int tpc; string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(parts[i + 1], out tpc); nestedTypes[i] = new NestedTypeName(name, tpc); } } } /// /// Gets the top-level type name. /// public TopLevelTypeName TopLevelTypeName { get { return topLevelType; } } /// /// Gets whether this is a nested type. /// public bool IsNested { get { return nestedTypes != null; } } /// /// Gets the nesting level. /// public int NestingLevel { get { return nestedTypes != null ? nestedTypes.Length : 0; } } /// /// Gets the name of the type. /// For nested types, this is the name of the innermost type. /// public string Name { get { if (nestedTypes != null) return nestedTypes[nestedTypes.Length - 1].Name; else return topLevelType.Name; } } public string ReflectionName { get { if (nestedTypes == null) return topLevelType.ReflectionName; StringBuilder b = new StringBuilder(topLevelType.ReflectionName); foreach (NestedTypeName nt in nestedTypes) { b.Append('+'); b.Append(nt.Name); if (nt.AdditionalTypeParameterCount > 0) { b.Append('`'); b.Append(nt.AdditionalTypeParameterCount); } } return b.ToString(); } } public string FullName { get { if (nestedTypes == null) return topLevelType.Namespace + "." + topLevelType.Name; StringBuilder b = new StringBuilder(topLevelType.Namespace); b.Append('.'); b.Append(topLevelType.Name); foreach (NestedTypeName nt in nestedTypes) { b.Append('.'); b.Append(nt.Name); } return b.ToString(); } } /// /// Gets the total type parameter count. /// public int TypeParameterCount { get { int tpc = topLevelType.TypeParameterCount; if (nestedTypes != null) { foreach (var nt in nestedTypes) { tpc += nt.AdditionalTypeParameterCount; } } return tpc; } } /// /// Gets the name of the nested type at the given level. /// public string GetNestedTypeName(int nestingLevel) { if (nestedTypes == null) throw new InvalidOperationException(); return nestedTypes[nestingLevel].Name; } /// /// Gets the number of additional type parameters of the nested type at the given level. /// public int GetNestedTypeAdditionalTypeParameterCount(int nestingLevel) { if (nestedTypes == null) throw new InvalidOperationException(); return nestedTypes[nestingLevel].AdditionalTypeParameterCount; } /// /// Gets the declaring type name. /// /// This is a top-level type name. /// new FullTypeName("NS.A+B+C").GetDeclaringType() will return new FullTypeName("NS.A+B") public FullTypeName GetDeclaringType() { if (nestedTypes == null) throw new InvalidOperationException(); if (nestedTypes.Length == 1) return topLevelType; NestedTypeName[] outerNestedTypeNames = new NestedTypeName[nestedTypes.Length - 1]; Array.Copy(nestedTypes, 0, outerNestedTypeNames, 0, outerNestedTypeNames.Length); return new FullTypeName(topLevelType, outerNestedTypeNames); } /// /// Creates a nested type name. /// /// new FullTypeName("NS.A+B").NestedType("C", 1) will return new FullTypeName("NS.A+B+C`1") public FullTypeName NestedType(string name, int additionalTypeParameterCount) { if (name == null) throw new ArgumentNullException(nameof(name)); var newNestedType = new NestedTypeName(name, additionalTypeParameterCount); if (nestedTypes == null) return new FullTypeName(topLevelType, new[] { newNestedType }); NestedTypeName[] newNestedTypeNames = new NestedTypeName[nestedTypes.Length + 1]; nestedTypes.CopyTo(newNestedTypeNames, 0); newNestedTypeNames[newNestedTypeNames.Length - 1] = newNestedType; return new FullTypeName(topLevelType, newNestedTypeNames); } public static implicit operator FullTypeName(TopLevelTypeName topLevelTypeName) { return new FullTypeName(topLevelTypeName); } public override string ToString() { return this.ReflectionName; } #region Equals and GetHashCode implementation public override bool Equals(object obj) { return obj is FullTypeName && Equals((FullTypeName)obj); } public bool Equals(FullTypeName other) { return FullTypeNameComparer.Ordinal.Equals(this, other); } public override int GetHashCode() { return FullTypeNameComparer.Ordinal.GetHashCode(this); } public static bool operator ==(FullTypeName left, FullTypeName right) { return left.Equals(right); } public static bool operator !=(FullTypeName left, FullTypeName right) { return !left.Equals(right); } #endregion } [Serializable] public sealed class FullTypeNameComparer : IEqualityComparer { public static readonly FullTypeNameComparer Ordinal = new FullTypeNameComparer(StringComparer.Ordinal); public static readonly FullTypeNameComparer OrdinalIgnoreCase = new FullTypeNameComparer(StringComparer.OrdinalIgnoreCase); public readonly StringComparer NameComparer; public FullTypeNameComparer(StringComparer nameComparer) { this.NameComparer = nameComparer; } public bool Equals(FullTypeName x, FullTypeName y) { if (x.NestingLevel != y.NestingLevel) return false; TopLevelTypeName topX = x.TopLevelTypeName; TopLevelTypeName topY = y.TopLevelTypeName; if (topX.TypeParameterCount == topY.TypeParameterCount && NameComparer.Equals(topX.Name, topY.Name) && NameComparer.Equals(topX.Namespace, topY.Namespace)) { for (int i = 0; i < x.NestingLevel; i++) { if (x.GetNestedTypeAdditionalTypeParameterCount(i) != y.GetNestedTypeAdditionalTypeParameterCount(i)) return false; if (!NameComparer.Equals(x.GetNestedTypeName(i), y.GetNestedTypeName(i))) return false; } return true; } return false; } public int GetHashCode(FullTypeName obj) { TopLevelTypeName top = obj.TopLevelTypeName; int hash = NameComparer.GetHashCode(top.Name) ^ NameComparer.GetHashCode(top.Namespace) ^ top.TypeParameterCount; unchecked { for (int i = 0; i < obj.NestingLevel; i++) { hash *= 31; hash += NameComparer.GetHashCode(obj.Name) ^ obj.TypeParameterCount; } } return hash; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { public class FunctionPointerType : AbstractType { public static FunctionPointerType FromSignature(MethodSignature signature, MetadataModule module) { IType returnType = signature.ReturnType; bool returnIsRefReadOnly = false; SignatureCallingConvention callingConvention = signature.Header.CallingConvention; var customCallConvs = ImmutableArray.CreateBuilder(); while (returnType is ModifiedType modReturn) { if (modReturn.Modifier.IsKnownType(KnownAttribute.In)) { returnType = modReturn.ElementType; returnIsRefReadOnly = true; } else if (modReturn.Modifier.Name.StartsWith("CallConv", StringComparison.Ordinal) && modReturn.Modifier.Namespace == "System.Runtime.CompilerServices") { returnType = modReturn.ElementType; if (callingConvention == SignatureCallingConvention.Unmanaged) { switch (modReturn.Modifier.Name) { case "CallConvCdecl": callingConvention = SignatureCallingConvention.CDecl; break; case "CallConvFastcall": callingConvention = SignatureCallingConvention.FastCall; break; case "CallConvStdcall": callingConvention = SignatureCallingConvention.StdCall; break; case "CallConvThiscall": callingConvention = SignatureCallingConvention.ThisCall; break; default: customCallConvs.Add(modReturn.Modifier); break; } } else { customCallConvs.Add(modReturn.Modifier); } } else { break; } } var parameterTypes = ImmutableArray.CreateBuilder(signature.ParameterTypes.Length); var parameterReferenceKinds = ImmutableArray.CreateBuilder(signature.ParameterTypes.Length); foreach (var p in signature.ParameterTypes) { IType paramType = p; ReferenceKind kind = ReferenceKind.None; if (p is ModifiedType mod) { if (mod.Modifier.IsKnownType(KnownAttribute.In)) { kind = ReferenceKind.In; paramType = mod.ElementType; } else if (mod.Modifier.IsKnownType(KnownAttribute.Out)) { kind = ReferenceKind.Out; paramType = mod.ElementType; } else if (mod.Modifier.IsKnownType(KnownAttribute.RequiresLocation)) { kind = ReferenceKind.RefReadOnly; paramType = mod.ElementType; } } if (paramType.Kind == TypeKind.ByReference) { if (kind == ReferenceKind.None) kind = ReferenceKind.Ref; } else { kind = ReferenceKind.None; } parameterTypes.Add(paramType); parameterReferenceKinds.Add(kind); } return new FunctionPointerType( module, callingConvention, customCallConvs.ToImmutable(), returnType, returnIsRefReadOnly, parameterTypes.MoveToImmutable(), parameterReferenceKinds.MoveToImmutable()); } private readonly MetadataModule module; public readonly SignatureCallingConvention CallingConvention; public readonly ImmutableArray CustomCallingConventions; public readonly IType ReturnType; public readonly bool ReturnIsRefReadOnly; public readonly ImmutableArray ParameterTypes; public readonly ImmutableArray ParameterReferenceKinds; public FunctionPointerType(MetadataModule module, SignatureCallingConvention callingConvention, ImmutableArray customCallingConventions, IType returnType, bool returnIsRefReadOnly, ImmutableArray parameterTypes, ImmutableArray parameterReferenceKinds) { this.module = module; this.CallingConvention = callingConvention; this.CustomCallingConventions = customCallingConventions; this.ReturnType = returnType; this.ReturnIsRefReadOnly = returnIsRefReadOnly; this.ParameterTypes = parameterTypes; this.ParameterReferenceKinds = parameterReferenceKinds; Debug.Assert(parameterTypes.Length == parameterReferenceKinds.Length); } public override string Name => "delegate*"; public override bool? IsReferenceType => false; public override TypeKind Kind => ((module.TypeSystemOptions & TypeSystemOptions.FunctionPointers) != 0) ? TypeKind.FunctionPointer : TypeKind.Struct; public override ITypeDefinition GetDefinition() { if ((module.TypeSystemOptions & TypeSystemOptions.FunctionPointers) != 0) { return null; } else { // If FunctionPointers are not enabled in the TS, we still use FunctionPointerType instances; // but have them act as if they were aliases for UIntPtr. return module.Compilation.FindType(KnownTypeCode.UIntPtr).GetDefinition(); } } public override ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return GetDefinition(); } public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitFunctionPointerType(this); } public override IType VisitChildren(TypeVisitor visitor) { IType r = ReturnType.AcceptVisitor(visitor); // Keep ta == null as long as no elements changed, allocate the array only if necessary. IType[] pt = (r != ReturnType) ? new IType[ParameterTypes.Length] : null; for (int i = 0; i < ParameterTypes.Length; i++) { IType p = ParameterTypes[i].AcceptVisitor(visitor); if (p == null) throw new NullReferenceException("TypeVisitor.Visit-method returned null"); if (pt == null && p != ParameterTypes[i]) { // we found a difference, so we need to allocate the array pt = new IType[ParameterTypes.Length]; for (int j = 0; j < i; j++) { pt[j] = ParameterTypes[j]; } } if (pt != null) pt[i] = p; } if (pt == null) return this; else return new FunctionPointerType( module, CallingConvention, CustomCallingConventions, r, ReturnIsRefReadOnly, pt.ToImmutableArray(), ParameterReferenceKinds); } public override bool Equals(IType other) { return other is FunctionPointerType fpt && CallingConvention == fpt.CallingConvention && CustomCallingConventions.SequenceEqual(fpt.CustomCallingConventions) && ReturnType.Equals(fpt.ReturnType) && ReturnIsRefReadOnly == fpt.ReturnIsRefReadOnly && ParameterTypes.SequenceEqual(fpt.ParameterTypes) && ParameterReferenceKinds.SequenceEqual(fpt.ParameterReferenceKinds); } public override int GetHashCode() { unchecked { int hash = ReturnType.GetHashCode() ^ CallingConvention.GetHashCode(); foreach (var (p, k) in ParameterTypes.Zip(ParameterReferenceKinds)) { hash ^= p.GetHashCode() ^ k.GetHashCode(); hash *= 8310859; } return hash; } } internal IType WithSignature(IType returnType, ImmutableArray parameterTypes) { return new FunctionPointerType(this.module, this.CallingConvention, this.CustomCallingConventions, returnType, this.ReturnIsRefReadOnly, parameterTypes, this.ParameterReferenceKinds); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/GenericContext.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { public readonly struct GenericContext { public readonly IReadOnlyList ClassTypeParameters; public readonly IReadOnlyList MethodTypeParameters; public GenericContext(IReadOnlyList classTypeParameters) { this.ClassTypeParameters = classTypeParameters; this.MethodTypeParameters = null; } public GenericContext(IReadOnlyList classTypeParameters, IReadOnlyList methodTypeParameters) { this.ClassTypeParameters = classTypeParameters; this.MethodTypeParameters = methodTypeParameters; } internal GenericContext(ITypeResolveContext context) { this.ClassTypeParameters = context.CurrentTypeDefinition?.TypeParameters; this.MethodTypeParameters = (context.CurrentMember as IMethod)?.TypeParameters; } internal GenericContext(IEntity context) { if (context is ITypeDefinition td) { this.ClassTypeParameters = td.TypeParameters; this.MethodTypeParameters = null; } else { this.ClassTypeParameters = context.DeclaringTypeDefinition?.TypeParameters; this.MethodTypeParameters = (context as IMethod)?.TypeParameters; } } public ITypeParameter GetClassTypeParameter(int index) { if (index < ClassTypeParameters?.Count) return ClassTypeParameters[index]; else return DummyTypeParameter.GetClassTypeParameter(index); } public ITypeParameter GetMethodTypeParameter(int index) { if (index < MethodTypeParameters?.Count) return MethodTypeParameters[index]; else return DummyTypeParameter.GetMethodTypeParameter(index); } internal TypeParameterSubstitution ToSubstitution() { // The TS prefers 'null' over empty lists in substitutions, and we need our substitution // to compare equal to the ones created by the TS. return new TypeParameterSubstitution( classTypeArguments: ClassTypeParameters?.Count > 0 ? ClassTypeParameters : null, methodTypeArguments: MethodTypeParameters?.Count > 0 ? MethodTypeParameters : null ); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IAssembly.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Interface used to help with construction of the type system. /// /// /// The type system is an immutable cyclic data structure: /// the compilation (ICompilation) has references to all modules, /// and each module has a reference back to the compilation. /// /// Module references are used to solve this cyclic dependency: /// The compilation constructor accepts module references, /// and only the IModuleReference.Resolve() function can observe a /// partially-constructed compilation; but not any user code. /// public interface IModuleReference { /// /// Resolves this metadata module. /// IModule? Resolve(ITypeResolveContext context); } /// /// Represents a metadata module. /// public interface IModule : ISymbol, ICompilationProvider { /// /// Gets the underlying metadata file. May return null, if the module was not created from a file. /// MetadataFile? MetadataFile { get; } /// /// Gets whether this assembly is the main assembly of the compilation. /// bool IsMainModule { get; } /// /// Gets the assembly name (short name). /// string AssemblyName { get; } /// /// Gets the assembly version. /// Version AssemblyVersion { get; } /// /// Gets the full assembly name (including public key token etc.) /// string FullAssemblyName { get; } /// /// Gets all assembly attributes. /// IEnumerable GetAssemblyAttributes(); /// /// Gets all module attributes. /// IEnumerable GetModuleAttributes(); /// /// Gets whether the internals of this assembly are visible in the specified assembly. /// bool InternalsVisibleTo(IModule module); /// /// Gets the root namespace for this module. /// /// /// This always is the namespace without a name - it's unrelated to the 'root namespace' project setting. /// It contains only subnamespaces and types defined in this module -- use ICompilation.RootNamespace /// to get the combined view of all referenced assemblies. /// INamespace RootNamespace { get; } /// /// Gets the type definition for a top-level type. /// /// This method uses ordinal name comparison, not the compilation's name comparer. ITypeDefinition? GetTypeDefinition(TopLevelTypeName topLevelTypeName); /// /// Gets all non-nested types in the assembly. /// IEnumerable TopLevelTypeDefinitions { get; } /// /// Gets all types in the assembly, including nested types. /// IEnumerable TypeDefinitions { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IAttribute.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Immutable; using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents an attribute. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] public interface IAttribute { /// /// Gets the type of the attribute. /// IType AttributeType { get; } /// /// Gets the constructor being used. /// This property may return null if no matching constructor was found. /// IMethod? Constructor { get; } /// /// Gets whether there were errors decoding the attribute. /// bool HasDecodeErrors { get; } /// /// Gets the positional arguments. /// ImmutableArray> FixedArguments { get; } /// /// Gets the named arguments passed to the attribute. /// ImmutableArray> NamedArguments { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ICodeContext.cs ================================================ // Copyright (c) 2014 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { public interface ICodeContext : ITypeResolveContext { /// /// Gets all currently visible local variables and lambda parameters. /// Does not include method parameters. /// IEnumerable LocalVariables { get; } /// /// Gets whether the context is within a lambda expression or anonymous method. /// bool IsWithinLambdaExpression { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ICompilation.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { public interface ICompilation { /// /// Gets the primary module. /// This is the module being (de)compiled; all other modules in the compilation are the other assemblies/modules /// referenced by the main module. /// IModule MainModule { get; } /// /// Gets the list of all modules in the compilation. /// /// /// This main module is the first entry in the list. /// IReadOnlyList Modules { get; } /// /// Gets the referenced modules. /// This list does not include the main module. /// IReadOnlyList ReferencedModules { get; } /// /// Gets the root namespace of this compilation. /// This is a merged version of the root namespaces of all assemblies. /// /// /// This always is the namespace without a name - it's unrelated to the 'root namespace' project setting. /// INamespace RootNamespace { get; } /// /// Gets the root namespace for a given extern alias. /// /// /// If is null or an empty string, this method /// returns the global root namespace. /// If no alias with the specified name exists, this method returns null. /// INamespace? GetNamespaceForExternAlias(string? alias); IType FindType(KnownTypeCode typeCode); /// /// Gets the name comparer for the language being compiled. /// This is the string comparer used for the INamespace.GetTypeDefinition method. /// StringComparer NameComparer { get; } CacheManager CacheManager { get; } TypeSystemOptions TypeSystemOptions { get; } } public interface ICompilationProvider { /// /// Gets the parent compilation. /// This property never returns null. /// ICompilation Compilation { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Main interface for the decompiler type system. /// /// The MetadataModule class allows decoding/resolving metadata tokens into type system entities. /// public interface IDecompilerTypeSystem : ICompilation { new MetadataModule MainModule { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IEntity.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a resolved entity. /// public interface IEntity : ISymbol, ICompilationProvider, INamedElement { /// /// Gets the metadata token for this entity. /// /// /// The token is only valid within the context of the assembly defining this entity. /// Token may be 0 if this is a generated member. /// Note: specialized members will return the token of the member definition. /// System.Reflection.Metadata.EntityHandle MetadataToken { get; } /// /// Gets the short name of the entity. /// new string Name { get; } /// /// Gets the declaring class. /// For members, this is the class that contains the member. /// For nested classes, this is the outer class. For top-level entities, this property returns null. /// ITypeDefinition? DeclaringTypeDefinition { get; } /// /// Gets/Sets the declaring type (incl. type arguments, if any). /// This property will return null for top-level entities. /// If this is not a specialized member, the value returned is equal to . /// IType? DeclaringType { get; } /// /// The module in which this entity is defined. /// May return null, if the IEntity was not created from a module. /// IModule? ParentModule { get; } /// /// Gets the attributes on this entity. /// Does not include inherited attributes. /// IEnumerable GetAttributes(); bool HasAttribute(KnownAttribute attribute); IAttribute? GetAttribute(KnownAttribute attribute); /// /// Gets the accessibility of this entity. /// Accessibility Accessibility { get; } /// /// Gets whether this entity is static. /// Returns true if either the 'static' or the 'const' modifier is set. /// bool IsStatic { get; } /// /// Returns whether this entity is abstract. /// /// Static classes also count as abstract classes. bool IsAbstract { get; } /// /// Returns whether this entity is sealed. /// /// Static classes also count as sealed classes. bool IsSealed { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IEvent.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Diagnostics.CodeAnalysis; namespace ICSharpCode.Decompiler.TypeSystem { public interface IEvent : IMember { [MemberNotNullWhen(true, nameof(AddAccessor))] bool CanAdd { get; } [MemberNotNullWhen(true, nameof(RemoveAccessor))] bool CanRemove { get; } [MemberNotNullWhen(true, nameof(InvokeAccessor))] bool CanInvoke { get; } IMethod? AddAccessor { get; } IMethod? RemoveAccessor { get; } IMethod? InvokeAccessor { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IField.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a field or constant. /// public interface IField : IMember, IVariable { /// /// Gets the name of the field. /// new string Name { get; } // solve ambiguity between IMember.Name and IVariable.Name /// /// Gets whether this field is readonly. /// bool IsReadOnly { get; } /// /// Gets whether the field type is 'ref readonly'. /// bool ReturnTypeIsRefReadOnly { get; } /// /// Gets whether this field is volatile. /// bool IsVolatile { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IFreezable.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { public interface IFreezable { /// /// Gets if this instance is frozen. Frozen instances are immutable and thus thread-safe. /// bool IsFrozen { get; } /// /// Freezes this instance. /// void Freeze(); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IInterningProvider.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Provider used for interning. /// /// /// A simple IInterningProvider implementation could use 3 dictionaries: /// 1. using value equality comparer (for certain types known to implement value equality, e.g. string and IType) /// 2. using comparer that calls into ISupportsInterning (for types implementing ISupportsInterning) /// 3. list comparer (for InternList method) /// /// On the first Intern()-call, the provider tells the object to prepare for interning (ISupportsInterning.PrepareForInterning) /// and stores it into a dictionary. On further Intern() calls, the original object is returned for all equal objects. /// This allows reducing the memory usage by using a single object instance where possible. /// /// Interning provider implementations could also use the interning logic for different purposes: /// for example, it could be used to determine which objects are used jointly between multiple type definitions /// and which are used only within a single type definition. Then a persistent file format could be organized so /// that shared objects are loaded only once, yet non-shared objects get loaded lazily together with the class. /// public abstract class InterningProvider { public static readonly InterningProvider Dummy = new DummyInterningProvider(); /// /// Interns the specified object. /// /// If the object is freezable, it will be frozen. /// [return: NotNullIfNotNull("obj")] public abstract ISupportsInterning? Intern(ISupportsInterning? obj); /// /// Interns the specified object. /// /// If the object is freezable, it will be frozen. /// [return: NotNullIfNotNull("obj")] public T? Intern(T? obj) where T : class, ISupportsInterning { ISupportsInterning? input = obj; return (T?)Intern(input); } /// /// Interns the specified string. /// [return: NotNullIfNotNull("text")] public abstract string? Intern(string? text); /// /// Inters a boxed value type. /// [return: NotNullIfNotNull("obj")] public abstract object? InternValue(object? obj); /// /// Interns the given list. Uses reference equality to compare the list elements. /// [return: NotNullIfNotNull("list")] public abstract IList? InternList(IList? list) where T : class; sealed class DummyInterningProvider : InterningProvider { public override ISupportsInterning? Intern(ISupportsInterning? obj) { return obj; } public override string? Intern(string? text) { return text; } public override object? InternValue(object? obj) { return obj; } public override IList? InternList(IList? list) { return list; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IMember.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { public interface IMemberReference { /// /// Gets the declaring type reference for the member. /// ITypeReference DeclaringTypeReference { get; } /// /// Resolves the member. /// /// /// Context to use for resolving this member reference. /// Which kind of context is required depends on the which kind of member reference this is; /// please consult the documentation of the method that was used to create this member reference, /// or that of the class implementing this method. /// /// /// Returns the resolved member, or null if the member could not be found. /// IMember? Resolve(ITypeResolveContext context); } /// /// Method/field/property/event. /// public interface IMember : IEntity { /// /// Gets the original member definition for this member. /// Returns this if this is not a specialized member. /// Specialized members are the result of overload resolution with type substitution. /// IMember MemberDefinition { get; } /// /// Gets the return type of this member. /// This property never returns null. /// IType ReturnType { get; } /// /// Gets/Sets the declaring type (incl. type arguments, if any). /// If this is not a specialized member, the value returned is equal to . /// new IType DeclaringType { get; } /// /// Gets the interface members explicitly implemented by this member. /// /// /// For methods, equivalent to ( /// from impl in DeclaringTypeDefinition.GetExplicitInterfaceImplementations() /// where impl.Implementation == this /// select impl.InterfaceMethod /// ), /// but may be more efficient than searching the whole list. /// /// Note that it is possible for a class to implement an interface using members in a /// base class unrelated to that interface: /// class BaseClass { public void Dispose() {} } /// class C : BaseClass, IDisposable { } /// In this case, the interface member will not show up in (BaseClass.Dispose).ImplementedInterfaceMembers, /// so use (C).GetInterfaceImplementations() instead to handle this case. /// IEnumerable ExplicitlyImplementedInterfaceMembers { get; } /// /// Gets whether this member is explicitly implementing an interface. /// bool IsExplicitInterfaceImplementation { get; } /// /// Gets if the member is virtual. Is true only if the "virtual" modifier was used, but non-virtual /// members can be overridden, too; if they are abstract or overriding a method. /// bool IsVirtual { get; } /// /// Gets whether this member is overriding another member. /// bool IsOverride { get; } /// /// Gets if the member can be overridden. Returns true when the member is "abstract", "virtual" or "override" but not "sealed". /// bool IsOverridable { get; } /// /// Gets the substitution belonging to this specialized member. /// Returns TypeParameterSubstitution.Identity for not specialized members. /// TypeParameterSubstitution Substitution { get; } /// /// Specializes this member with the given substitution. /// If this member is already specialized, the new substitution is composed with the existing substition. /// IMember Specialize(TypeParameterSubstitution substitution); /// /// Gets whether the members are considered equal when applying the specified type normalization. /// bool Equals(IMember? obj, TypeVisitor typeNormalization); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IMethod.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a method, constructor, destructor or operator. /// public interface IMethod : IParameterizedMember { /// /// Gets the attributes associated with the return type. (e.g. [return: MarshalAs(...)]) /// /// /// Does not include inherited attributes. /// IEnumerable GetReturnTypeAttributes(); /// /// Gets whether the return type is 'ref readonly'. /// bool ReturnTypeIsRefReadOnly { get; } /// /// Gets whether this method may only be called on fresh instances. /// Used with C# 9 `init;` property setters. /// bool IsInitOnly { get; } /// /// Gets whether the method accepts the 'this' reference as ref readonly. /// This can be either because the method is C# 8.0 'readonly', or because it is within a C# 7.2 'readonly struct' /// bool ThisIsRefReadOnly { get; } /// /// Gets the type parameters of this method; or an empty list if the method is not generic. /// IReadOnlyList TypeParameters { get; } /// /// Gets the type arguments passed to this method. /// If the method is generic but not parameterized yet, this property returns the type parameters, /// as if the method was parameterized with its own type arguments (void M<T>() { M<T>(); }). /// IReadOnlyList TypeArguments { get; } bool IsExtensionMethod { get; } bool IsLocalFunction { get; } bool IsConstructor { get; } bool IsDestructor { get; } bool IsOperator { get; } /// /// Gets whether the method has a body. /// This property returns false for abstract or extern methods, /// or for partial methods without implementation. /// bool HasBody { get; } /// /// Gets whether the method is a property/event accessor. /// [MemberNotNullWhen(true, nameof(AccessorOwner))] bool IsAccessor { get; } /// /// If this method is an accessor, returns the corresponding property/event. /// Otherwise, returns null. /// IMember? AccessorOwner { get; } /// /// Gets the kind of accessor this is. /// MethodSemanticsAttributes AccessorKind { get; } /// /// If this method is reduced from an extension method or a local function returns the original method, null otherwise. /// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition. /// A local function doesn't contain compiler-generated method parameters at the end. /// IMethod? ReducedFrom { get; } /// /// Specializes this method with the given substitution. /// If this method is already specialized, the new substitution is composed with the existing substition. /// new IMethod Specialize(TypeParameterSubstitution substitution); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/INamedElement.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { public interface INamedElement { /// /// Gets the fully qualified name of the class the return type is pointing to. /// /// /// "System.Int32[]" for int[]
/// "System.Collections.Generic.List" for List<string> /// "System.Environment.SpecialFolder" for Environment.SpecialFolder ///
string FullName { get; } /// /// Gets the short name of the class the return type is pointing to. /// /// /// "Int32[]" for int[]
/// "List" for List<string> /// "SpecialFolder" for Environment.SpecialFolder ///
string Name { get; } /// /// Gets the full reflection name of the element. /// /// /// For types, the reflection name can be parsed back into a IType by using /// . /// /// /// "System.Int32[]" for int[]
/// "System.Int32[][,]" for C# int[,][]
/// "System.Collections.Generic.List`1[[System.String]]" for List<string> /// "System.Environment+SpecialFolder" for Environment.SpecialFolder ///
string ReflectionName { get; } /// /// Gets the full name of the namespace containing this entity. /// string Namespace { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/INamespace.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a resolved namespace. /// public interface INamespace : ISymbol, ICompilationProvider { // No pointer back to unresolved namespace: // multiple unresolved namespaces (from different assemblies) get // merged into one INamespace. /// /// Gets the extern alias for this namespace. /// Returns an empty string for normal namespaces. /// string ExternAlias { get; } /// /// Gets the full name of this namespace. (e.g. "System.Collections") /// string FullName { get; } /// /// Gets the short name of this namespace (e.g. "Collections"). /// new string Name { get; } /// /// Gets the parent namespace. /// Returns null if this is the root namespace. /// INamespace? ParentNamespace { get; } /// /// Gets the child namespaces in this namespace. /// IEnumerable ChildNamespaces { get; } /// /// Gets the types in this namespace. /// IEnumerable Types { get; } /// /// Gets the modules that contribute types to this namespace (or to child namespaces). /// IEnumerable ContributingModules { get; } /// /// Gets a direct child namespace by its short name. /// Returns null when the namespace cannot be found. /// /// /// This method uses the compilation's current string comparer. /// INamespace? GetChildNamespace(string name); /// /// Gets the type with the specified short name and type parameter count. /// Returns null if the type cannot be found. /// /// /// This method uses the compilation's current string comparer. /// ITypeDefinition? GetTypeDefinition(string name, int typeParameterCount); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Should match order in . /// public enum ReferenceKind : byte { None, Out, Ref, In, RefReadOnly, } public struct LifetimeAnnotation { /// /// C# 11 scoped annotation: "scoped ref" (ScopedRefAttribute) /// public bool ScopedRef { #pragma warning disable 618 get { return RefScoped; } set { RefScoped = value; } #pragma warning restore 618 } [Obsolete("Use ScopedRef property instead of directly accessing this field")] public bool RefScoped; [Obsolete("C# 11 preview: \"ref scoped\" no longer supported")] public bool ValueScoped; } public interface IParameter : IVariable { /// /// Gets the attributes on this parameter. /// IEnumerable GetAttributes(); /// /// Gets the reference kind of this parameter. /// ReferenceKind ReferenceKind { get; } /// /// C# 11 scoped annotation. /// LifetimeAnnotation Lifetime { get; } /// /// Gets whether this parameter is a C# 'params' parameter. /// bool IsParams { get; } /// /// Gets whether this parameter is optional. /// The default value is given by the function. /// bool IsOptional { get; } /// /// Gets whether this parameter has a constant value when presented in method signature. /// /// /// This can only be true if the parameter is optional, and it's true for most /// optional parameters. However it is possible to compile a parameter without a default value, /// and some parameters handle their default values in an special way. /// /// For example, does not use normal constants, /// so when is false /// we expose DecimalConstantAttribute directly instead of a constant value. /// /// On the call sites, though, we can still use the value inferred from the attribute. /// bool HasConstantValueInSignature { get; } /// /// Gets the owner of this parameter. /// May return null; for example when parameters belong to lambdas or anonymous methods. /// IParameterizedMember? Owner { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IParameterizedMember.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a method or property. /// public interface IParameterizedMember : IMember { IReadOnlyList Parameters { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IProperty.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Diagnostics.CodeAnalysis; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a property or indexer. /// public interface IProperty : IParameterizedMember { [MemberNotNullWhen(true, nameof(Getter))] bool CanGet { get; } [MemberNotNullWhen(true, nameof(Setter))] bool CanSet { get; } IMethod? Getter { get; } IMethod? Setter { get; } bool IsIndexer { get; } /// /// Gets whether the return type is 'ref readonly'. /// bool ReturnTypeIsRefReadOnly { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ISupportsInterning.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Interface for TypeSystem objects that support interning. /// See for more information. /// public interface ISupportsInterning { /// /// Gets a hash code for interning. /// int GetHashCodeForInterning(); /// /// Equality test for interning. /// bool EqualsForInterning(ISupportsInterning other); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ISymbol.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { public enum SymbolKind : byte { None, /// Module, /// TypeDefinition, /// Field, /// /// The symbol is a property, but not an indexer. /// /// Property, /// /// The symbol is an indexer, not a regular property. /// /// Indexer, /// Event, /// /// The symbol is a method which is not an operator/constructor/destructor or accessor. /// /// Method, /// /// The symbol is a user-defined operator. /// /// Operator, /// Constructor, /// Destructor, /// /// The accessor method for a property getter/setter or event add/remove. /// /// Accessor, /// Namespace, /// /// The symbol is a variable, but not a parameter. /// /// Variable, /// Parameter, /// TypeParameter, /// /// Constraint on a type parameter. /// Constraint, /// /// Return type. Not actually an ISymbol implementation; but can appear as attribut target. /// ReturnType, } /// /// Interface for type system symbols. /// public interface ISymbol { /// /// This property returns an enum specifying which kind of symbol this is /// (which derived interfaces of ISymbol are implemented) /// SymbolKind SymbolKind { get; } /// /// Gets the short name of the symbol. /// string Name { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IType.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// This interface represents a resolved type in the type system. /// /// /// /// A type is potentially /// - a type definition (, i.e. a class, struct, interface, delegate, or built-in primitive type) /// - a parameterized type (, e.g. List<int>) /// - a type parameter (, e.g. T) /// - an array () /// - a pointer () /// - a managed reference () /// - one of the special types (, , /// , ) /// /// The property can be used to switch on the kind of a type. /// /// /// IType uses the null object pattern: serves as the null object. /// Methods or properties returning IType never return null unless documented otherwise. /// /// /// Types should be compared for equality using the method. /// Identical types do not necessarily use the same object reference. /// /// public interface IType : INamedElement, IEquatable { /// /// Gets the type kind. /// TypeKind Kind { get; } /// /// Gets whether the type is a reference type or value type. /// /// /// true, if the type is a reference type. /// false, if the type is a value type. /// null, if the type is not known (e.g. unconstrained generic type parameter or type not found) /// bool? IsReferenceType { get; } /// /// Gets whether this type is "ref-like": a ByReferenceType or "ref struct". /// bool IsByRefLike { get; } /// /// Gets the nullability annotation on this type. /// Nullability Nullability { get; } /// /// Creates a new type that is a copy of this type, with the changed nullability annotation. /// IType ChangeNullability(Nullability newNullability); /// /// Gets the underlying type definition. /// Can return null for types which do not have a type definition (for example arrays, pointers, type parameters). /// ITypeDefinition? GetDefinition(); /// /// Gets the underlying type definition or UnkownType, if unknown. /// Can return null for types which do not have a type definition (for example arrays, pointers, type parameters). /// ITypeDefinitionOrUnknown? GetDefinitionOrUnknown(); /// /// Gets the parent type, if this is a nested type. /// Returns null for top-level types. /// IType? DeclaringType { get; } /// /// Gets the number of type parameters. /// int TypeParameterCount { get; } /// /// Gets the type parameters. /// Returns an empty list if this type is not generic. /// IReadOnlyList TypeParameters { get; } /// /// Gets the type arguments passed to this type. /// If this type is a generic type definition that is not parameterized, this property returns the type parameters, /// as if the type was parameterized with its own type arguments (class C<T> { C<T> field; }). /// IReadOnlyList TypeArguments { get; } /// /// Calls ITypeVisitor.Visit for this type. /// /// The return value of the ITypeVisitor.Visit call IType AcceptVisitor(TypeVisitor visitor); /// /// Calls ITypeVisitor.Visit for all children of this type, and reconstructs this type with the children based /// on the return values of the visit calls. /// /// A copy of this type, with all children replaced by the return value of the corresponding visitor call. /// If the visitor returned the original types for all children (or if there are no children), returns this. /// IType VisitChildren(TypeVisitor visitor); /// /// Gets the direct base types. /// /// Returns the direct base types including interfaces IEnumerable DirectBaseTypes { get; } /// /// Gets a type visitor that performs the substitution of class type parameters with the type arguments /// of this parameterized type. /// Returns TypeParameterSubstitution.Identity if the type is not parametrized. /// TypeParameterSubstitution GetSubstitution(); /// /// Gets inner classes (including inherited inner classes). /// /// The filter used to select which types to return. /// The filter is tested on the original type definitions (before parameterization). /// Specified additional options for the GetMembers() operation. /// /// /// If the nested type is generic, this method will return a parameterized type, /// where the additional type parameters are set to . /// /// /// Type parameters belonging to the outer class will have the value copied from the outer type /// if it is a parameterized type. Otherwise, those existing type parameters will be self-parameterized, /// and thus 'leaked' to the caller in the same way the GetMembers() method does not specialize members /// from an and 'leaks' type parameters in member signatures. /// /// /// /// /// class Base<T> { /// class Nested<X> {} /// } /// class Derived<A, B> : Base<B> {} /// /// Derived[string,int].GetNestedTypes() = { Base`1+Nested`1[int, unbound] } /// Derived.GetNestedTypes() = { Base`1+Nested`1[`1, unbound] } /// Base[`1].GetNestedTypes() = { Base`1+Nested`1[`1, unbound] } /// Base.GetNestedTypes() = { Base`1+Nested`1[`0, unbound] } /// /// IEnumerable GetNestedTypes(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); // Note that we cannot 'leak' the additional type parameter as we leak the normal type parameters, because // the index might collide. For example, // class Base { class Nested {} } // class Derived : Base { } // // Derived.GetNestedTypes() = Base+Nested // Derived.GetNestedTypes() = Base+Nested<`1, > // Here `1 refers to B, and there's no way to return X as it would collide with B. /// /// Gets inner classes (including inherited inner classes) /// that have typeArguments.Count additional type parameters. /// /// The type arguments passed to the inner class /// The filter used to select which types to return. /// The filter is tested on the original type definitions (before parameterization). /// Specified additional options for the GetMembers() operation. /// /// Type parameters belonging to the outer class will have the value copied from the outer type /// if it is a parameterized type. Otherwise, those existing type parameters will be self-parameterized, /// and thus 'leaked' to the caller in the same way the GetMembers() method does not specialize members /// from an and 'leaks' type parameters in member signatures. /// IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all instance constructors for this type. /// /// The filter used to select which constructors to return. /// The filter is tested on the original method definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// The result does not include static constructors. /// Constructors in base classes are not returned by default, as GetMemberOptions.IgnoreInheritedMembers is the default value. /// /// For methods on parameterized types, type substitution will be performed on the method signature, /// and the appropriate will be returned. /// /// IEnumerable GetConstructors(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers); /// /// Gets all methods that can be called on this type. /// /// The filter used to select which methods to return. /// The filter is tested on the original method definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// /// The result does not include constructors or accessors. /// /// /// For methods on parameterized types, type substitution will be performed on the method signature, /// and the appropriate will be returned. /// /// /// If the method being returned is generic, and this type is a parameterized type where the type /// arguments involve another method's type parameters, the resulting specialized signature /// will be ambiguous as to which method a type parameter belongs to. /// For example, "List[[``0]].GetMethods()" will return "ConvertAll(Converter`2[[``0, ``0]])". /// /// If possible, use the other GetMethods() overload to supply type arguments to the method, /// so that both class and method type parameter can be substituted at the same time, so that /// the ambiguity can be avoided. /// /// IEnumerable GetMethods(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all generic methods that can be called on this type with the specified type arguments. /// /// The type arguments used for the method call. /// The filter used to select which methods to return. /// The filter is tested on the original method definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// The result does not include constructors or accessors. /// /// Type substitution will be performed on the method signature, creating a /// with the specified type arguments. /// /// /// When the list of type arguments is empty, this method acts like the GetMethods() overload without /// the type arguments parameter - that is, it also returns generic methods, /// and the other overload's remarks about ambiguous signatures apply here as well. /// /// IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all properties that can be called on this type. /// /// The filter used to select which properties to return. /// The filter is tested on the original property definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// For properties on parameterized types, type substitution will be performed on the property signature, /// and the appropriate will be returned. /// IEnumerable GetProperties(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all fields that can be accessed on this type. /// /// The filter used to select which constructors to return. /// The filter is tested on the original field definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// For fields on parameterized types, type substitution will be performed on the field's return type, /// and the appropriate will be returned. /// IEnumerable GetFields(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all events that can be accessed on this type. /// /// The filter used to select which events to return. /// The filter is tested on the original event definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// For fields on parameterized types, type substitution will be performed on the event's return type, /// and the appropriate will be returned. /// IEnumerable GetEvents(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all members that can be called on this type. /// /// The filter used to select which members to return. /// The filter is tested on the original member definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// /// The resulting list is the union of GetFields(), GetProperties(), GetMethods() and GetEvents(). /// It does not include constructors. /// For parameterized types, type substitution will be performed. /// /// /// For generic methods, the remarks about ambiguous signatures from the /// method apply here as well. /// /// IEnumerable GetMembers(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); /// /// Gets all accessors belonging to properties or events on this type. /// /// The filter used to select which members to return. /// The filter is tested on the original member definitions (before specialization). /// Specified additional options for the GetMembers() operation. /// /// Accessors are not returned by GetMembers() or GetMethods(). /// IEnumerable GetAccessors(Predicate? filter = null, GetMemberOptions options = GetMemberOptions.None); } [Flags] public enum GetMemberOptions { /// /// No options specified - this is the default. /// Members will be specialized, and inherited members will be included. /// None = 0x00, /// /// Do not specialize the returned members - directly return the definitions. /// ReturnMemberDefinitions = 0x01, /// /// Do not list inherited members - only list members defined directly on this type. /// IgnoreInheritedMembers = 0x02 } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a class, enum, interface, struct, delegate, record or VB module. /// For partial classes, this represents the whole class. /// public interface ITypeDefinition : ITypeDefinitionOrUnknown, IType, IEntity { ExtensionInfo? ExtensionInfo { get; } IReadOnlyList NestedTypes { get; } IReadOnlyList Members { get; } IEnumerable Fields { get; } IEnumerable Methods { get; } IEnumerable Properties { get; } IEnumerable Events { get; } /// /// Gets the known type code for this type definition. /// KnownTypeCode KnownTypeCode { get; } /// /// For enums: returns the underlying primitive type. /// For all other types: returns . /// IType? EnumUnderlyingType { get; } /// /// For structs: returns whether this is a readonly struct. /// For all other types: returns false. /// bool IsReadOnly { get; } /// /// Gets the short type name as stored in metadata. /// That is, the short type name including the generic arity (`N) appended. /// /// /// "Int32" for int /// "List`1" for List<T> /// "List`1" for List<string> /// string MetadataName { get; } /// /// Gets/Sets the declaring type (incl. type arguments, if any). /// This property will return null for top-level types. /// new IType? DeclaringType { get; } // solves ambiguity between IType.DeclaringType and IEntity.DeclaringType /// /// Gets whether this type contains extension methods or C# 14 extensions. /// /// This property is used to speed up the search for extension members. bool HasExtensions { get; } /// /// The nullability specified in the [NullableContext] attribute on the type. /// This serves as default nullability for members of the type that do not have a [Nullable] attribute. /// Nullability NullableContext { get; } /// /// Gets whether the type has the necessary members to be considered a C# 9 record or C# 10 record struct type. /// bool IsRecord { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ITypeDefinitionOrUnknown.cs ================================================ // Copyright (c) 2022 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a class, enum, interface, struct, delegate, record or unknown type. /// For partial classes, this represents the whole class. /// public interface ITypeDefinitionOrUnknown : IType { /// /// Gets the full name of this type. /// FullTypeName FullTypeName { get; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Type parameter of a generic class/method. /// public interface ITypeParameter : IType, ISymbol { /// /// Get the type of this type parameter's owner. /// /// SymbolKind.TypeDefinition or SymbolKind.Method SymbolKind OwnerType { get; } /// /// Gets the owning method/class. /// This property may return null (for example for the dummy type parameters used by ). /// /// /// For "class Outer<T> { class Inner {} }", /// inner.TypeParameters[0].Owner will be the outer class, because the same /// ITypeParameter instance is used both on Outer`1 and Outer`1+Inner. /// IEntity? Owner { get; } /// /// Gets the index of the type parameter in the type parameter list of the owning method/class. /// int Index { get; } /// /// Gets the name of the type parameter. /// new string Name { get; } /// /// Gets the attributes declared on this type parameter. /// IEnumerable GetAttributes(); /// /// Gets the variance of this type parameter. /// VarianceModifier Variance { get; } /// /// Gets the effective base class of this type parameter. /// IType EffectiveBaseClass { get; } /// /// Gets the effective interface set of this type parameter. /// IReadOnlyCollection EffectiveInterfaceSet { get; } /// /// Gets if the type parameter has the 'new()' constraint. /// bool HasDefaultConstructorConstraint { get; } /// /// Gets if the type parameter has the 'class' constraint. /// bool HasReferenceTypeConstraint { get; } /// /// Gets if the type parameter has the 'struct' or 'unmanaged' constraint. /// bool HasValueTypeConstraint { get; } /// /// Gets if the type parameter has the 'unmanaged' constraint. /// bool HasUnmanagedConstraint { get; } /// /// if the allows ref struct constraint is specified for the type parameter. /// bool AllowsRefLikeType { get; } /// /// Nullability of the reference type constraint. (e.g. "where T : class?"). /// /// Note that the nullability of a use of the type parameter may differ from this. /// E.g. "T? GetNull<T>() where T : class => null;" /// Nullability NullabilityConstraint { get; } IReadOnlyList TypeConstraints { get; } } public readonly struct TypeConstraint { public SymbolKind SymbolKind => SymbolKind.Constraint; public IType Type { get; } public IReadOnlyList Attributes { get; } public TypeConstraint(IType type, IReadOnlyList? attributes = null) { this.Type = type ?? throw new ArgumentNullException(nameof(type)); this.Attributes = attributes ?? EmptyList.Instance; } } /// /// Represents the variance of a type parameter. /// public enum VarianceModifier : byte { /// /// The type parameter is not variant. /// Invariant, /// /// The type parameter is covariant (used in output position). /// Covariant, /// /// The type parameter is contravariant (used in input position). /// Contravariant }; } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ITypeReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a reference to a type. /// Must be resolved before it can be used as type. /// public interface ITypeReference { // Keep this interface simple: I decided against having GetMethods/GetEvents etc. here, // so that the Resolve step is never hidden from the consumer. // I decided against implementing IFreezable here: IUnresolvedTypeDefinition can be used as ITypeReference, // but when freezing the reference, one wouldn't expect the definition to freeze. /// /// Resolves this type reference. /// /// /// Context to use for resolving this type reference. /// Which kind of context is required depends on the which kind of type reference this is; /// please consult the documentation of the method that was used to create this type reference, /// or that of the class implementing this method. /// /// /// Returns the resolved type. /// In case of an error, returns an unknown type (). /// Never returns null. /// IType Resolve(ITypeResolveContext context); } public interface ITypeResolveContext : ICompilationProvider { /// /// Gets the current module. /// This property may return null if this context does not specify any module. /// IModule? CurrentModule { get; } /// /// Gets the current type definition. /// ITypeDefinition? CurrentTypeDefinition { get; } /// /// Gets the current member. /// IMember? CurrentMember { get; } ITypeResolveContext WithCurrentTypeDefinition(ITypeDefinition? typeDefinition); ITypeResolveContext WithCurrentMember(IMember? member); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IVariable.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents a variable (name/type pair). /// public interface IVariable : ISymbol { /// /// Gets the name of the variable. /// new string Name { get; } /// /// Gets the type of the variable. /// IType Type { get; } /// /// Gets whether this variable is a constant (C#-like const). /// bool IsConst { get; } /// /// If this field is a constant, retrieves the value. /// For parameters, this is the default value. /// object? GetConstantValue(bool throwOnInvalidMetadata = false); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractFreezable.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.ObjectModel; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public static class FreezableHelper { public static void ThrowIfFrozen(IFreezable freezable) { if (freezable.IsFrozen) throw new InvalidOperationException("Cannot mutate frozen " + freezable.GetType().Name); } public static IList FreezeListAndElements(IList list) { if (list != null) { foreach (T item in list) Freeze(item); } return FreezeList(list); } public static IList FreezeList(IList list) { if (list == null || list.Count == 0) return EmptyList.Instance; if (list.IsReadOnly) { // If the list is already read-only, return it directly. // This is important, otherwise we might undo the effects of interning. return list; } else { return new ReadOnlyCollection(list.ToArray()); } } public static void Freeze(object item) { IFreezable f = item as IFreezable; if (f != null) f.Freeze(); } public static T FreezeAndReturn(T item) where T : IFreezable { item.Freeze(); return item; } /// /// If the item is not frozen, this method creates and returns a frozen clone. /// If the item is already frozen, it is returned without creating a clone. /// public static T GetFrozenClone(T item) where T : IFreezable, ICloneable { if (!item.IsFrozen) { item = (T)item.Clone(); item.Freeze(); } return item; } } [Serializable] public abstract class AbstractFreezable : IFreezable { bool isFrozen; /// /// Gets if this instance is frozen. Frozen instances are immutable and thus thread-safe. /// public bool IsFrozen { get { return isFrozen; } } /// /// Freezes this instance. /// public void Freeze() { if (!isFrozen) { FreezeInternal(); isFrozen = true; } } protected virtual void FreezeInternal() { } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Default implementation for IType interface. /// [Serializable] public abstract class AbstractType : IType { public virtual string FullName { get { string ns = this.Namespace; string name = this.Name; if (string.IsNullOrEmpty(ns)) { return name; } else { return ns + "." + name; } } } public abstract string Name { get; } public virtual string Namespace { get { return string.Empty; } } public virtual string ReflectionName { get { return this.FullName; } } public abstract bool? IsReferenceType { get; } public virtual bool IsByRefLike => false; public virtual Nullability Nullability => Nullability.Oblivious; public virtual IType ChangeNullability(Nullability nullability) { // Only some types support nullability, in the default implementation // we just ignore the nullability change. return this; } public abstract TypeKind Kind { get; } public virtual int TypeParameterCount { get { return 0; } } public virtual IReadOnlyList TypeParameters { get { return EmptyList.Instance; } } public virtual IReadOnlyList TypeArguments { get { return EmptyList.Instance; } } public virtual IType DeclaringType { get { return null; } } public virtual ITypeDefinition GetDefinition() { return null; } public virtual ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return null; } public virtual IEnumerable DirectBaseTypes { get { return EmptyList.Instance; } } public virtual IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { return EmptyList.Instance; } public virtual IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public virtual IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { IEnumerable members = GetMethods(filter, options); return members .Concat(GetProperties(filter, options)) .Concat(GetFields(filter, options)) .Concat(GetEvents(filter, options)); } public virtual IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return EmptyList.Instance; } public TypeParameterSubstitution GetSubstitution() { return TypeParameterSubstitution.Identity; } public TypeParameterSubstitution GetSubstitution(IReadOnlyList methodTypeArguments) { return TypeParameterSubstitution.Identity; } public override sealed bool Equals(object obj) { return Equals(obj as IType); } public override int GetHashCode() { return base.GetHashCode(); } public virtual bool Equals(IType other) { return this == other; // use reference equality by default } public override string ToString() { return this.ReflectionName; } public virtual IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitOtherType(this); } public virtual IType VisitChildren(TypeVisitor visitor) { return this; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public abstract class AbstractTypeParameter : ITypeParameter, ICompilationProvider { readonly ICompilation compilation; readonly SymbolKind ownerType; readonly IEntity owner; readonly int index; readonly string name; readonly VarianceModifier variance; protected AbstractTypeParameter(IEntity owner, int index, string name, VarianceModifier variance) { if (owner == null) throw new ArgumentNullException(nameof(owner)); this.owner = owner; this.compilation = owner.Compilation; this.ownerType = owner.SymbolKind; this.index = index; this.name = name ?? ((this.OwnerType == SymbolKind.Method ? "!!" : "!") + index.ToString(CultureInfo.InvariantCulture)); this.variance = variance; } protected AbstractTypeParameter(ICompilation compilation, SymbolKind ownerType, int index, string name, VarianceModifier variance) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.ownerType = ownerType; this.index = index; this.name = name ?? ((this.OwnerType == SymbolKind.Method ? "!!" : "!") + index.ToString(CultureInfo.InvariantCulture)); this.variance = variance; } SymbolKind ISymbol.SymbolKind { get { return SymbolKind.TypeParameter; } } public SymbolKind OwnerType { get { return ownerType; } } public IEntity Owner { get { return owner; } } public int Index { get { return index; } } public abstract IEnumerable GetAttributes(); public VarianceModifier Variance { get { return variance; } } public ICompilation Compilation { get { return compilation; } } volatile IType effectiveBaseClass; public IType EffectiveBaseClass { get { if (effectiveBaseClass == null) { // protect against cyclic type parameters using (var busyLock = BusyManager.Enter(this)) { if (!busyLock.Success) return SpecialType.UnknownType; // don't cache this error effectiveBaseClass = CalculateEffectiveBaseClass(); } } return effectiveBaseClass; } } IType CalculateEffectiveBaseClass() { if (HasValueTypeConstraint) return this.Compilation.FindType(KnownTypeCode.ValueType); List classTypeConstraints = new List(); foreach (IType constraint in this.DirectBaseTypes) { if (constraint.Kind == TypeKind.Class) { classTypeConstraints.Add(constraint); } else if (constraint.Kind == TypeKind.TypeParameter) { IType baseClass = ((ITypeParameter)constraint).EffectiveBaseClass; if (baseClass.Kind == TypeKind.Class) classTypeConstraints.Add(baseClass); } } if (classTypeConstraints.Count == 0) return this.Compilation.FindType(KnownTypeCode.Object); // Find the derived-most type in the resulting set: IType result = classTypeConstraints[0]; for (int i = 1; i < classTypeConstraints.Count; i++) { if (classTypeConstraints[i].GetDefinition().IsDerivedFrom(result.GetDefinition())) result = classTypeConstraints[i]; } return result; } IReadOnlyCollection effectiveInterfaceSet; public IReadOnlyCollection EffectiveInterfaceSet { get { var result = LazyInit.VolatileRead(ref effectiveInterfaceSet); if (result != null) { return result; } else { // protect against cyclic type parameters using (var busyLock = BusyManager.Enter(this)) { if (!busyLock.Success) return EmptyList.Instance; // don't cache this error return LazyInit.GetOrSet(ref effectiveInterfaceSet, CalculateEffectiveInterfaceSet()); } } } } IReadOnlyCollection CalculateEffectiveInterfaceSet() { HashSet result = new HashSet(); foreach (IType constraint in this.DirectBaseTypes) { if (constraint.Kind == TypeKind.Interface) { result.Add(constraint); } else if (constraint.Kind == TypeKind.TypeParameter) { result.UnionWith(((ITypeParameter)constraint).EffectiveInterfaceSet); } } return result.ToArray(); } public abstract bool HasDefaultConstructorConstraint { get; } public abstract bool HasReferenceTypeConstraint { get; } public abstract bool HasValueTypeConstraint { get; } public abstract bool HasUnmanagedConstraint { get; } public abstract bool AllowsRefLikeType { get; } public abstract Nullability NullabilityConstraint { get; } public TypeKind Kind { get { return TypeKind.TypeParameter; } } public bool? IsReferenceType { get { if (this.HasValueTypeConstraint) return false; if (this.HasReferenceTypeConstraint) return true; // A type parameter is known to be a reference type if it has the reference type constraint // or its effective base class is not object or System.ValueType. IType effectiveBaseClass = this.EffectiveBaseClass; if (effectiveBaseClass.Kind == TypeKind.Class || effectiveBaseClass.Kind == TypeKind.Delegate) { ITypeDefinition effectiveBaseClassDef = effectiveBaseClass.GetDefinition(); if (effectiveBaseClassDef != null) { switch (effectiveBaseClassDef.KnownTypeCode) { case KnownTypeCode.Object: case KnownTypeCode.ValueType: case KnownTypeCode.Enum: return null; } } return true; } else if (effectiveBaseClass.Kind == TypeKind.Struct || effectiveBaseClass.Kind == TypeKind.Enum) { return false; } return null; } } bool IType.IsByRefLike => false; Nullability IType.Nullability => Nullability.Oblivious; public IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious) return this; else return new NullabilityAnnotatedTypeParameter(this, nullability); } IType IType.DeclaringType { get { return null; } } int IType.TypeParameterCount { get { return 0; } } IReadOnlyList IType.TypeParameters { get { return EmptyList.Instance; } } IReadOnlyList IType.TypeArguments { get { return EmptyList.Instance; } } public IEnumerable DirectBaseTypes { get { return TypeConstraints.Select(t => t.Type); } } public abstract IReadOnlyList TypeConstraints { get; } public string Name { get { return name; } } string INamedElement.Namespace { get { return string.Empty; } } string INamedElement.FullName { get { return name; } } public string ReflectionName { get { return (this.OwnerType == SymbolKind.Method ? "``" : "`") + index.ToString(CultureInfo.InvariantCulture); } } ITypeDefinition IType.GetDefinition() { return null; } ITypeDefinitionOrUnknown IType.GetDefinitionOrUnknown() { return null; } public IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTypeParameter(this); } public IType VisitChildren(TypeVisitor visitor) { return this; } IEnumerable IType.GetNestedTypes(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetNestedTypes(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } public IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { if (this.HasDefaultConstructorConstraint || this.HasValueTypeConstraint) { var dummyCtor = FakeMethod.CreateDummyConstructor(compilation, this); if (filter == null || filter(dummyCtor)) { return new[] { dummyCtor }; } } return EmptyList.Instance; } else { return GetMembersHelper.GetConstructors(this, filter, options); } } public IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetMethods(this, FilterNonStatic(filter), options); } public IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetMethods(this, typeArguments, FilterNonStatic(filter), options); } public IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetProperties(this, FilterNonStatic(filter), options); } public IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetFields(this, FilterNonStatic(filter), options); } public IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetEvents(this, FilterNonStatic(filter), options); } public IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetMembers(this, FilterNonStatic(filter), options); } public IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) return EmptyList.Instance; else return GetMembersHelper.GetAccessors(this, FilterNonStatic(filter), options); } TypeParameterSubstitution IType.GetSubstitution() { return TypeParameterSubstitution.Identity; } static Predicate FilterNonStatic(Predicate filter) where T : class, IMember { return member => (!member.IsStatic || member.SymbolKind == SymbolKind.Operator || member.IsVirtual) && (filter == null || filter(member)); } public sealed override bool Equals(object obj) { return Equals(obj as IType); } public override int GetHashCode() { return base.GetHashCode(); } public virtual bool Equals(IType other) { return this == other; // use reference equality for type parameters } public override string ToString() { return this.ReflectionName; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Text; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { readonly struct AttributeListBuilder { readonly MetadataModule module; readonly List attributes; public AttributeListBuilder(MetadataModule module) { Debug.Assert(module != null); this.module = module; this.attributes = new List(); } public AttributeListBuilder(MetadataModule module, int capacity) { Debug.Assert(module != null); this.module = module; this.attributes = new List(capacity); } public void Add(IAttribute attr) { attributes.Add(attr); } /// /// Add a builtin attribute without any arguments. /// public void Add(KnownAttribute type) { // use the assemblies' cache for simple attributes Add(module.MakeAttribute(type)); } /// /// Construct a builtin attribute with a single positional argument of known type. /// public void Add(KnownAttribute type, KnownTypeCode argType, object argValue) { Add(type, ImmutableArray.Create(new CustomAttributeTypedArgument(module.Compilation.FindType(argType), argValue))); } /// /// Construct a builtin attribute with a single positional argument of known type. /// public void Add(KnownAttribute type, TopLevelTypeName argType, object argValue) { Add(type, ImmutableArray.Create(new CustomAttributeTypedArgument(module.Compilation.FindType(argType), argValue))); } /// /// Construct a builtin attribute. /// public void Add(KnownAttribute type, ImmutableArray> fixedArguments) { Add(new DefaultAttribute(module.GetAttributeType(type), fixedArguments, ImmutableArray.Create>())); } #region MarshalAsAttribute (ConvertMarshalInfo) internal void AddMarshalInfo(BlobHandle marshalInfo) { if (marshalInfo.IsNil) return; var metadata = module.metadata; Add(ConvertMarshalInfo(metadata.GetBlobReader(marshalInfo))); } const string InteropServices = "System.Runtime.InteropServices"; IAttribute ConvertMarshalInfo(SRM.BlobReader marshalInfo) { var b = new AttributeBuilder(module, KnownAttribute.MarshalAs); IType unmanagedTypeType = module.Compilation.FindType(new TopLevelTypeName(InteropServices, nameof(UnmanagedType))); int type = marshalInfo.ReadByte(); b.AddFixedArg(unmanagedTypeType, type); int size; switch (type) { case 0x1e: // FixedArray if (!marshalInfo.TryReadCompressedInteger(out size)) size = 0; b.AddNamedArg("SizeConst", KnownTypeCode.Int32, size); if (marshalInfo.RemainingBytes > 0) { type = marshalInfo.ReadByte(); if (type != 0x66) // None b.AddNamedArg("ArraySubType", unmanagedTypeType, type); } break; case 0x1d: // SafeArray if (marshalInfo.RemainingBytes > 0) { VarEnum varType = (VarEnum)marshalInfo.ReadByte(); if (varType != VarEnum.VT_EMPTY) { var varEnumType = new TopLevelTypeName(InteropServices, nameof(VarEnum)); b.AddNamedArg("SafeArraySubType", varEnumType, (int)varType); } } break; case 0x2a: // NATIVE_TYPE_ARRAY if (marshalInfo.RemainingBytes > 0) { type = marshalInfo.ReadByte(); } else { type = 0x66; // Cecil uses NativeType.None as default. } if (type != 0x50) { // Max b.AddNamedArg("ArraySubType", unmanagedTypeType, type); } int sizeParameterIndex = marshalInfo.TryReadCompressedInteger(out int value) ? value : -1; size = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; int sizeParameterMultiplier = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; if (size >= 0) { b.AddNamedArg("SizeConst", KnownTypeCode.Int32, size); } if (sizeParameterMultiplier != 0 && sizeParameterIndex >= 0) { b.AddNamedArg("SizeParamIndex", KnownTypeCode.Int16, (short)sizeParameterIndex); } break; case 0x2c: // CustomMarshaler string guidValue = marshalInfo.ReadSerializedString(); string unmanagedType = marshalInfo.ReadSerializedString(); string managedType = marshalInfo.ReadSerializedString(); string cookie = marshalInfo.ReadSerializedString(); if (managedType != null) { b.AddNamedArg("MarshalType", KnownTypeCode.String, managedType); } if (!string.IsNullOrEmpty(cookie)) { b.AddNamedArg("MarshalCookie", KnownTypeCode.String, cookie); } break; case 0x17: // FixedSysString b.AddNamedArg("SizeConst", KnownTypeCode.Int32, marshalInfo.ReadCompressedInteger()); break; } return b.Build(); } #endregion #region Custom Attributes (ReadAttribute) public void Add(CustomAttributeHandleCollection attributes, SymbolKind target) { var metadata = module.metadata; foreach (var handle in attributes) { var attribute = metadata.GetCustomAttribute(handle); // Attribute types shouldn't be open generic, so we don't need a generic context. var ctor = module.ResolveMethod(attribute.Constructor, new GenericContext()); var type = ctor.DeclaringType; if (IgnoreAttribute(type, target)) { continue; } Add(new CustomAttribute(module, ctor, handle)); } } bool IgnoreAttribute(IType attributeType, SymbolKind target) { if (attributeType.DeclaringType != null || attributeType.TypeParameterCount != 0) return false; return IgnoreAttribute(new TopLevelTypeName(attributeType.Namespace, attributeType.Name), target); } internal bool IgnoreAttribute(TopLevelTypeName attributeType, SymbolKind target) { switch (attributeType.Namespace) { case "System.Runtime.CompilerServices": var options = module.TypeSystemOptions; switch (attributeType.Name) { case "DynamicAttribute": return (options & TypeSystemOptions.Dynamic) != 0; case "NativeIntegerAttribute": return (options & TypeSystemOptions.NativeIntegers) != 0; case "TupleElementNamesAttribute": return (options & TypeSystemOptions.Tuple) != 0; case "ExtensionAttribute": return (options & TypeSystemOptions.ExtensionMethods) != 0; case "DecimalConstantAttribute": return (options & TypeSystemOptions.DecimalConstants) != 0 && (target == SymbolKind.Field || target == SymbolKind.Parameter); case "IsReadOnlyAttribute": switch (target) { case SymbolKind.TypeDefinition: case SymbolKind.Parameter: return (options & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0; case SymbolKind.Method: case SymbolKind.Accessor: return (options & TypeSystemOptions.ReadOnlyMethods) != 0; case SymbolKind.ReturnType: case SymbolKind.Property: case SymbolKind.Indexer: case SymbolKind.Field: return true; // "ref readonly" is currently always active default: return false; } case "IsByRefLikeAttribute": return (options & TypeSystemOptions.RefStructs) != 0 && target == SymbolKind.TypeDefinition; case "IsUnmanagedAttribute": return (options & TypeSystemOptions.UnmanagedConstraints) != 0 && target == SymbolKind.TypeParameter; case "NullableAttribute": return (options & TypeSystemOptions.NullabilityAnnotations) != 0; case "NullableContextAttribute": return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || IsMethodLike(target)); case "ScopedRefAttribute": return (options & TypeSystemOptions.ScopedRef) != 0 && (target == SymbolKind.Parameter); case "RequiresLocationAttribute": return (options & TypeSystemOptions.RefReadOnlyParameters) != 0 && (target == SymbolKind.Parameter); case "ParamCollectionAttribute": return (options & TypeSystemOptions.ParamsCollections) != 0 && (target == SymbolKind.Parameter); default: return false; } case "System": return attributeType.Name == "ParamArrayAttribute" && target == SymbolKind.Parameter; default: return false; } } internal bool HasAttribute(MetadataReader metadata, CustomAttributeHandleCollection customAttributes, KnownAttribute attribute, SymbolKind symbolKind) { Debug.Assert(attribute.IsCustomAttribute()); foreach (var h in customAttributes) { var attr = metadata.GetCustomAttribute(h); if (attr.IsKnownAttribute(metadata, attribute)) { return !IgnoreAttribute(attribute.GetTypeName(), symbolKind); } } return false; } internal IAttribute GetAttribute(MetadataReader metadata, CustomAttributeHandleCollection customAttributes, KnownAttribute attribute, SymbolKind symbolKind) { Debug.Assert(attribute.IsCustomAttribute()); foreach (var h in customAttributes) { var attr = metadata.GetCustomAttribute(h); if (attr.IsKnownAttribute(metadata, attribute) && !IgnoreAttribute(attribute.GetTypeName(), symbolKind)) { // Attribute types shouldn't be open generic, so we don't need a generic context. var ctor = module.ResolveMethod(attr.Constructor, new GenericContext()); return new CustomAttribute(module, ctor, h); } } return null; } static bool IsMethodLike(SymbolKind kind) { return kind switch { SymbolKind.Method => true, SymbolKind.Operator => true, SymbolKind.Constructor => true, SymbolKind.Destructor => true, SymbolKind.Accessor => true, _ => false }; } #endregion #region Security Attributes public void AddSecurityAttributes(DeclarativeSecurityAttributeHandleCollection securityDeclarations) { var metadata = module.metadata; foreach (var secDecl in securityDeclarations) { if (secDecl.IsNil) continue; try { AddSecurityAttributes(metadata.GetDeclarativeSecurityAttribute(secDecl)); } catch (EnumUnderlyingTypeResolveException) { // ignore resolve errors } catch (BadImageFormatException) { // ignore invalid security declarations } } } public void AddSecurityAttributes(DeclarativeSecurityAttribute secDecl) { var securityActionType = module.Compilation.FindType(new TopLevelTypeName("System.Security.Permissions", "SecurityAction")); var securityAction = new CustomAttributeTypedArgument(securityActionType, (int)secDecl.Action); var metadata = module.metadata; var reader = metadata.GetBlobReader(secDecl.PermissionSet); if (reader.ReadByte() == '.') { // binary attribute int attributeCount = reader.ReadCompressedInteger(); for (int i = 0; i < attributeCount; i++) { Add(ReadBinarySecurityAttribute(ref reader, securityAction)); } } else { // for backward compatibility with .NET 1.0: XML-encoded attribute reader.Reset(); Add(ReadXmlSecurityAttribute(ref reader, securityAction)); } } private IAttribute ReadXmlSecurityAttribute(ref SRM.BlobReader reader, CustomAttributeTypedArgument securityAction) { string xml = reader.ReadUTF16(reader.RemainingBytes); var b = new AttributeBuilder(module, KnownAttribute.PermissionSet); b.AddFixedArg(securityAction); b.AddNamedArg("XML", KnownTypeCode.String, xml); return b.Build(); } private IAttribute ReadBinarySecurityAttribute(ref SRM.BlobReader reader, CustomAttributeTypedArgument securityAction) { string attributeTypeName = reader.ReadSerializedString(); IType attributeType = module.TypeProvider.GetTypeFromSerializedName(attributeTypeName); reader.ReadCompressedInteger(); // ?? // The specification seems to be incorrect here, so I'm using the logic from Cecil instead. int numNamed = reader.ReadCompressedInteger(); var decoder = new Metadata.CustomAttributeDecoder(module.TypeProvider, module.metadata); var namedArgs = decoder.DecodeNamedArguments(ref reader, numNamed); return new DefaultAttribute( attributeType, fixedArguments: ImmutableArray.Create(securityAction), namedArguments: namedArgs); } #endregion public IAttribute[] Build() { if (attributes.Count == 0) return Empty.Array; else return attributes.ToArray(); } } struct AttributeBuilder { readonly ICompilation compilation; readonly IType attributeType; ImmutableArray>.Builder fixedArgs; ImmutableArray>.Builder namedArgs; public AttributeBuilder(MetadataModule module, KnownAttribute attributeType) : this(module, module.GetAttributeType(attributeType)) { } public AttributeBuilder(MetadataModule module, IType attributeType) { this.compilation = module.Compilation; this.attributeType = attributeType; this.fixedArgs = ImmutableArray.CreateBuilder>(); this.namedArgs = ImmutableArray.CreateBuilder>(); } public void AddFixedArg(CustomAttributeTypedArgument arg) { fixedArgs.Add(arg); } public void AddFixedArg(KnownTypeCode type, object value) { AddFixedArg(compilation.FindType(type), value); } public void AddFixedArg(TopLevelTypeName type, object value) { AddFixedArg(compilation.FindType(type), value); } public void AddFixedArg(IType type, object value) { fixedArgs.Add(new CustomAttributeTypedArgument(type, value)); } public void AddNamedArg(string name, KnownTypeCode type, object value) { AddNamedArg(name, compilation.FindType(type), value); } public void AddNamedArg(string name, TopLevelTypeName type, object value) { AddNamedArg(name, compilation.FindType(type), value); } public void AddNamedArg(string name, IType type, object value) { CustomAttributeNamedArgumentKind kind; if (attributeType.GetFields(f => f.Name == name, GetMemberOptions.ReturnMemberDefinitions).Any()) kind = CustomAttributeNamedArgumentKind.Field; else kind = CustomAttributeNamedArgumentKind.Property; namedArgs.Add(new CustomAttributeNamedArgument(name, kind, type, value)); } public IAttribute Build() { return new DefaultAttribute(attributeType, fixedArgs.ToImmutable(), namedArgs.ToImmutable()); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/BaseTypeCollector.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Helper class for the GetAllBaseTypes() implementation. /// sealed class BaseTypeCollector : List { readonly Stack activeTypes = new Stack(); /// /// If this option is enabled, the list will not contain interfaces when retrieving the base types /// of a class. /// internal bool SkipImplementedInterfaces; public void CollectBaseTypes(IType type) { IType def = type.GetDefinition() ?? type; // Maintain a stack of currently active type definitions, and avoid having one definition // multiple times on that stack. // This is necessary to ensure the output is finite in the presence of cyclic inheritance: // class C : C> {} would not be caught by the 'no duplicate output' check, yet would // produce infinite output. if (activeTypes.Contains(def)) return; activeTypes.Push(def); // Note that we also need to push non-type definitions, e.g. for protecting against // cyclic inheritance in type parameters (where T : S where S : T). // The output check doesn't help there because we call Add(type) only at the end. // We can't simply call this.Add(type); at the start because that would return in an incorrect order. // Avoid outputting a type more than once - necessary for "diamond" multiple inheritance // (e.g. C implements I1 and I2, and both interfaces derive from Object) if (!this.Contains(type)) { foreach (IType baseType in type.DirectBaseTypes) { if (SkipImplementedInterfaces && def != null && def.Kind != TypeKind.Interface && def.Kind != TypeKind.TypeParameter) { if (baseType.Kind == TypeKind.Interface) { // skip the interface continue; } } CollectBaseTypes(baseType); } // Add(type) at the end - we want a type to be output only after all its base types were added. this.Add(type); // Note that this is not the same as putting the this.Add() call in front and then reversing the list. // For the diamond inheritance, Add() at the start produces "C, I1, Object, I2", // while Add() at the end produces "Object, I1, I2, C". } activeTypes.Pop(); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Custom attribute loaded from metadata. /// sealed class CustomAttribute : IAttribute { readonly MetadataModule module; readonly CustomAttributeHandle handle; public IMethod Constructor { get; } // lazy-loaded: CustomAttributeValue value; bool valueDecoded; bool hasDecodeErrors; internal CustomAttribute(MetadataModule module, IMethod attrCtor, CustomAttributeHandle handle) { Debug.Assert(module != null); Debug.Assert(attrCtor != null); Debug.Assert(!handle.IsNil); this.module = module; this.Constructor = attrCtor; this.handle = handle; } public IType AttributeType => Constructor.DeclaringType; public ImmutableArray> FixedArguments { get { DecodeValue(); return value.FixedArguments; } } public ImmutableArray> NamedArguments { get { DecodeValue(); return value.NamedArguments; } } public bool HasDecodeErrors { get { DecodeValue(); return hasDecodeErrors; } } void DecodeValue() { lock (this) { try { if (!valueDecoded) { var metadata = module.metadata; var attr = metadata.GetCustomAttribute(handle); value = attr.DecodeValue(module.TypeProvider); valueDecoded = true; } } catch (EnumUnderlyingTypeResolveException) { value = new CustomAttributeValue( ImmutableArray>.Empty, ImmutableArray>.Empty ); hasDecodeErrors = true; valueDecoded = true; // in case of errors, never try again. } catch (BadImageFormatException) { value = new CustomAttributeValue( ImmutableArray>.Empty, ImmutableArray>.Empty ); hasDecodeErrors = true; valueDecoded = true; // in case of errors, never try again. } } } internal static IMember MemberForNamedArgument(IType attributeType, CustomAttributeNamedArgument namedArgument) { switch (namedArgument.Kind) { case CustomAttributeNamedArgumentKind.Field: return attributeType.GetFields(f => f.Name == namedArgument.Name).LastOrDefault(); case CustomAttributeNamedArgumentKind.Property: return attributeType.GetProperties(p => p.Name == namedArgument.Name).LastOrDefault(); default: return null; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DecimalConstantHelper.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { static class DecimalConstantHelper { public static bool AllowsDecimalConstants(MetadataModule module) { return ((module.TypeSystemOptions & TypeSystemOptions.DecimalConstants) == TypeSystemOptions.DecimalConstants); } public static bool IsDecimalConstant(MetadataModule module, CustomAttributeHandleCollection attributeHandles) { return attributeHandles.HasKnownAttribute(module.metadata, KnownAttribute.DecimalConstant); } public static object GetDecimalConstantValue(MetadataModule module, CustomAttributeHandleCollection attributeHandles) { var metadata = module.metadata; foreach (var attributeHandle in attributeHandles) { var attribute = metadata.GetCustomAttribute(attributeHandle); if (attribute.IsKnownAttribute(metadata, KnownAttribute.DecimalConstant)) return TryDecodeDecimalConstantAttribute(module, attribute); } return null; } static decimal? TryDecodeDecimalConstantAttribute(MetadataModule module, System.Reflection.Metadata.CustomAttribute attribute) { var attrValue = attribute.DecodeValue(module.TypeProvider); if (attrValue.FixedArguments.Length != 5) return null; // DecimalConstantAttribute has the arguments (byte scale, byte sign, uint hi, uint mid, uint low) or (byte scale, byte sign, int hi, int mid, int low) // Both of these invoke the Decimal constructor (int lo, int mid, int hi, bool isNegative, byte scale) with explicit argument conversions if required. if (!(attrValue.FixedArguments[0].Value is byte scale && attrValue.FixedArguments[1].Value is byte sign)) return null; unchecked { if (attrValue.FixedArguments[2].Value is uint hi && attrValue.FixedArguments[3].Value is uint mid && attrValue.FixedArguments[4].Value is uint lo) { return new decimal((int)lo, (int)mid, (int)hi, sign != 0, scale); } } { if (attrValue.FixedArguments[2].Value is int hi && attrValue.FixedArguments[3].Value is int mid && attrValue.FixedArguments[4].Value is int lo) { return new decimal(lo, mid, hi, sign != 0, scale); } } return null; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DecoratedType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public abstract class DecoratedType : IType { protected readonly IType baseType; protected DecoratedType(IType baseType) { this.baseType = baseType; } TypeKind IType.Kind => baseType.Kind; bool? IType.IsReferenceType => baseType.IsReferenceType; bool IType.IsByRefLike => baseType.IsByRefLike; Nullability IType.Nullability => baseType.Nullability; public abstract IType ChangeNullability(Nullability nullability); IType IType.DeclaringType => baseType.DeclaringType; int IType.TypeParameterCount => baseType.TypeParameterCount; IReadOnlyList IType.TypeParameters => baseType.TypeParameters; IReadOnlyList IType.TypeArguments => baseType.TypeArguments; IEnumerable IType.DirectBaseTypes => baseType.DirectBaseTypes; string INamedElement.FullName => baseType.FullName; string INamedElement.Name => baseType.Name; string INamedElement.ReflectionName => baseType.ReflectionName; string INamedElement.Namespace => baseType.Namespace; public abstract IType AcceptVisitor(TypeVisitor visitor); public abstract bool Equals(IType other); IEnumerable IType.GetAccessors(Predicate filter, GetMemberOptions options) { return baseType.GetAccessors(filter, options); } IEnumerable IType.GetConstructors(Predicate filter, GetMemberOptions options) { return baseType.GetConstructors(filter, options); } ITypeDefinition IType.GetDefinition() { return baseType.GetDefinition(); } ITypeDefinitionOrUnknown IType.GetDefinitionOrUnknown() { return baseType.GetDefinitionOrUnknown(); } IEnumerable IType.GetEvents(Predicate filter, GetMemberOptions options) { return baseType.GetEvents(filter, options); } IEnumerable IType.GetFields(Predicate filter, GetMemberOptions options) { return baseType.GetFields(filter, options); } IEnumerable IType.GetMembers(Predicate filter, GetMemberOptions options) { return baseType.GetMembers(filter, options); } IEnumerable IType.GetMethods(Predicate filter, GetMemberOptions options) { return baseType.GetMethods(filter, options); } IEnumerable IType.GetMethods(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return baseType.GetMethods(typeArguments, filter, options); } IEnumerable IType.GetNestedTypes(Predicate filter, GetMemberOptions options) { return baseType.GetNestedTypes(filter, options); } IEnumerable IType.GetNestedTypes(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return baseType.GetNestedTypes(typeArguments, filter, options); } IEnumerable IType.GetProperties(Predicate filter, GetMemberOptions options) { return baseType.GetProperties(filter, options); } TypeParameterSubstitution IType.GetSubstitution() { return baseType.GetSubstitution(); } public abstract IType VisitChildren(TypeVisitor visitor); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAssemblyReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// References an existing assembly by name. /// [Serializable] public sealed class DefaultAssemblyReference : IModuleReference, ISupportsInterning { public static readonly IModuleReference CurrentAssembly = new CurrentModuleReference(); readonly string shortName; public DefaultAssemblyReference(string assemblyName) { int pos = assemblyName != null ? assemblyName.IndexOf(',') : -1; if (pos >= 0) shortName = assemblyName.Substring(0, pos); else shortName = assemblyName; } public IModule Resolve(ITypeResolveContext context) { IModule current = context.CurrentModule; if (current != null && string.Equals(shortName, current.AssemblyName, StringComparison.OrdinalIgnoreCase)) return current; foreach (IModule asm in context.Compilation.Modules) { if (string.Equals(shortName, asm.AssemblyName, StringComparison.OrdinalIgnoreCase)) return asm; } return null; } public override string ToString() { return shortName; } int ISupportsInterning.GetHashCodeForInterning() { unchecked { return shortName.GetHashCode(); } } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { DefaultAssemblyReference o = other as DefaultAssemblyReference; return o != null && shortName == o.shortName; } [Serializable] sealed class CurrentModuleReference : IModuleReference { public IModule Resolve(ITypeResolveContext context) { IModule asm = context.CurrentModule; if (asm == null) throw new ArgumentException("A reference to the current assembly cannot be resolved in the compilation's global type resolve context."); return asm; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Immutable; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// IAttribute implementation for already-resolved attributes. /// public class DefaultAttribute : IAttribute { readonly IType attributeType; volatile IMethod constructor; public ImmutableArray> FixedArguments { get; } public ImmutableArray> NamedArguments { get; } public DefaultAttribute(IType attributeType, ImmutableArray> fixedArguments, ImmutableArray> namedArguments) { if (attributeType == null) throw new ArgumentNullException(nameof(attributeType)); this.attributeType = attributeType; this.FixedArguments = fixedArguments; this.NamedArguments = namedArguments; } public DefaultAttribute(IMethod constructor, ImmutableArray> fixedArguments, ImmutableArray> namedArguments) { if (constructor == null) throw new ArgumentNullException(nameof(constructor)); this.constructor = constructor; this.attributeType = constructor.DeclaringType ?? SpecialType.UnknownType; this.FixedArguments = fixedArguments; this.NamedArguments = namedArguments; if (fixedArguments.Length != constructor.Parameters.Count) { throw new ArgumentException("Positional argument count must match the constructor's parameter count"); } } public IType AttributeType { get { return attributeType; } } bool IAttribute.HasDecodeErrors => false; public IMethod Constructor { get { IMethod ctor = this.constructor; if (ctor == null) { foreach (IMethod candidate in this.AttributeType.GetConstructors(m => m.Parameters.Count == FixedArguments.Length)) { if (candidate.Parameters.Select(p => p.Type).SequenceEqual(this.FixedArguments.Select(a => a.Type))) { ctor = candidate; break; } } this.constructor = ctor; } return ctor; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Default implementation of . /// public sealed class DefaultParameter : IParameter { readonly IType type; readonly string name; readonly IReadOnlyList attributes; readonly ReferenceKind referenceKind; readonly bool isParams, isOptional; readonly object defaultValue; readonly IParameterizedMember owner; public DefaultParameter(IType type, string name) { if (type == null) throw new ArgumentNullException(nameof(type)); if (name == null) throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; this.attributes = EmptyList.Instance; } public DefaultParameter(IType type, string name, IParameterizedMember owner = null, IReadOnlyList attributes = null, ReferenceKind referenceKind = ReferenceKind.None, bool isParams = false, bool isOptional = false, object defaultValue = null) { if (type == null) throw new ArgumentNullException(nameof(type)); if (name == null) throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; this.owner = owner; this.attributes = attributes ?? EmptyList.Instance; this.referenceKind = referenceKind; this.isParams = isParams; this.isOptional = isOptional; this.defaultValue = defaultValue; } SymbolKind ISymbol.SymbolKind { get { return SymbolKind.Parameter; } } public IParameterizedMember Owner { get { return owner; } } public IEnumerable GetAttributes() => attributes; public ReferenceKind ReferenceKind => referenceKind; public bool IsParams => isParams; public bool IsOptional => isOptional; public string Name { get { return name; } } public IType Type { get { return type; } } bool IVariable.IsConst { get { return false; } } public bool HasConstantValueInSignature { get { return IsOptional; } } public LifetimeAnnotation Lifetime => default; public object GetConstantValue(bool throwOnInvalidMetadata) { return defaultValue; } public override string ToString() { return ToString(this); } public static string ToString(IParameter parameter) { StringBuilder b = new StringBuilder(); b.Append(parameter.ReferenceKind switch { ReferenceKind.None => "", ReferenceKind.Ref => "ref ", ReferenceKind.Out => "out ", ReferenceKind.In => "in ", ReferenceKind.RefReadOnly => "ref readonly ", _ => throw new NotSupportedException() }); if (parameter.IsParams) b.Append("params "); b.Append(parameter.Name); b.Append(':'); b.Append(parameter.Type.ReflectionName); if (parameter.IsOptional && parameter.HasConstantValueInSignature) { b.Append(" = "); object val = parameter.GetConstantValue(throwOnInvalidMetadata: false); if (val != null) b.Append(val.ToString()); else b.Append("null"); } return b.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public class DefaultTypeParameter : AbstractTypeParameter { readonly bool hasValueTypeConstraint; readonly bool hasReferenceTypeConstraint; readonly bool hasDefaultConstructorConstraint; readonly Nullability nullabilityConstraint; readonly IReadOnlyList attributes; public DefaultTypeParameter( IEntity owner, int index, string name = null, VarianceModifier variance = VarianceModifier.Invariant, IReadOnlyList attributes = null, bool hasValueTypeConstraint = false, bool hasReferenceTypeConstraint = false, bool hasDefaultConstructorConstraint = false, IReadOnlyList constraints = null, Nullability nullabilityConstraint = Nullability.Oblivious) : base(owner, index, name, variance) { this.hasValueTypeConstraint = hasValueTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } public DefaultTypeParameter( ICompilation compilation, SymbolKind ownerType, int index, string name = null, VarianceModifier variance = VarianceModifier.Invariant, IReadOnlyList attributes = null, bool hasValueTypeConstraint = false, bool hasReferenceTypeConstraint = false, bool hasDefaultConstructorConstraint = false, IReadOnlyList constraints = null, Nullability nullabilityConstraint = Nullability.Oblivious) : base(compilation, ownerType, index, name, variance) { this.hasValueTypeConstraint = hasValueTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } public override IEnumerable GetAttributes() => attributes; public override bool HasValueTypeConstraint => hasValueTypeConstraint; public override bool HasReferenceTypeConstraint => hasReferenceTypeConstraint; public override bool HasDefaultConstructorConstraint => hasDefaultConstructorConstraint; public override bool HasUnmanagedConstraint => false; public override bool AllowsRefLikeType => false; public override Nullability NullabilityConstraint => nullabilityConstraint; public override IReadOnlyList TypeConstraints { get; } IReadOnlyList MakeConstraints(IReadOnlyList constraints) { var result = new List(); bool hasNonInterfaceConstraint = false; if (constraints != null) { foreach (IType c in constraints) { result.Add(new TypeConstraint(c)); if (c.Kind != TypeKind.Interface) hasNonInterfaceConstraint = true; } } // Do not add the 'System.Object' constraint if there is another constraint with a base class. if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { result.Add(new TypeConstraint(this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object))); } return result; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultVariable.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Default implementation of . /// public sealed class DefaultVariable : IVariable { readonly string name; readonly IType type; readonly object constantValue; readonly bool isConst; public DefaultVariable(IType type, string name) { if (type == null) throw new ArgumentNullException(nameof(type)); if (name == null) throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; } public DefaultVariable(IType type, string name, bool isConst = false, object constantValue = null) : this(type, name) { this.isConst = isConst; this.constantValue = constantValue; } public string Name { get { return name; } } public IType Type { get { return type; } } public bool IsConst { get { return isConst; } } public object GetConstantValue(bool throwOnInvalidMetadata) { return constantValue; } public SymbolKind SymbolKind { get { return SymbolKind.Variable; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Threading; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public sealed class DummyTypeParameter : AbstractType, ITypeParameter { static ITypeParameter[] methodTypeParameters = { new DummyTypeParameter(SymbolKind.Method, 0) }; static ITypeParameter[] classTypeParameters = { new DummyTypeParameter(SymbolKind.TypeDefinition, 0) }; static IReadOnlyList[] classTypeParameterLists = { EmptyList.Instance }; public static ITypeParameter GetMethodTypeParameter(int index) { return GetTypeParameter(ref methodTypeParameters, SymbolKind.Method, index); } public static ITypeParameter GetClassTypeParameter(int index) { return GetTypeParameter(ref classTypeParameters, SymbolKind.TypeDefinition, index); } static ITypeParameter GetTypeParameter(ref ITypeParameter[] typeParameters, SymbolKind symbolKind, int index) { ITypeParameter[] tps = typeParameters; while (index >= tps.Length) { // We don't have a normal type parameter for this index, so we need to extend our array. // Because the array can be used concurrently from multiple threads, we have to use // Interlocked.CompareExchange. ITypeParameter[] newTps = new ITypeParameter[index + 1]; tps.CopyTo(newTps, 0); for (int i = tps.Length; i < newTps.Length; i++) { newTps[i] = new DummyTypeParameter(symbolKind, i); } ITypeParameter[] oldTps = Interlocked.CompareExchange(ref typeParameters, newTps, tps); if (oldTps == tps) { // exchange successful tps = newTps; } else { // exchange not successful tps = oldTps; } } return tps[index]; } /// /// Gets a list filled with dummy type parameters. /// internal static IReadOnlyList GetClassTypeParameterList(int length) { IReadOnlyList[] tps = classTypeParameterLists; while (length >= tps.Length) { // We don't have a normal type parameter for this index, so we need to extend our array. // Because the array can be used concurrently from multiple threads, we have to use // Interlocked.CompareExchange. IReadOnlyList[] newTps = new IReadOnlyList[length + 1]; tps.CopyTo(newTps, 0); for (int i = tps.Length; i < newTps.Length; i++) { var newList = new ITypeParameter[i]; for (int j = 0; j < newList.Length; j++) { newList[j] = GetClassTypeParameter(j); } newTps[i] = newList; } var oldTps = Interlocked.CompareExchange(ref classTypeParameterLists, newTps, tps); if (oldTps == tps) { // exchange successful tps = newTps; } else { // exchange not successful tps = oldTps; } } return tps[length]; } readonly SymbolKind ownerType; readonly int index; private DummyTypeParameter(SymbolKind ownerType, int index) { this.ownerType = ownerType; this.index = index; } SymbolKind ISymbol.SymbolKind { get { return SymbolKind.TypeParameter; } } public override string Name { get { return (ownerType == SymbolKind.Method ? "!!" : "!") + index; } } public override string ReflectionName { get { return (ownerType == SymbolKind.Method ? "``" : "`") + index; } } public override string ToString() { return ReflectionName + " (dummy)"; } public override bool? IsReferenceType { get { return null; } } public override TypeKind Kind { get { return TypeKind.TypeParameter; } } public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTypeParameter(this); } public int Index { get { return index; } } IEnumerable ITypeParameter.GetAttributes() => EmptyList.Instance; SymbolKind ITypeParameter.OwnerType { get { return ownerType; } } VarianceModifier ITypeParameter.Variance { get { return VarianceModifier.Invariant; } } IEntity ITypeParameter.Owner { get { return null; } } IType ITypeParameter.EffectiveBaseClass { get { return SpecialType.UnknownType; } } IReadOnlyCollection ITypeParameter.EffectiveInterfaceSet { get { return EmptyList.Instance; } } bool ITypeParameter.HasDefaultConstructorConstraint => false; bool ITypeParameter.HasReferenceTypeConstraint => false; bool ITypeParameter.HasValueTypeConstraint => false; bool ITypeParameter.HasUnmanagedConstraint => false; bool ITypeParameter.AllowsRefLikeType => false; Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious; IReadOnlyList ITypeParameter.TypeConstraints => EmptyList.Instance; public override IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious) { return this; } else { return new NullabilityAnnotatedTypeParameter(this, nullability); } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Base class for fake members. /// abstract class FakeMember : IMember { readonly ICompilation compilation; protected FakeMember(ICompilation compilation) { this.compilation = compilation ?? throw new ArgumentNullException(nameof(compilation)); } IMember IMember.MemberDefinition => this; public IType ReturnType { get; set; } = SpecialType.UnknownType; IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => EmptyList.Instance; bool IMember.IsExplicitInterfaceImplementation => false; bool IMember.IsVirtual => false; bool IMember.IsOverride => false; bool IMember.IsOverridable => false; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; EntityHandle IEntity.MetadataToken => default; public string Name { get; set; } ITypeDefinition IEntity.DeclaringTypeDefinition => DeclaringType?.GetDefinition(); public IType DeclaringType { get; set; } IModule IEntity.ParentModule => DeclaringType?.GetDefinition()?.ParentModule; IEnumerable IEntity.GetAttributes() => EmptyList.Instance; bool IEntity.HasAttribute(KnownAttribute attribute) => false; IAttribute IEntity.GetAttribute(KnownAttribute attribute) => null; public Accessibility Accessibility { get; set; } = Accessibility.Public; public bool IsStatic { get; set; } bool IEntity.IsAbstract => false; bool IEntity.IsSealed => false; public abstract SymbolKind SymbolKind { get; } ICompilation ICompilationProvider.Compilation => compilation; string INamedElement.FullName { get { if (DeclaringType != null) return DeclaringType.FullName + "." + Name; else return Name; } } string INamedElement.ReflectionName { get { if (DeclaringType != null) return DeclaringType.ReflectionName + "." + Name; else return Name; } } string INamedElement.Namespace => DeclaringType?.Namespace; bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return Equals(obj); } public abstract IMember Specialize(TypeParameterSubstitution substitution); } class FakeField : FakeMember, IField { public FakeField(ICompilation compilation) : base(compilation) { } bool IField.IsReadOnly => false; bool IField.ReturnTypeIsRefReadOnly => false; bool IField.IsVolatile => false; bool IVariable.IsConst => false; object IVariable.GetConstantValue(bool throwOnInvalidMetadata) => null; IType IVariable.Type => ReturnType; public override SymbolKind SymbolKind => SymbolKind.Field; public override IMember Specialize(TypeParameterSubstitution substitution) { return SpecializedField.Create(this, substitution); } } class FakeMethod : FakeMember, IMethod { readonly SymbolKind symbolKind; public FakeMethod(ICompilation compilation, SymbolKind symbolKind) : base(compilation) { this.symbolKind = symbolKind; } public override SymbolKind SymbolKind => symbolKind; IEnumerable IMethod.GetReturnTypeAttributes() => EmptyList.Instance; bool IMethod.ReturnTypeIsRefReadOnly => false; bool IMethod.ThisIsRefReadOnly => false; bool IMethod.IsInitOnly => false; public IReadOnlyList TypeParameters { get; set; } = EmptyList.Instance; IReadOnlyList IMethod.TypeArguments => TypeParameters; bool IMethod.IsExtensionMethod => false; bool IMethod.IsLocalFunction => false; bool IMethod.IsConstructor => symbolKind == SymbolKind.Constructor; bool IMethod.IsDestructor => symbolKind == SymbolKind.Destructor; bool IMethod.IsOperator => symbolKind == SymbolKind.Operator; bool IMethod.HasBody => false; public bool IsAccessor => AccessorOwner is not null; public IMember AccessorOwner { get; set; } public MethodSemanticsAttributes AccessorKind { get; set; } IMethod IMethod.ReducedFrom => null; public IReadOnlyList Parameters { get; set; } = EmptyList.Instance; public override IMember Specialize(TypeParameterSubstitution substitution) { return SpecializedMethod.Create(this, substitution); } IMethod IMethod.Specialize(TypeParameterSubstitution substitution) { return SpecializedMethod.Create(this, substitution); } internal static IMethod CreateDummyConstructor(ICompilation compilation, IType declaringType, Accessibility accessibility = Accessibility.Public) { return new FakeMethod(compilation, SymbolKind.Constructor) { DeclaringType = declaringType, Name = ".ctor", ReturnType = compilation.FindType(KnownTypeCode.Void), Accessibility = accessibility, }; } } sealed class FakeProperty : FakeMember, IProperty { public FakeProperty(ICompilation compilation) : base(compilation) { } public override SymbolKind SymbolKind => IsIndexer ? SymbolKind.Indexer : SymbolKind.Property; public override IMember Specialize(TypeParameterSubstitution substitution) => SpecializedProperty.Create(this, substitution); public bool CanGet => Getter is not null; public bool CanSet => Setter is not null; public IMethod Getter { get; set; } public IMethod Setter { get; set; } public bool IsIndexer { get; set; } public bool ReturnTypeIsRefReadOnly => false; public IReadOnlyList Parameters { get; set; } public override string ToString() => "FakeProperty " + ReturnType + " " + DeclaringType.Name + "." + Name + (Parameters.Count == 0 ? "" : "[" + string.Join(", ", Parameters) + "]") + " { " + (CanGet ? "get; " : "") + (CanSet ? "set; " : "") + "}"; } sealed class FakeEvent : FakeMember, IEvent { public FakeEvent(ICompilation compilation) : base(compilation) { } public override SymbolKind SymbolKind => SymbolKind.Event; public override IMember Specialize(TypeParameterSubstitution substitution) => SpecializedEvent.Create(this, substitution); public bool CanAdd => AddAccessor is not null; public bool CanRemove => RemoveAccessor is not null; public bool CanInvoke => InvokeAccessor is not null; public IMethod AddAccessor { get; set; } public IMethod RemoveAccessor { get; set; } public IMethod InvokeAccessor { get; set; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/GetMembersHelper.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Provides helper methods for implementing GetMembers() on IType-implementations. /// Note: GetMembersHelper will recursively call back into IType.GetMembers(), but only with /// both GetMemberOptions.IgnoreInheritedMembers and GetMemberOptions.ReturnMemberDefinitions set, /// and only the 'simple' overloads (not taking type arguments). /// /// Ensure that your IType implementation does not use the GetMembersHelper if both flags are set, /// otherwise you'll get a StackOverflowException! /// static class GetMembersHelper { #region GetNestedTypes public static IEnumerable GetNestedTypes(IType type, Predicate filter, GetMemberOptions options) { return GetNestedTypes(type, null, filter, options); } public static IEnumerable GetNestedTypes(IType type, IReadOnlyList nestedTypeArguments, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetNestedTypesImpl(type, nestedTypeArguments, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetNestedTypesImpl(t, nestedTypeArguments, filter, options)); } } static IEnumerable GetNestedTypesImpl(IType outerType, IReadOnlyList nestedTypeArguments, Predicate filter, GetMemberOptions options) { ITypeDefinition outerTypeDef = outerType.GetDefinition(); if (outerTypeDef == null) yield break; int outerTypeParameterCount = outerTypeDef.TypeParameterCount; ParameterizedType pt = outerType as ParameterizedType; foreach (ITypeDefinition nestedType in outerTypeDef.NestedTypes) { int totalTypeParameterCount = nestedType.TypeParameterCount; if (nestedTypeArguments != null) { if (totalTypeParameterCount - outerTypeParameterCount != nestedTypeArguments.Count) continue; } if (!(filter == null || filter(nestedType))) continue; if (totalTypeParameterCount == 0 || (options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) { yield return nestedType; } else { // We need to parameterize the nested type IType[] newTypeArguments = new IType[totalTypeParameterCount]; for (int i = 0; i < outerTypeParameterCount; i++) { newTypeArguments[i] = pt != null ? pt.GetTypeArgument(i) : outerTypeDef.TypeParameters[i]; } for (int i = outerTypeParameterCount; i < totalTypeParameterCount; i++) { if (nestedTypeArguments != null) newTypeArguments[i] = nestedTypeArguments[i - outerTypeParameterCount]; else newTypeArguments[i] = SpecialType.UnboundTypeArgument; } yield return new ParameterizedType(nestedType, newTypeArguments); } } } #endregion #region GetMethods public static IEnumerable GetMethods(IType type, Predicate filter, GetMemberOptions options) { return GetMethods(type, null, filter, options); } public static IEnumerable GetMethods(IType type, IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { if (typeArguments != null && typeArguments.Count > 0) { filter = FilterTypeParameterCount(typeArguments.Count).And(filter); } if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetMethodsImpl(type, typeArguments, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetMethodsImpl(t, typeArguments, filter, options)); } } static Predicate FilterTypeParameterCount(int expectedTypeParameterCount) { return m => m.TypeParameters.Count == expectedTypeParameterCount; } const GetMemberOptions declaredMembers = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; static IEnumerable GetMethodsImpl(IType baseType, IReadOnlyList methodTypeArguments, Predicate filter, GetMemberOptions options) { IEnumerable declaredMethods = baseType.GetMethods(filter, options | declaredMembers); ParameterizedType pt = baseType as ParameterizedType; if ((options & GetMemberOptions.ReturnMemberDefinitions) == 0 && (pt != null || (methodTypeArguments != null && methodTypeArguments.Count > 0))) { TypeParameterSubstitution substitution = null; foreach (IMethod m in declaredMethods) { if (methodTypeArguments != null && methodTypeArguments.Count > 0) { if (m.TypeParameters.Count != methodTypeArguments.Count) continue; } if (substitution == null) { if (pt != null) substitution = pt.GetSubstitution(methodTypeArguments); else substitution = new TypeParameterSubstitution(null, methodTypeArguments); } yield return new SpecializedMethod(m, substitution); } } else { foreach (IMethod m in declaredMethods) { yield return m; } } } #endregion #region GetAccessors public static IEnumerable GetAccessors(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetAccessorsImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetAccessorsImpl(t, filter, options)); } } static IEnumerable GetAccessorsImpl(IType baseType, Predicate filter, GetMemberOptions options) { return GetConstructorsOrAccessorsImpl(baseType, baseType.GetAccessors(filter, options | declaredMembers), options); } #endregion #region GetConstructors public static IEnumerable GetConstructors(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetConstructorsImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetConstructorsImpl(t, filter, options)); } } static IEnumerable GetConstructorsImpl(IType baseType, Predicate filter, GetMemberOptions options) { return GetConstructorsOrAccessorsImpl(baseType, baseType.GetConstructors(filter, options | declaredMembers), options); } static IEnumerable GetConstructorsOrAccessorsImpl(IType baseType, IEnumerable declaredMembers, GetMemberOptions options) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) { return declaredMembers; } ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); return declaredMembers.Select(m => new SpecializedMethod(m, substitution) { DeclaringType = pt }); } else { return declaredMembers; } } #endregion #region GetProperties public static IEnumerable GetProperties(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetPropertiesImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetPropertiesImpl(t, filter, options)); } } static IEnumerable GetPropertiesImpl(IType baseType, Predicate filter, GetMemberOptions options) { IEnumerable declaredProperties = baseType.GetProperties(filter, options | declaredMembers); if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) { return declaredProperties; } ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); return declaredProperties.Select(m => new SpecializedProperty(m, substitution) { DeclaringType = pt }); } else { return declaredProperties; } } #endregion #region GetFields public static IEnumerable GetFields(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFieldsImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetFieldsImpl(t, filter, options)); } } static IEnumerable GetFieldsImpl(IType baseType, Predicate filter, GetMemberOptions options) { IEnumerable declaredFields = baseType.GetFields(filter, options | declaredMembers); if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) { return declaredFields; } ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); return declaredFields.Select(m => new SpecializedField(m, substitution) { DeclaringType = pt }); } else { return declaredFields; } } #endregion #region GetEvents public static IEnumerable GetEvents(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetEventsImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetEventsImpl(t, filter, options)); } } static IEnumerable GetEventsImpl(IType baseType, Predicate filter, GetMemberOptions options) { IEnumerable declaredEvents = baseType.GetEvents(filter, options | declaredMembers); if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) { return declaredEvents; } ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); return declaredEvents.Select(m => new SpecializedEvent(m, substitution) { DeclaringType = pt }); } else { return declaredEvents; } } #endregion #region GetMembers public static IEnumerable GetMembers(IType type, Predicate filter, GetMemberOptions options) { if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetMembersImpl(type, filter, options); } else { return type.GetNonInterfaceBaseTypes().SelectMany(t => GetMembersImpl(t, filter, options)); } } static IEnumerable GetMembersImpl(IType baseType, Predicate filter, GetMemberOptions options) { foreach (var m in GetMethodsImpl(baseType, null, filter, options)) yield return m; foreach (var m in GetPropertiesImpl(baseType, filter, options)) yield return m; foreach (var m in GetFieldsImpl(baseType, filter, options)) yield return m; foreach (var m in GetEventsImpl(baseType, filter, options)) yield return m; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs ================================================ // Copyright (c) 2010-2018 Daniel Grunwald // // 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.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.TypeSystem { public enum KnownAttribute { /// /// Not a known attribute /// None, CompilerGenerated, CompilerFeatureRequired, /// /// Marks a method as extension method; or a class as containing extension methods. /// Extension, Dynamic, TupleElementNames, Nullable, NullableContext, NullablePublicOnly, Conditional, Obsolete, Embedded, IsReadOnly, SpecialName, DebuggerHidden, DebuggerStepThrough, DebuggerBrowsable, // Assembly attributes: AssemblyVersion, InternalsVisibleTo, TypeForwardedTo, ReferenceAssembly, // Type attributes: Serializable, Flags, ComImport, CoClass, StructLayout, DefaultMember, IsByRefLike, IteratorStateMachine, AsyncStateMachine, AsyncMethodBuilder, AsyncIteratorStateMachine, // Field attributes: FieldOffset, NonSerialized, DecimalConstant, FixedBuffer, // Method attributes: DllImport, PreserveSig, MethodImpl, // Property attributes: IndexerName, // Parameter attributes: ParamArray, ParamCollection, In, Out, Optional, DefaultParameterValue, CallerMemberName, CallerFilePath, CallerLineNumber, ScopedRef, RequiresLocation, // Type parameter attributes: IsUnmanaged, // Marshalling attributes: MarshalAs, // Security attributes: PermissionSet, // C# 9 attributes: NativeInteger, PreserveBaseOverrides, UnmanagedCallersOnly, // C# 11 attributes: Required, // C# 12 attributes: InlineArray, // C# 14 attributes: ExtensionMarker, } public static class KnownAttributes { internal const int Count = (int)KnownAttribute.ExtensionMarker + 1; static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{ default, new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CompilerGeneratedAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "CompilerFeatureRequiredAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ExtensionAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "NullableAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "NullableContextAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "NullablePublicOnlyAttribute"), new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)), new TopLevelTypeName("System", nameof(ObsoleteAttribute)), new TopLevelTypeName("Microsoft.CodeAnalysis", "EmbeddedAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(SpecialNameAttribute)), new TopLevelTypeName("System.Diagnostics", nameof(DebuggerHiddenAttribute)), new TopLevelTypeName("System.Diagnostics", nameof(DebuggerStepThroughAttribute)), new TopLevelTypeName("System.Diagnostics", nameof(DebuggerBrowsableAttribute)), // Assembly attributes: new TopLevelTypeName("System.Reflection", nameof(AssemblyVersionAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(InternalsVisibleToAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TypeForwardedToAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ReferenceAssemblyAttribute)), // Type attributes: new TopLevelTypeName("System", nameof(SerializableAttribute)), new TopLevelTypeName("System", nameof(FlagsAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(ComImportAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(CoClassAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(StructLayoutAttribute)), new TopLevelTypeName("System.Reflection", nameof(DefaultMemberAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "IsByRefLikeAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IteratorStateMachineAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(AsyncStateMachineAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncMethodBuilderAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncIteratorStateMachineAttribute"), // Field attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)), new TopLevelTypeName("System", nameof(NonSerializedAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DecimalConstantAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(FixedBufferAttribute)), // Method attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(DllImportAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(PreserveSigAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplAttribute)), // Property attributes: new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IndexerNameAttribute)), // Parameter attributes: new TopLevelTypeName("System", nameof(ParamArrayAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "ParamCollectionAttribute"), new TopLevelTypeName("System.Runtime.InteropServices", nameof(InAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OutAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OptionalAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(DefaultParameterValueAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "ScopedRefAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "RequiresLocationAttribute"), // Type parameter attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "IsUnmanagedAttribute"), // Marshalling attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(MarshalAsAttribute)), // Security attributes: new TopLevelTypeName("System.Security.Permissions", "PermissionSetAttribute"), // C# 9 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"), new TopLevelTypeName("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute"), // C# 11 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "RequiredMemberAttribute"), // C# 12 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "InlineArrayAttribute"), // C# 14 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "ExtensionMarkerAttribute"), }; public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr) { Debug.Assert(attr != KnownAttribute.None); return ref typeNames[(int)attr]; } public static IType FindType(this ICompilation compilation, KnownAttribute attrType) { return compilation.FindType(attrType.GetTypeName()); } public static KnownAttribute IsKnownAttributeType(this ITypeDefinition attributeType) { if (!attributeType.GetNonInterfaceBaseTypes().Any(t => t.IsKnownType(KnownTypeCode.Attribute))) return KnownAttribute.None; for (int i = 1; i < typeNames.Length; i++) { if (typeNames[i] == attributeType.FullTypeName) return (KnownAttribute)i; } return KnownAttribute.None; } public static bool IsCustomAttribute(this KnownAttribute knownAttribute) { switch (knownAttribute) { case KnownAttribute.Serializable: case KnownAttribute.ComImport: case KnownAttribute.StructLayout: case KnownAttribute.DllImport: case KnownAttribute.PreserveSig: case KnownAttribute.MethodImpl: case KnownAttribute.FieldOffset: case KnownAttribute.NonSerialized: case KnownAttribute.MarshalAs: case KnownAttribute.PermissionSet: case KnownAttribute.Optional: case KnownAttribute.DefaultParameterValue: case KnownAttribute.In: case KnownAttribute.Out: case KnownAttribute.IndexerName: case KnownAttribute.SpecialName: return false; default: return true; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/KnownTypeCache.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Cache for KnownTypeReferences. /// sealed class KnownTypeCache { readonly ICompilation compilation; readonly IType[] knownTypes = new IType[KnownTypeReference.KnownTypeCodeCount]; public KnownTypeCache(ICompilation compilation) { this.compilation = compilation; } public IType FindType(KnownTypeCode typeCode) { IType type = LazyInit.VolatileRead(ref knownTypes[(int)typeCode]); if (type != null) { return type; } return LazyInit.GetOrSet(ref knownTypes[(int)typeCode], SearchType(typeCode)); } IType SearchType(KnownTypeCode typeCode) { KnownTypeReference typeRef = KnownTypeReference.Get(typeCode); if (typeRef == null) return SpecialType.UnknownType; var typeName = new TopLevelTypeName(typeRef.Namespace, typeRef.Name, typeRef.TypeParameterCount); foreach (IModule asm in compilation.Modules) { var typeDef = asm.GetTypeDefinition(typeName); if (typeDef != null) return typeDef; } return new UnknownType(typeName); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs ================================================ // 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 // 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; using System.Reflection; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// A local function has zero or more compiler-generated parameters added at the end. /// class LocalFunctionMethod : IMethod { readonly IMethod baseMethod; public LocalFunctionMethod(IMethod baseMethod, string name, bool isStaticLocalFunction, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters) { if (baseMethod == null) throw new ArgumentNullException(nameof(baseMethod)); this.baseMethod = baseMethod; this.Name = name; this.IsStaticLocalFunction = isStaticLocalFunction; this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters; } public bool Equals(IMember obj, TypeVisitor typeNormalization) { if (!(obj is LocalFunctionMethod other)) return false; return baseMethod.Equals(other.baseMethod, typeNormalization) && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters && IsStaticLocalFunction == other.IsStaticLocalFunction; } public override bool Equals(object obj) { if (!(obj is LocalFunctionMethod other)) return false; return baseMethod.Equals(other.baseMethod) && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters && IsStaticLocalFunction == other.IsStaticLocalFunction; } public override int GetHashCode() { return baseMethod.GetHashCode(); } public override string ToString() { return string.Format("[LocalFunctionMethod: ReducedFrom={0}, Name={1}, NumberOfGeneratedParameters={2}, NumberOfCompilerGeneratedTypeParameters={3}, IsStaticLocalFunction={4}]", ReducedFrom, Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters, IsStaticLocalFunction); } internal int NumberOfCompilerGeneratedParameters { get; } internal int NumberOfCompilerGeneratedTypeParameters { get; } internal bool IsStaticLocalFunction { get; } public IMember MemberDefinition => this; public IType ReturnType => baseMethod.ReturnType; IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers; bool IMember.IsExplicitInterfaceImplementation => baseMethod.IsExplicitInterfaceImplementation; public bool IsVirtual => baseMethod.IsVirtual; public bool IsOverride => baseMethod.IsOverride; public bool IsOverridable => baseMethod.IsOverridable; public TypeParameterSubstitution Substitution => baseMethod.Substitution; public IMethod Specialize(TypeParameterSubstitution substitution) { return new LocalFunctionMethod( baseMethod.Specialize(substitution), Name, IsStaticLocalFunction, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); } IMember IMember.Specialize(TypeParameterSubstitution substitution) { return Specialize(substitution); } public bool IsExtensionMethod => baseMethod.IsExtensionMethod; public bool IsLocalFunction => true; public bool IsConstructor => baseMethod.IsConstructor; public bool IsDestructor => baseMethod.IsDestructor; public bool IsOperator => baseMethod.IsOperator; public bool HasBody => baseMethod.HasBody; public bool IsAccessor => baseMethod.IsAccessor; public IMember AccessorOwner => baseMethod.AccessorOwner; public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; public IMethod ReducedFrom => baseMethod; List typeParameters; public IReadOnlyList TypeParameters { get { if (typeParameters == null) typeParameters = new List(baseMethod.TypeParameters.Skip(NumberOfCompilerGeneratedTypeParameters)); return typeParameters; } } List typeArguments; public IReadOnlyList TypeArguments { get { if (typeArguments == null) typeArguments = new List(baseMethod.TypeArguments.Skip(NumberOfCompilerGeneratedTypeParameters)); return typeArguments; } } List parameters; public IReadOnlyList Parameters { get { if (parameters == null) parameters = new List(baseMethod.Parameters.SkipLast(NumberOfCompilerGeneratedParameters)); return parameters; } } public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; public SymbolKind SymbolKind => baseMethod.SymbolKind; public ITypeDefinition DeclaringTypeDefinition => baseMethod.DeclaringTypeDefinition; public IType DeclaringType => baseMethod.DeclaringType; public IModule ParentModule => baseMethod.ParentModule; IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); bool IEntity.HasAttribute(KnownAttribute attribute) => baseMethod.HasAttribute(attribute); IAttribute IEntity.GetAttribute(KnownAttribute attribute) => baseMethod.GetAttribute(attribute); IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly; bool IMethod.IsInitOnly => baseMethod.IsInitOnly; /// /// We consider local functions as always static, because they do not have a "this parameter". /// Even local functions in instance methods capture this. /// public bool IsStatic => true; public bool IsAbstract => baseMethod.IsAbstract; public bool IsSealed => baseMethod.IsSealed; public Accessibility Accessibility => baseMethod.Accessibility; public string FullName => Name; public string Name { get; set; } public string ReflectionName => baseMethod.ReflectionName; public string Namespace => baseMethod.Namespace; public ICompilation Compilation => baseMethod.Compilation; } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MergedNamespace.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// A merged namespace. /// public sealed class MergedNamespace : INamespace { readonly string externAlias; readonly ICompilation compilation; readonly INamespace parentNamespace; readonly INamespace[] namespaces; Dictionary childNamespaces; /// /// Creates a new merged root namespace. /// /// The main compilation. /// The individual namespaces being merged. /// The extern alias for this namespace. public MergedNamespace(ICompilation compilation, INamespace[] namespaces, string externAlias = null) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (namespaces == null) throw new ArgumentNullException(nameof(namespaces)); this.compilation = compilation; this.namespaces = namespaces; this.externAlias = externAlias; } /// /// Creates a new merged child namespace. /// /// The parent merged namespace. /// The individual namespaces being merged. public MergedNamespace(INamespace parentNamespace, INamespace[] namespaces) { if (parentNamespace == null) throw new ArgumentNullException(nameof(parentNamespace)); if (namespaces == null) throw new ArgumentNullException(nameof(namespaces)); this.parentNamespace = parentNamespace; this.namespaces = namespaces; this.compilation = parentNamespace.Compilation; this.externAlias = parentNamespace.ExternAlias; } public string ExternAlias { get { return externAlias; } } public string FullName { get { return namespaces[0].FullName; } } public string Name { get { return namespaces[0].Name; } } public INamespace ParentNamespace { get { return parentNamespace; } } public IEnumerable Types { get { return namespaces.SelectMany(ns => ns.Types); } } public SymbolKind SymbolKind { get { return SymbolKind.Namespace; } } public ICompilation Compilation { get { return compilation; } } public IEnumerable ContributingModules { get { return namespaces.SelectMany(ns => ns.ContributingModules); } } public IEnumerable ChildNamespaces { get { return GetChildNamespaces().Values; } } public INamespace GetChildNamespace(string name) { INamespace ns; if (GetChildNamespaces().TryGetValue(name, out ns)) return ns; else return null; } Dictionary GetChildNamespaces() { var result = LazyInit.VolatileRead(ref this.childNamespaces); if (result != null) { return result; } else { result = new Dictionary(compilation.NameComparer); foreach (var g in namespaces.SelectMany(ns => ns.ChildNamespaces).GroupBy(ns => ns.Name, compilation.NameComparer)) { result.Add(g.Key, new MergedNamespace(this, g.ToArray())); } return LazyInit.GetOrSet(ref this.childNamespaces, result); } } public ITypeDefinition GetTypeDefinition(string name, int typeParameterCount) { ITypeDefinition anyTypeDef = null; foreach (var ns in namespaces) { ITypeDefinition typeDef = ns.GetTypeDefinition(name, typeParameterCount); if (typeDef != null) { if (typeDef.Accessibility == Accessibility.Public) { // Prefer accessible types over non-accessible types. return typeDef; // || (typeDef.IsInternal && typeDef.ParentAssembly.InternalsVisibleTo(...)) // We can't call InternalsVisibleTo() here as we don't know the correct 'current' assembly, // and using the main assembly can cause a stack overflow if there // are internal assembly attributes. } anyTypeDef = typeDef; } } return anyTypeDef; } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "[MergedNamespace {0}{1} (from {2} assemblies)]", externAlias != null ? externAlias + "::" : null, this.FullName, this.namespaces.Length); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataEvent : IEvent { readonly MetadataModule module; readonly EventDefinitionHandle handle; readonly EventAccessors accessors; readonly string name; // lazy-loaded: IType returnType; internal MetadataEvent(MetadataModule module, EventDefinitionHandle handle) { Debug.Assert(module != null); Debug.Assert(!handle.IsNil); this.module = module; this.handle = handle; var metadata = module.metadata; var ev = metadata.GetEventDefinition(handle); accessors = ev.GetAccessors(); name = metadata.GetString(ev.Name); } public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; } public EntityHandle MetadataToken => handle; public string Name => name; SymbolKind ISymbol.SymbolKind => SymbolKind.Event; public bool CanAdd => !accessors.Adder.IsNil; public bool CanRemove => !accessors.Remover.IsNil; public bool CanInvoke => !accessors.Raiser.IsNil; public IMethod AddAccessor => module.GetDefinition(accessors.Adder); public IMethod RemoveAccessor => module.GetDefinition(accessors.Remover); public IMethod InvokeAccessor => module.GetDefinition(accessors.Raiser); IMethod AnyAccessor => module.GetDefinition(accessors.GetAny()); #region Signature (ReturnType + Parameters) public IType ReturnType { get { var returnType = LazyInit.VolatileRead(ref this.returnType); if (returnType != null) return returnType; var metadata = module.metadata; var ev = metadata.GetEventDefinition(handle); var declaringTypeDef = DeclaringTypeDefinition; var context = new GenericContext(declaringTypeDef?.TypeParameters); var nullableContext = declaringTypeDef?.NullableContext ?? Nullability.Oblivious; // The event does not have explicit accessibility in metadata, so use its // containing type to determine whether nullability applies to this type. var typeOptions = module.OptionsForEntity(declaringTypeDef); returnType = module.ResolveType(ev.Type, context, typeOptions, ev.GetCustomAttributes(), nullableContext); return LazyInit.GetOrSet(ref this.returnType, returnType); } } #endregion public bool IsExplicitInterfaceImplementation => AnyAccessor?.IsExplicitInterfaceImplementation ?? false; public IEnumerable ExplicitlyImplementedInterfaceMembers => GetInterfaceMembersFromAccessor(AnyAccessor); internal static IEnumerable GetInterfaceMembersFromAccessor(IMethod method) { if (method == null) return EmptyList.Instance; return method.ExplicitlyImplementedInterfaceMembers.Select(m => ((IMethod)m).AccessorOwner).Where(m => m != null); } public ITypeDefinition DeclaringTypeDefinition => AnyAccessor?.DeclaringTypeDefinition; public IType DeclaringType => AnyAccessor?.DeclaringType; IMember IMember.MemberDefinition => this; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; #region Attributes public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var eventDef = metadata.GetEventDefinition(handle); // SpecialName if ((eventDef.Attributes & (EventAttributes.SpecialName | EventAttributes.RTSpecialName)) == EventAttributes.SpecialName) { b.Add(KnownAttribute.SpecialName); } b.Add(eventDef.GetCustomAttributes(), SymbolKind.Event); return b.Build(); } public bool HasAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().Any(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetEventDefinition(handle); return b.HasAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Event); } public IAttribute GetAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().FirstOrDefault(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetEventDefinition(handle); return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Event); } #endregion public Accessibility Accessibility => AnyAccessor?.Accessibility ?? Accessibility.None; public bool IsStatic => AnyAccessor?.IsStatic ?? false; public bool IsAbstract => AnyAccessor?.IsAbstract ?? false; public bool IsSealed => AnyAccessor?.IsSealed ?? false; public bool IsVirtual => AnyAccessor?.IsVirtual ?? false; public bool IsOverride => AnyAccessor?.IsOverride ?? false; public bool IsOverridable => AnyAccessor?.IsOverridable ?? false; public IModule ParentModule => module; public ICompilation Compilation => module.Compilation; public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; public string Namespace => DeclaringType?.Namespace ?? string.Empty; public override bool Equals(object obj) { if (obj is MetadataEvent ev) { return handle == ev.handle && module.MetadataFile == ev.module.MetadataFile; } return false; } public override int GetHashCode() { return 0x7937039a ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return Equals(obj); } public IMember Specialize(TypeParameterSubstitution substitution) { return SpecializedEvent.Create(this, substitution); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Field definition backed by System.Reflection.Metadata /// sealed class MetadataField : IField { readonly MetadataModule module; readonly FieldDefinitionHandle handle; readonly FieldAttributes attributes; // lazy-loaded fields: ITypeDefinition declaringType; string name; object constantValue; IType type; bool isVolatile; // initialized together with this.type // this can't be bool? as bool? is not thread-safe from torn reads byte decimalConstantState; internal MetadataField(MetadataModule module, FieldDefinitionHandle handle) { Debug.Assert(module != null); Debug.Assert(!handle.IsNil); this.module = module; this.handle = handle; var def = module.metadata.GetFieldDefinition(handle); this.attributes = def.Attributes; if ((attributes & (FieldAttributes.Static | FieldAttributes.InitOnly)) != (FieldAttributes.Static | FieldAttributes.InitOnly)) decimalConstantState = ThreeState.False; } public EntityHandle MetadataToken => handle; public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; } public string Name { get { string name = LazyInit.VolatileRead(ref this.name); if (name != null) return name; var metadata = module.metadata; var fieldDef = metadata.GetFieldDefinition(handle); return LazyInit.GetOrSet(ref this.name, metadata.GetString(fieldDef.Name)); } } public Accessibility Accessibility { get { switch (attributes & FieldAttributes.FieldAccessMask) { case FieldAttributes.Public: return Accessibility.Public; case FieldAttributes.FamANDAssem: return Accessibility.ProtectedAndInternal; case FieldAttributes.Assembly: return Accessibility.Internal; case FieldAttributes.Family: return Accessibility.Protected; case FieldAttributes.FamORAssem: return Accessibility.ProtectedOrInternal; default: return Accessibility.Private; } } } public bool IsReadOnly => (attributes & FieldAttributes.InitOnly) != 0; public bool IsStatic => (attributes & FieldAttributes.Static) != 0; SymbolKind ISymbol.SymbolKind => SymbolKind.Field; IMember IMember.MemberDefinition => this; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; // Fields can't implement interfaces: IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => EmptyList.Instance; bool IMember.IsExplicitInterfaceImplementation => false; bool IMember.IsVirtual => false; bool IMember.IsOverride => false; bool IMember.IsOverridable => false; bool IEntity.IsAbstract => false; bool IEntity.IsSealed => false; public ITypeDefinition DeclaringTypeDefinition { get { var declType = LazyInit.VolatileRead(ref this.declaringType); if (declType != null) { return declType; } else { var def = module.metadata.GetFieldDefinition(handle); return LazyInit.GetOrSet(ref this.declaringType, module.GetDefinition(def.GetDeclaringType())); } } } public IType DeclaringType => DeclaringTypeDefinition; public IModule ParentModule => module; public ICompilation Compilation => module.Compilation; public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var fieldDef = metadata.GetFieldDefinition(handle); // FieldOffsetAttribute int offset = fieldDef.GetOffset(); if (offset != -1) { b.Add(KnownAttribute.FieldOffset, KnownTypeCode.Int32, offset); } // NonSerializedAttribute if ((fieldDef.Attributes & FieldAttributes.NotSerialized) != 0) { b.Add(KnownAttribute.NonSerialized); } // SpecialName if ((fieldDef.Attributes & (FieldAttributes.SpecialName | FieldAttributes.RTSpecialName)) == FieldAttributes.SpecialName) { b.Add(KnownAttribute.SpecialName); } b.AddMarshalInfo(fieldDef.GetMarshallingDescriptor()); b.Add(fieldDef.GetCustomAttributes(), SymbolKind.Field); return b.Build(); } public bool HasAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().Any(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetFieldDefinition(handle); return b.HasAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Field); } public IAttribute GetAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().FirstOrDefault(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetFieldDefinition(handle); return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Field); } public bool ReturnTypeIsRefReadOnly { get { var def = module.metadata.GetFieldDefinition(handle); return def.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly); } } public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; public string Namespace => DeclaringType?.Namespace ?? string.Empty; public bool IsVolatile { get { if (LazyInit.VolatileRead(ref this.type) == null) { DecodeTypeAndVolatileFlag(); } return this.isVolatile; } } IType IMember.ReturnType => Type; public IType Type { get { var ty = LazyInit.VolatileRead(ref this.type); if (ty != null) { return ty; } return DecodeTypeAndVolatileFlag(); } } private IType DecodeTypeAndVolatileFlag() { var metadata = module.metadata; var fieldDef = metadata.GetFieldDefinition(handle); IType ty; try { ty = fieldDef.DecodeSignature(module.TypeProvider, new GenericContext(DeclaringType?.TypeParameters)); if (ty is ModifiedType mod && mod.Modifier.Name == "IsVolatile" && mod.Modifier.Namespace == "System.Runtime.CompilerServices") { Volatile.Write(ref this.isVolatile, true); } ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, fieldDef.GetCustomAttributes(), metadata, module.OptionsForEntity(this), DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious); } catch (BadImageFormatException) { ty = SpecialType.UnknownType; } return LazyInit.GetOrSet(ref this.type, ty); } public bool IsConst => (attributes & FieldAttributes.Literal) != 0 || (IsDecimalConstant && DecimalConstantHelper.AllowsDecimalConstants(module)); bool IsDecimalConstant { get { if (decimalConstantState == ThreeState.Unknown) { var fieldDef = module.metadata.GetFieldDefinition(handle); decimalConstantState = ThreeState.From(DecimalConstantHelper.IsDecimalConstant(module, fieldDef.GetCustomAttributes())); } return decimalConstantState == ThreeState.True; } } public object GetConstantValue(bool throwOnInvalidMetadata) { object val = LazyInit.VolatileRead(ref this.constantValue); if (val != null) return val; try { var metadata = module.metadata; var fieldDef = metadata.GetFieldDefinition(handle); if (IsDecimalConstant && DecimalConstantHelper.AllowsDecimalConstants(module)) { val = DecimalConstantHelper.GetDecimalConstantValue(module, fieldDef.GetCustomAttributes()); } else { var constantHandle = fieldDef.GetDefaultValue(); if (constantHandle.IsNil) return null; var constant = metadata.GetConstant(constantHandle); var blobReader = metadata.GetBlobReader(constant.Value); try { val = blobReader.ReadConstant(constant.TypeCode); } catch (ArgumentOutOfRangeException) { throw new BadImageFormatException($"Constant with invalid typecode: {constant.TypeCode}"); } } return LazyInit.GetOrSet(ref this.constantValue, val); } catch (BadImageFormatException) when (!throwOnInvalidMetadata) { return null; } } public override bool Equals(object obj) { if (obj is MetadataField f) { return handle == f.handle && module.MetadataFile == f.module.MetadataFile; } return false; } public override int GetHashCode() { return 0x11dda32b ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return Equals(obj); } public IMember Specialize(TypeParameterSubstitution substitution) { return SpecializedField.Create(this, substitution); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataMethod : IMethod { readonly MetadataModule module; readonly MethodDefinitionHandle handle; // eagerly loaded fields: readonly MethodAttributes attributes; readonly SymbolKind symbolKind; readonly ITypeParameter[] typeParameters; readonly EntityHandle accessorOwner; public MethodSemanticsAttributes AccessorKind { get; } public bool IsExtensionMethod { get; } bool IMethod.IsLocalFunction => false; // lazy-loaded fields: ITypeDefinition declaringType; string name; IParameter[] parameters; IType returnType; byte returnTypeIsRefReadonly = ThreeState.Unknown; byte thisIsRefReadonly = ThreeState.Unknown; bool isInitOnly; internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle) { Debug.Assert(module != null); Debug.Assert(!handle.IsNil); this.module = module; this.handle = handle; var metadata = module.metadata; var def = metadata.GetMethodDefinition(handle); this.attributes = def.Attributes; this.symbolKind = SymbolKind.Method; var (accessorOwner, semanticsAttribute) = module.MetadataFile.MethodSemanticsLookup.GetSemantics(handle); const MethodAttributes finalizerAttributes = (MethodAttributes.Virtual | MethodAttributes.Family | MethodAttributes.HideBySig); this.typeParameters = MetadataTypeParameter.Create(module, this, def.GetGenericParameters()); if (semanticsAttribute != 0 && !accessorOwner.IsNil && accessorOwner.Kind is HandleKind.PropertyDefinition or HandleKind.EventDefinition) { this.symbolKind = SymbolKind.Accessor; this.accessorOwner = accessorOwner; this.AccessorKind = semanticsAttribute; } else if ((attributes & (MethodAttributes.SpecialName | MethodAttributes.RTSpecialName)) != 0 && typeParameters.Length == 0) { string name = this.Name; if (name == ".cctor" || name == ".ctor") { this.symbolKind = SymbolKind.Constructor; } else if (name.StartsWith("op_", StringComparison.Ordinal) && CSharp.Syntax.OperatorDeclaration.GetOperatorType(name) != null) { this.symbolKind = SymbolKind.Operator; } } else if ((attributes & finalizerAttributes) == finalizerAttributes && typeParameters.Length == 0) { if (Name == "Finalize" && Parameters.Count == 0 && ReturnType.IsKnownType(KnownTypeCode.Void) && (DeclaringTypeDefinition as MetadataTypeDefinition)?.Kind == TypeKind.Class) { this.symbolKind = SymbolKind.Destructor; } } else if ((attributes & MethodAttributes.Static) != 0 && typeParameters.Length == 0) { // Operators that are explicit interface implementations are not marked // with MethodAttributes.SpecialName or MethodAttributes.RTSpecialName string name = this.Name; int index = name.LastIndexOf('.'); if (index > 0) { name = name.Substring(index + 1); if (name.StartsWith("op_", StringComparison.Ordinal) && CSharp.Syntax.OperatorDeclaration.GetOperatorType(name) != null) { this.symbolKind = SymbolKind.Operator; } } } this.IsExtensionMethod = (attributes & MethodAttributes.Static) == MethodAttributes.Static && (module.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods && def.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension); } public EntityHandle MetadataToken => handle; public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; } public string Name { get { string name = LazyInit.VolatileRead(ref this.name); if (name != null) return name; var metadata = module.metadata; var methodDef = metadata.GetMethodDefinition(handle); return LazyInit.GetOrSet(ref this.name, metadata.GetString(methodDef.Name)); } } public IReadOnlyList TypeParameters => typeParameters; IReadOnlyList IMethod.TypeArguments => typeParameters; public SymbolKind SymbolKind => symbolKind; public bool IsConstructor => symbolKind == SymbolKind.Constructor; public bool IsDestructor => symbolKind == SymbolKind.Destructor; public bool IsOperator => symbolKind == SymbolKind.Operator; public bool IsAccessor => symbolKind == SymbolKind.Accessor; public bool HasBody => module.metadata.GetMethodDefinition(handle).HasBody(); public IMember AccessorOwner { get { if (accessorOwner.IsNil) return null; if (accessorOwner.Kind == HandleKind.PropertyDefinition) return module.GetDefinition((PropertyDefinitionHandle)accessorOwner); else if (accessorOwner.Kind == HandleKind.EventDefinition) return module.GetDefinition((EventDefinitionHandle)accessorOwner); else return null; } } #region Signature (ReturnType + Parameters) public IReadOnlyList Parameters { get { var parameters = LazyInit.VolatileRead(ref this.parameters); if (parameters != null) return parameters; DecodeSignature(); return this.parameters; } } public IType ReturnType { get { var returnType = LazyInit.VolatileRead(ref this.returnType); if (returnType != null) return returnType; DecodeSignature(); return this.returnType; } } public bool IsInitOnly { get { var returnType = LazyInit.VolatileRead(ref this.returnType); if (returnType == null) DecodeSignature(); return this.isInitOnly; } } internal Nullability NullableContext { get { var methodDef = module.metadata.GetMethodDefinition(handle); return methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; } } private void DecodeSignature() { var methodDef = module.metadata.GetMethodDefinition(handle); var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters); IType returnType; IParameter[] parameters; ModifiedType mod; try { var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext); (returnType, parameters, mod) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this)); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; mod = null; } this.isInitOnly = mod is { Modifier: { Name: "IsExternalInit", Namespace: "System.Runtime.CompilerServices" } }; LazyInit.GetOrSet(ref this.returnType, returnType); LazyInit.GetOrSet(ref this.parameters, parameters); } internal static (IType returnType, IParameter[] parameters, ModifiedType returnTypeModifier) DecodeSignature( MetadataModule module, IParameterizedMember owner, MethodSignature signature, ParameterHandleCollection? parameterHandles, Nullability nullableContext, TypeSystemOptions typeSystemOptions, CustomAttributeHandleCollection? additionalReturnTypeAttributes = null) { var metadata = module.metadata; int i = 0; IParameter[] parameters = new IParameter[signature.RequiredParameterCount + (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs ? 1 : 0)]; IType parameterType; CustomAttributeHandleCollection? returnTypeAttributes = null; if (parameterHandles != null) { foreach (var parameterHandle in parameterHandles) { var par = metadata.GetParameter(parameterHandle); if (par.SequenceNumber == 0) { // "parameter" holds return type attributes. // Note: for properties, the attributes normally stored on a method's return type // are instead typically stored as normal attributes on the property. // So MetadataProperty provides a non-null value for additionalReturnTypeAttributes, // which then will be preferred over the attributes on the accessor's parameters. // However if an attribute only exists on the accessor's parameters, we still want // to process it here. returnTypeAttributes = par.GetCustomAttributes(); } else if (i < par.SequenceNumber && par.SequenceNumber <= signature.RequiredParameterCount) { // "Successive rows of the Param table that are owned by the same method shall be // ordered by increasing Sequence value - although gaps in the sequence are allowed" Debug.Assert(par.SequenceNumber <= signature.ParameterTypes.Length); Debug.Assert(par.SequenceNumber <= parameters.Length); // Fill gaps in the sequence with non-metadata parameters: while (i < par.SequenceNumber - 1) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, null, metadata, typeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, par.GetCustomAttributes(), metadata, typeSystemOptions, nullableContext); parameters[i] = new MetadataParameter(module, owner, parameterType, parameterHandle); i++; } } } while (i < signature.RequiredParameterCount) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, null, metadata, typeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { parameters[i] = new DefaultParameter(SpecialType.ArgList, name: string.Empty, owner); i++; } Debug.Assert(i == parameters.Length); var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType, module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext, additionalAttributes: additionalReturnTypeAttributes); return (returnType, parameters, signature.ReturnType as ModifiedType); } #endregion public bool IsExplicitInterfaceImplementation { get { if (Name.IndexOf('.') < 0) return false; var typeDef = ((MetadataTypeDefinition)DeclaringTypeDefinition); return typeDef.HasOverrides(handle); } } public IEnumerable ExplicitlyImplementedInterfaceMembers { get { var typeDef = ((MetadataTypeDefinition)DeclaringTypeDefinition); return typeDef.GetOverrides(handle); } } IMember IMember.MemberDefinition => this; IMethod IMethod.ReducedFrom => null; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; public ITypeDefinition DeclaringTypeDefinition { get { var declType = LazyInit.VolatileRead(ref this.declaringType); if (declType != null) { return declType; } else { var def = module.metadata.GetMethodDefinition(handle); return LazyInit.GetOrSet(ref this.declaringType, module.GetDefinition(def.GetDeclaringType())); } } } public IType DeclaringType => DeclaringTypeDefinition; public IModule ParentModule => module; public ICompilation Compilation => module.Compilation; #region Attributes IType FindInteropType(string name) { return module.Compilation.FindType(new TopLevelTypeName( "System.Runtime.InteropServices", name, 0 )); } public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetMethodDefinition(handle); MethodImplAttributes implAttributes = def.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; int methodCodeType = (int)(def.ImplAttributes & MethodImplAttributes.CodeTypeMask); #region DllImportAttribute var info = def.GetImport(); if ((attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl && !info.Module.IsNil) { var dllImport = new AttributeBuilder(module, KnownAttribute.DllImport); dllImport.AddFixedArg(KnownTypeCode.String, metadata.GetString(metadata.GetModuleReference(info.Module).Name)); var importAttrs = info.Attributes; if ((importAttrs & MethodImportAttributes.BestFitMappingDisable) == MethodImportAttributes.BestFitMappingDisable) dllImport.AddNamedArg("BestFitMapping", KnownTypeCode.Boolean, false); if ((importAttrs & MethodImportAttributes.BestFitMappingEnable) == MethodImportAttributes.BestFitMappingEnable) dllImport.AddNamedArg("BestFitMapping", KnownTypeCode.Boolean, true); CallingConvention callingConvention; switch (info.Attributes & MethodImportAttributes.CallingConventionMask) { case 0: Debug.WriteLine($"P/Invoke calling convention not set on: {this}"); callingConvention = 0; break; case MethodImportAttributes.CallingConventionCDecl: callingConvention = CallingConvention.Cdecl; break; case MethodImportAttributes.CallingConventionFastCall: callingConvention = CallingConvention.FastCall; break; case MethodImportAttributes.CallingConventionStdCall: callingConvention = CallingConvention.StdCall; break; case MethodImportAttributes.CallingConventionThisCall: callingConvention = CallingConvention.ThisCall; break; case MethodImportAttributes.CallingConventionWinApi: callingConvention = CallingConvention.Winapi; break; default: throw new NotSupportedException("unknown calling convention"); } if (callingConvention != CallingConvention.Winapi) { var callingConventionType = FindInteropType(nameof(CallingConvention)); dllImport.AddNamedArg("CallingConvention", callingConventionType, (int)callingConvention); } CharSet charSet = CharSet.None; switch (info.Attributes & MethodImportAttributes.CharSetMask) { case MethodImportAttributes.CharSetAnsi: charSet = CharSet.Ansi; break; case MethodImportAttributes.CharSetAuto: charSet = CharSet.Auto; break; case MethodImportAttributes.CharSetUnicode: charSet = CharSet.Unicode; break; } if (charSet != CharSet.None) { var charSetType = FindInteropType(nameof(CharSet)); dllImport.AddNamedArg("CharSet", charSetType, (int)charSet); } if (!info.Name.IsNil && info.Name != def.Name) { dllImport.AddNamedArg("EntryPoint", KnownTypeCode.String, metadata.GetString(info.Name)); } if ((info.Attributes & MethodImportAttributes.ExactSpelling) == MethodImportAttributes.ExactSpelling) { dllImport.AddNamedArg("ExactSpelling", KnownTypeCode.Boolean, true); } if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) { implAttributes &= ~MethodImplAttributes.PreserveSig; } else { dllImport.AddNamedArg("PreserveSig", KnownTypeCode.Boolean, false); } if ((info.Attributes & MethodImportAttributes.SetLastError) == MethodImportAttributes.SetLastError) dllImport.AddNamedArg("SetLastError", KnownTypeCode.Boolean, true); if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharDisable) == MethodImportAttributes.ThrowOnUnmappableCharDisable) dllImport.AddNamedArg("ThrowOnUnmappableChar", KnownTypeCode.Boolean, false); if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharEnable) == MethodImportAttributes.ThrowOnUnmappableCharEnable) dllImport.AddNamedArg("ThrowOnUnmappableChar", KnownTypeCode.Boolean, true); b.Add(dllImport.Build()); } #endregion #region PreserveSigAttribute if (implAttributes == MethodImplAttributes.PreserveSig && methodCodeType == 0) { b.Add(KnownAttribute.PreserveSig); implAttributes = 0; } #endregion #region MethodImplAttribute if (implAttributes != 0) { var methodImpl = new AttributeBuilder(module, KnownAttribute.MethodImpl); methodImpl.AddFixedArg(new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplOptions)), (int)implAttributes); if (methodCodeType != 0) { methodImpl.AddNamedArg("MethodCodeType", new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodCodeType)), methodCodeType); } b.Add(methodImpl.Build()); } #endregion // SpecialName if ((def.Attributes & (MethodAttributes.SpecialName | MethodAttributes.RTSpecialName)) == MethodAttributes.SpecialName && SymbolKind == SymbolKind.Method) { b.Add(KnownAttribute.SpecialName); } b.Add(def.GetCustomAttributes(), symbolKind); b.AddSecurityAttributes(def.GetDeclarativeSecurityAttributes()); return b.Build(); } public bool HasAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().Any(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetMethodDefinition(handle); return b.HasAttribute(metadata, def.GetCustomAttributes(), attribute, symbolKind); } public IAttribute GetAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().FirstOrDefault(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetMethodDefinition(handle); return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, symbolKind); } #endregion #region Return type attributes public IEnumerable GetReturnTypeAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var methodDefinition = metadata.GetMethodDefinition(handle); var parameters = methodDefinition.GetParameters(); if (parameters.Count > 0) { var retParam = metadata.GetParameter(parameters.First()); if (retParam.SequenceNumber == 0) { b.AddMarshalInfo(retParam.GetMarshallingDescriptor()); b.Add(retParam.GetCustomAttributes(), SymbolKind.ReturnType); } } return b.Build(); } public bool ReturnTypeIsRefReadOnly { get { if (returnTypeIsRefReadonly != ThreeState.Unknown) { return returnTypeIsRefReadonly == ThreeState.True; } var metadata = module.metadata; var methodDefinition = metadata.GetMethodDefinition(handle); var parameters = methodDefinition.GetParameters(); bool hasReadOnlyAttr = false; if (parameters.Count > 0) { var retParam = metadata.GetParameter(parameters.First()); if (retParam.SequenceNumber == 0) { hasReadOnlyAttr = retParam.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly); } } this.returnTypeIsRefReadonly = ThreeState.From(hasReadOnlyAttr); return hasReadOnlyAttr; } } public bool ThisIsRefReadOnly { get { if (thisIsRefReadonly != ThreeState.Unknown) { return thisIsRefReadonly == ThreeState.True; } var metadata = module.metadata; var methodDefinition = metadata.GetMethodDefinition(handle); bool hasReadOnlyAttr = DeclaringTypeDefinition?.IsReadOnly ?? false; if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyMethods) != 0) { hasReadOnlyAttr |= methodDefinition.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly); } this.thisIsRefReadonly = ThreeState.From(hasReadOnlyAttr); return hasReadOnlyAttr; } } #endregion public Accessibility Accessibility => GetAccessibility(attributes); internal static Accessibility GetAccessibility(MethodAttributes attr) { switch (attr & MethodAttributes.MemberAccessMask) { case MethodAttributes.Public: return Accessibility.Public; case MethodAttributes.Assembly: return Accessibility.Internal; case MethodAttributes.Private: return Accessibility.Private; case MethodAttributes.Family: return Accessibility.Protected; case MethodAttributes.FamANDAssem: return Accessibility.ProtectedAndInternal; case MethodAttributes.FamORAssem: return Accessibility.ProtectedOrInternal; default: return Accessibility.None; } } public bool IsStatic => (attributes & MethodAttributes.Static) != 0; public bool IsAbstract => (attributes & MethodAttributes.Abstract) != 0; public bool IsSealed => (attributes & (MethodAttributes.Abstract | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.Static)) == MethodAttributes.Final; public bool IsVirtual { get { if (IsStatic) { return (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual)) == MethodAttributes.Virtual; } else { const MethodAttributes mask = MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final; return (attributes & mask) == (MethodAttributes.Virtual | MethodAttributes.NewSlot); } } } public bool IsOverride => (attributes & (MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Static)) == MethodAttributes.Virtual; public bool IsOverridable => (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual)) != 0 && (attributes & MethodAttributes.Final) == 0; public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; public string Namespace => DeclaringType?.Namespace ?? string.Empty; public override bool Equals(object obj) { if (obj is MetadataMethod m) { return handle == m.handle && module.MetadataFile == m.module.MetadataFile; } return false; } public override int GetHashCode() { return 0x5a00d671 ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return Equals(obj); } public IMethod Specialize(TypeParameterSubstitution substitution) { return SpecializedMethod.Create(this, substitution); } IMember IMember.Specialize(TypeParameterSubstitution substitution) { return SpecializedMethod.Create(this, substitution); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataNamespace.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataNamespace : INamespace { readonly MetadataModule module; readonly NamespaceDefinition ns; public INamespace ParentNamespace { get; } public string FullName { get; } public string Name { get; } public MetadataNamespace(MetadataModule module, INamespace parent, string fullName, NamespaceDefinition ns) { Debug.Assert(module != null); Debug.Assert(fullName != null); this.module = module; this.ParentNamespace = parent; this.ns = ns; this.FullName = fullName; this.Name = module.GetString(ns.Name); } string INamespace.ExternAlias => string.Empty; INamespace[] childNamespaces; public IEnumerable ChildNamespaces { get { var children = LazyInit.VolatileRead(ref childNamespaces); if (children != null) { return children; } var nsDefs = ns.NamespaceDefinitions; children = new INamespace[nsDefs.Length]; for (int i = 0; i < children.Length; i++) { var nsHandle = nsDefs[i]; string fullName = module.metadata.GetString(nsHandle); children[i] = new MetadataNamespace(module, this, fullName, module.metadata.GetNamespaceDefinition(nsHandle)); } return LazyInit.GetOrSet(ref childNamespaces, children); } } IEnumerable INamespace.Types { get { foreach (var typeHandle in ns.TypeDefinitions) { var def = module.GetDefinition(typeHandle); if (def != null) yield return def; } } } IEnumerable INamespace.ContributingModules => new[] { module }; SymbolKind ISymbol.SymbolKind => SymbolKind.Namespace; ICompilation ICompilationProvider.Compilation => module.Compilation; INamespace INamespace.GetChildNamespace(string name) { foreach (var ns in ChildNamespaces) { if (ns.Name == name) return ns; } return null; } ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) { return module.GetTypeDefinition(FullName, name, typeParameterCount); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataParameter : IParameter { readonly MetadataModule module; readonly ParameterHandle handle; readonly ParameterAttributes attributes; public IType Type { get; } public IParameterizedMember Owner { get; } // lazy-loaded: string name; // these can't be bool? as bool? is not thread-safe from torn reads byte constantValueInSignatureState; byte decimalConstantState; internal MetadataParameter(MetadataModule module, IParameterizedMember owner, IType type, ParameterHandle handle) { this.module = module; this.Owner = owner; this.Type = type; this.handle = handle; var param = module.metadata.GetParameter(handle); this.attributes = param.Attributes; if (!IsOptional) decimalConstantState = ThreeState.False; // only optional parameters can be constants } public EntityHandle MetadataToken => handle; #region Attributes public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var parameter = metadata.GetParameter(handle); bool defaultValueAssignmentAllowed = this.IsDefaultValueAssignmentAllowed(); if (IsOptional && !defaultValueAssignmentAllowed) { b.Add(KnownAttribute.Optional); } if (!IsDecimalConstant && HasConstantValueInSignature && !defaultValueAssignmentAllowed) { b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false)); } if ((attributes & ParameterAttributes.In) == ParameterAttributes.In && ReferenceKind is not (ReferenceKind.In or ReferenceKind.RefReadOnly)) b.Add(KnownAttribute.In); if ((attributes & ParameterAttributes.Out) == ParameterAttributes.Out && ReferenceKind != ReferenceKind.Out) b.Add(KnownAttribute.Out); b.Add(parameter.GetCustomAttributes(), SymbolKind.Parameter); b.AddMarshalInfo(parameter.GetMarshallingDescriptor()); return b.Build(); } #endregion const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out; public ReferenceKind ReferenceKind => DetectRefKind(); public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0; ReferenceKind DetectRefKind() { if (Type.Kind != TypeKind.ByReference) return ReferenceKind.None; if ((attributes & inOut) == ParameterAttributes.Out) return ReferenceKind.Out; if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0) { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly)) return ReferenceKind.In; } if ((module.TypeSystemOptions & TypeSystemOptions.RefReadOnlyParameters) != 0 && (attributes & inOut) == ParameterAttributes.In) { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.RequiresLocation)) return ReferenceKind.RefReadOnly; } return ReferenceKind.Ref; } public LifetimeAnnotation Lifetime { get { if ((module.TypeSystemOptions & TypeSystemOptions.ScopedRef) == 0) { return default; } var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if ((module.TypeSystemOptions & TypeSystemOptions.ParamsCollections) != 0 && parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamCollection)) { // params collections are implicitly scoped return default; } if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef)) { return new LifetimeAnnotation { ScopedRef = true }; } return default; } } public bool IsParams { get { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (Type.Kind == TypeKind.Array) { return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamArray); } if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.ParamsCollections)) { return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamCollection); } return false; } } public string Name { get { string name = LazyInit.VolatileRead(ref this.name); if (name != null) return name; var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); return LazyInit.GetOrSet(ref this.name, metadata.GetString(parameterDef.Name)); } } bool IVariable.IsConst => false; public object GetConstantValue(bool throwOnInvalidMetadata) { try { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (IsDecimalConstant) return DecimalConstantHelper.GetDecimalConstantValue(module, parameterDef.GetCustomAttributes()); var constantHandle = parameterDef.GetDefaultValue(); if (constantHandle.IsNil) return null; var constant = metadata.GetConstant(constantHandle); var blobReader = metadata.GetBlobReader(constant.Value); try { return blobReader.ReadConstant(constant.TypeCode); } catch (ArgumentOutOfRangeException) { throw new BadImageFormatException($"Constant with invalid typecode: {constant.TypeCode}"); } } catch (BadImageFormatException) when (!throwOnInvalidMetadata) { return null; } } public bool HasConstantValueInSignature { get { if (constantValueInSignatureState == ThreeState.Unknown) { if (IsDecimalConstant) { constantValueInSignatureState = ThreeState.From(DecimalConstantHelper.AllowsDecimalConstants(module)); } else { constantValueInSignatureState = ThreeState.From(!module.metadata.GetParameter(handle).GetDefaultValue().IsNil); } } return constantValueInSignatureState == ThreeState.True; } } bool IsDecimalConstant { get { if (decimalConstantState == ThreeState.Unknown) { var parameterDef = module.metadata.GetParameter(handle); decimalConstantState = ThreeState.From(DecimalConstantHelper.IsDecimalConstant(module, parameterDef.GetCustomAttributes())); } return decimalConstantState == ThreeState.True; } } SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter; public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {DefaultParameter.ToString(this)}"; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataProperty : IProperty { const Accessibility InvalidAccessibility = (Accessibility)0xff; readonly MetadataModule module; readonly PropertyDefinitionHandle handle; readonly IMethod getter; readonly IMethod setter; readonly string name; readonly SymbolKind symbolKind; // lazy-loaded: volatile Accessibility cachedAccessiblity = InvalidAccessibility; IParameter[] parameters; IType returnType; internal MetadataProperty(MetadataModule module, PropertyDefinitionHandle handle) { Debug.Assert(module != null); Debug.Assert(!handle.IsNil); this.module = module; this.handle = handle; var metadata = module.metadata; var prop = metadata.GetPropertyDefinition(handle); var accessors = prop.GetAccessors(); getter = module.GetDefinition(accessors.Getter); setter = module.GetDefinition(accessors.Setter); name = metadata.GetString(prop.Name); // Maybe we should defer the calculation of symbolKind? if (DetermineIsIndexer(name)) { symbolKind = SymbolKind.Indexer; } else if (name.IndexOf('.') >= 0) { // explicit interface implementation var interfaceProp = this.ExplicitlyImplementedInterfaceMembers.FirstOrDefault() as IProperty; symbolKind = interfaceProp?.SymbolKind ?? SymbolKind.Property; } else { symbolKind = SymbolKind.Property; } } bool DetermineIsIndexer(string name) { if (name != (DeclaringTypeDefinition as MetadataTypeDefinition)?.DefaultMemberName) return false; return Parameters.Count > 0; } public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; } public EntityHandle MetadataToken => handle; public string Name => name; public bool CanGet => getter != null; public bool CanSet => setter != null; public IMethod Getter => getter; public IMethod Setter => setter; IMethod AnyAccessor => getter ?? setter; public bool IsIndexer => symbolKind == SymbolKind.Indexer; public SymbolKind SymbolKind => symbolKind; #region Signature (ReturnType + Parameters) public IReadOnlyList Parameters { get { var parameters = LazyInit.VolatileRead(ref this.parameters); if (parameters != null) return parameters; DecodeSignature(); return this.parameters; } } public IType ReturnType { get { var returnType = LazyInit.VolatileRead(ref this.returnType); if (returnType != null) return returnType; DecodeSignature(); return this.returnType; } } public bool ReturnTypeIsRefReadOnly { get { var propertyDef = module.metadata.GetPropertyDefinition(handle); return propertyDef.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly); } } private void DecodeSignature() { var propertyDef = module.metadata.GetPropertyDefinition(handle); var genericContext = new GenericContext(DeclaringType.TypeParameters); IType returnType; IParameter[] parameters; try { var signature = propertyDef.DecodeSignature(module.TypeProvider, genericContext); var accessors = propertyDef.GetAccessors(); var declTypeDef = this.DeclaringTypeDefinition; ParameterHandleCollection? parameterHandles; Nullability nullableContext; if (!accessors.Getter.IsNil) { var getter = module.metadata.GetMethodDefinition(accessors.Getter); parameterHandles = getter.GetParameters(); nullableContext = getter.GetCustomAttributes().GetNullableContext(module.metadata) ?? declTypeDef?.NullableContext ?? Nullability.Oblivious; } else if (!accessors.Setter.IsNil) { var setter = module.metadata.GetMethodDefinition(accessors.Setter); parameterHandles = setter.GetParameters(); nullableContext = setter.GetCustomAttributes().GetNullableContext(module.metadata) ?? declTypeDef?.NullableContext ?? Nullability.Oblivious; } else { parameterHandles = null; nullableContext = declTypeDef?.NullableContext ?? Nullability.Oblivious; } // We call OptionsForEntity() for the declaring type, not the property itself, // because the property's accessibilty isn't stored in metadata but computed. // Otherwise we'd get infinite recursion, because computing the accessibility // requires decoding the signature for the GetBaseMembers() call. // Roslyn uses the same workaround (see the NullableTypeDecoder.TransformType // call in PEPropertySymbol). var typeOptions = module.OptionsForEntity(declTypeDef); (returnType, parameters, _) = MetadataMethod.DecodeSignature( module, this, signature, parameterHandles, nullableContext, typeOptions, additionalReturnTypeAttributes: propertyDef.GetCustomAttributes()); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; } LazyInit.GetOrSet(ref this.returnType, returnType); LazyInit.GetOrSet(ref this.parameters, parameters); } #endregion public bool IsExplicitInterfaceImplementation => AnyAccessor?.IsExplicitInterfaceImplementation ?? false; public IEnumerable ExplicitlyImplementedInterfaceMembers => GetInterfaceMembersFromAccessor(AnyAccessor); internal static IEnumerable GetInterfaceMembersFromAccessor(IMethod method) { if (method == null) return EmptyList.Instance; return method.ExplicitlyImplementedInterfaceMembers.Select(m => ((IMethod)m).AccessorOwner).Where(m => m != null); } public ITypeDefinition DeclaringTypeDefinition => AnyAccessor?.DeclaringTypeDefinition; public IType DeclaringType => AnyAccessor?.DeclaringType; IMember IMember.MemberDefinition => this; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; #region Attributes public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var propertyDef = metadata.GetPropertyDefinition(handle); if (IsIndexer && Name != "Item" && !IsExplicitInterfaceImplementation) { b.Add(KnownAttribute.IndexerName, KnownTypeCode.String, Name); } // SpecialName if ((propertyDef.Attributes & (PropertyAttributes.SpecialName | PropertyAttributes.RTSpecialName)) == PropertyAttributes.SpecialName) { b.Add(KnownAttribute.SpecialName); } b.Add(propertyDef.GetCustomAttributes(), symbolKind); return b.Build(); } public bool HasAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().Any(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetPropertyDefinition(handle); return b.HasAttribute(metadata, def.GetCustomAttributes(), attribute, symbolKind); } public IAttribute GetAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().FirstOrDefault(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetPropertyDefinition(handle); return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, symbolKind); } #endregion #region Accessibility public Accessibility Accessibility { get { var acc = cachedAccessiblity; if (acc == InvalidAccessibility) return cachedAccessiblity = ComputeAccessibility(); else return acc; } } Accessibility ComputeAccessibility() { if (IsOverride && (getter == null || setter == null)) { // Overrides may override only one of the accessors, hence calculating the accessibility from // the declared accessors is not sufficient. We need to "copy" accessibility from the baseMember. foreach (var baseMember in InheritanceHelper.GetBaseMembers(this, includeImplementedInterfaces: false)) { if (!baseMember.IsOverride) { // See https://github.com/icsharpcode/ILSpy/issues/2653 // "protected internal" (ProtectedOrInternal) accessibility is "reduced" // to "protected" accessibility across assembly boundaries. if (baseMember.Accessibility == Accessibility.ProtectedOrInternal && this.ParentModule?.MetadataFile != baseMember.ParentModule?.MetadataFile) { return Accessibility.Protected; } else { return baseMember.Accessibility; } } } } return AccessibilityExtensions.Union( this.Getter?.Accessibility ?? Accessibility.None, this.Setter?.Accessibility ?? Accessibility.None); } #endregion public bool IsStatic => AnyAccessor?.IsStatic ?? false; public bool IsAbstract => AnyAccessor?.IsAbstract ?? false; public bool IsSealed => AnyAccessor?.IsSealed ?? false; public bool IsVirtual => AnyAccessor?.IsVirtual ?? false; public bool IsOverride => AnyAccessor?.IsOverride ?? false; public bool IsOverridable => AnyAccessor?.IsOverridable ?? false; public IModule ParentModule => module; public ICompilation Compilation => module.Compilation; public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; public string Namespace => DeclaringType?.Namespace ?? string.Empty; public override bool Equals(object obj) { if (obj is MetadataProperty p) { return handle == p.handle && module.MetadataFile == p.module.MetadataFile; } return false; } public override int GetHashCode() { return 0x32b6a76c ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return Equals(obj); } public IMember Specialize(TypeParameterSubstitution substitution) { return SpecializedProperty.Create(this, substitution); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Type definition backed by System.Reflection.Metadata /// sealed class MetadataTypeDefinition : ITypeDefinition { readonly MetadataModule module; readonly TypeDefinitionHandle handle; // eagerly loaded: readonly FullTypeName fullTypeName; readonly TypeAttributes attributes; public TypeKind Kind { get; } public bool IsByRefLike { get; } public bool IsReadOnly { get; } public ITypeDefinition DeclaringTypeDefinition { get; } public IReadOnlyList TypeParameters { get; } public KnownTypeCode KnownTypeCode { get; } public IType EnumUnderlyingType { get; } public bool HasExtensions { get; } public Nullability NullableContext { get; } // lazy-loaded: IMember[] members; IField[] fields; IProperty[] properties; IEvent[] events; IMethod[] methods; List directBaseTypes; bool defaultMemberNameInitialized; string defaultMemberName; internal MetadataTypeDefinition(MetadataModule module, TypeDefinitionHandle handle) { Debug.Assert(module != null); Debug.Assert(!handle.IsNil); this.module = module; this.handle = handle; var metadata = module.metadata; var td = metadata.GetTypeDefinition(handle); this.attributes = td.Attributes; this.fullTypeName = td.GetFullTypeName(metadata); this.MetadataName = metadata.GetString(td.Name); // Find DeclaringType + KnownTypeCode: if (fullTypeName.IsNested) { this.DeclaringTypeDefinition = module.GetDefinition(td.GetDeclaringType()); // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this.DeclaringTypeDefinition, this, td.GetGenericParameters()); this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? this.DeclaringTypeDefinition.NullableContext; } else { // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this, td.GetGenericParameters()); this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? module.NullableContext; var topLevelTypeName = fullTypeName.TopLevelTypeName; for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) { var ktr = KnownTypeReference.Get((KnownTypeCode)i); if (ktr != null && ktr.TypeName == topLevelTypeName) { this.KnownTypeCode = (KnownTypeCode)i; break; } } } // Find type kind: if ((attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) { this.Kind = TypeKind.Interface; } else if (td.IsEnum(metadata, out var underlyingType)) { this.Kind = TypeKind.Enum; this.EnumUnderlyingType = module.Compilation.FindType(underlyingType.ToKnownTypeCode()); } else if (td.IsValueType(metadata)) { if (KnownTypeCode == KnownTypeCode.Void) { this.Kind = TypeKind.Void; } else { this.Kind = TypeKind.Struct; this.IsByRefLike = (module.TypeSystemOptions & TypeSystemOptions.RefStructs) == TypeSystemOptions.RefStructs && td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsByRefLike); this.IsReadOnly = (module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == TypeSystemOptions.ReadOnlyStructsAndParameters && td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly); } } else if (td.IsDelegate(metadata)) { this.Kind = TypeKind.Delegate; } else { this.Kind = TypeKind.Class; this.HasExtensions = this.IsStatic && (module.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods && td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension); } } public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {fullTypeName}"; } private ExtensionInfo extensionInfo; public ExtensionInfo ExtensionInfo { get { if (!HasExtensions) return null; var extensionInfo = LazyInit.VolatileRead(ref this.extensionInfo); if (extensionInfo != null) return extensionInfo; extensionInfo = new ExtensionInfo(module, this); if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return extensionInfo; return LazyInit.GetOrSet(ref this.extensionInfo, extensionInfo); } } ITypeDefinition[] nestedTypes; public IReadOnlyList NestedTypes { get { var nestedTypes = LazyInit.VolatileRead(ref this.nestedTypes); if (nestedTypes != null) return nestedTypes; var metadata = module.metadata; var nestedTypeCollection = metadata.GetTypeDefinition(handle).GetNestedTypes(); var nestedTypeList = new List(nestedTypeCollection.Length); foreach (TypeDefinitionHandle h in nestedTypeCollection) { nestedTypeList.Add(module.GetDefinition(h)); } if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return nestedTypeList; return LazyInit.GetOrSet(ref this.nestedTypes, nestedTypeList.ToArray()); } } #region Members public IReadOnlyList Members { get { var members = LazyInit.VolatileRead(ref this.members); if (members != null) return members; members = this.Fields.Concat(this.Methods).Concat(this.Properties).Concat(this.Events).ToArray(); if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return members; return LazyInit.GetOrSet(ref this.members, members); } } public IEnumerable Fields { get { var fields = LazyInit.VolatileRead(ref this.fields); if (fields != null) return fields; var metadata = module.metadata; var fieldCollection = metadata.GetTypeDefinition(handle).GetFields(); var fieldList = new List(fieldCollection.Count); foreach (FieldDefinitionHandle h in fieldCollection) { var field = metadata.GetFieldDefinition(h); var attr = field.Attributes; if (module.IsVisible(attr)) { fieldList.Add(module.GetDefinition(h)); } } if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return fieldList; return LazyInit.GetOrSet(ref this.fields, fieldList.ToArray()); } } public IEnumerable Properties { get { var properties = LazyInit.VolatileRead(ref this.properties); if (properties != null) return properties; var metadata = module.metadata; var propertyCollection = metadata.GetTypeDefinition(handle).GetProperties(); var propertyList = new List(propertyCollection.Count); foreach (PropertyDefinitionHandle h in propertyCollection) { var property = metadata.GetPropertyDefinition(h); var accessors = property.GetAccessors(); bool getterVisible = !accessors.Getter.IsNil && module.IsVisible(metadata.GetMethodDefinition(accessors.Getter).Attributes); bool setterVisible = !accessors.Setter.IsNil && module.IsVisible(metadata.GetMethodDefinition(accessors.Setter).Attributes); if (getterVisible || setterVisible) { propertyList.Add(module.GetDefinition(h)); } } if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return propertyList; return LazyInit.GetOrSet(ref this.properties, propertyList.ToArray()); } } public IEnumerable Events { get { var events = LazyInit.VolatileRead(ref this.events); if (events != null) return events; var metadata = module.metadata; var eventCollection = metadata.GetTypeDefinition(handle).GetEvents(); var eventList = new List(eventCollection.Count); foreach (EventDefinitionHandle h in eventCollection) { var ev = metadata.GetEventDefinition(h); var accessors = ev.GetAccessors(); if (accessors.Adder.IsNil) continue; var addMethod = metadata.GetMethodDefinition(accessors.Adder); if (module.IsVisible(addMethod.Attributes)) { eventList.Add(module.GetDefinition(h)); } } if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return eventList; return LazyInit.GetOrSet(ref this.events, eventList.ToArray()); } } public IEnumerable Methods { get { var methods = LazyInit.VolatileRead(ref this.methods); if (methods != null) return methods; var metadata = module.metadata; var methodsCollection = metadata.GetTypeDefinition(handle).GetMethods(); var methodsList = new List(methodsCollection.Count); var methodSemantics = module.MetadataFile.MethodSemanticsLookup; bool hasDefaultCtor = false; foreach (MethodDefinitionHandle h in methodsCollection) { var md = metadata.GetMethodDefinition(h); if (methodSemantics.GetSemantics(h).Item2 == 0 && module.IsVisible(md.Attributes)) { IMethod method = module.GetDefinition(h); if (method.SymbolKind == SymbolKind.Constructor && !method.IsStatic && method.Parameters.Count == 0) { hasDefaultCtor = true; } methodsList.Add(method); } } if (!hasDefaultCtor && (this.Kind == TypeKind.Struct || this.Kind == TypeKind.Enum)) { methodsList.Add(FakeMethod.CreateDummyConstructor(Compilation, this, Accessibility.Public)); } if ((module.TypeSystemOptions & TypeSystemOptions.Uncached) != 0) return methodsList; return LazyInit.GetOrSet(ref this.methods, methodsList.ToArray()); } } #endregion public IType DeclaringType => DeclaringTypeDefinition; public bool? IsReferenceType { get { switch (Kind) { case TypeKind.Struct: case TypeKind.Enum: case TypeKind.Void: return false; default: return true; } } } public int TypeParameterCount => TypeParameters.Count; IReadOnlyList IType.TypeArguments => TypeParameters; Nullability IType.Nullability => Nullability.Oblivious; public IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious || IsReferenceType == false) return this; else return new NullabilityAnnotatedType(this, nullability); } public IEnumerable DirectBaseTypes { get { var baseTypes = LazyInit.VolatileRead(ref this.directBaseTypes); if (baseTypes != null) return baseTypes; var metadata = module.metadata; var td = metadata.GetTypeDefinition(handle); var context = new GenericContext(TypeParameters); var interfaceImplCollection = td.GetInterfaceImplementations(); baseTypes = new List(1 + interfaceImplCollection.Count); IType baseType = null; try { EntityHandle baseTypeHandle = td.BaseType; if (!baseTypeHandle.IsNil) { baseType = module.ResolveType(baseTypeHandle, context, metadata.GetCustomAttributes(this.handle), Nullability.Oblivious); } } catch (BadImageFormatException) { baseType = SpecialType.UnknownType; } if (baseType != null) { baseTypes.Add(baseType); } else if (Kind == TypeKind.Interface) { // td.BaseType.IsNil is always true for interfaces, // but the type system expects every interface to derive from System.Object as well. baseTypes.Add(Compilation.FindType(KnownTypeCode.Object)); } foreach (var h in interfaceImplCollection) { var iface = metadata.GetInterfaceImplementation(h); baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes(), Nullability.Oblivious)); } return LazyInit.GetOrSet(ref this.directBaseTypes, baseTypes); } } public EntityHandle MetadataToken => handle; public string MetadataName { get; } public FullTypeName FullTypeName => fullTypeName; public string Name => fullTypeName.Name; public IModule ParentModule => module; #region Type Attributes public IEnumerable GetAttributes() { var b = new AttributeListBuilder(module); var metadata = module.metadata; var typeDefinition = metadata.GetTypeDefinition(handle); // SerializableAttribute if ((typeDefinition.Attributes & TypeAttributes.Serializable) != 0) b.Add(KnownAttribute.Serializable); // ComImportAttribute if ((typeDefinition.Attributes & TypeAttributes.Import) != 0) b.Add(KnownAttribute.ComImport); // SpecialName if ((typeDefinition.Attributes & (TypeAttributes.SpecialName | TypeAttributes.RTSpecialName)) == TypeAttributes.SpecialName) { b.Add(KnownAttribute.SpecialName); } #region StructLayoutAttribute LayoutKind layoutKind = LayoutKind.Auto; switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { case TypeAttributes.SequentialLayout: layoutKind = LayoutKind.Sequential; break; case TypeAttributes.ExplicitLayout: layoutKind = LayoutKind.Explicit; break; } CharSet charSet = CharSet.None; switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { case TypeAttributes.AnsiClass: charSet = CharSet.Ansi; break; case TypeAttributes.AutoClass: charSet = CharSet.Auto; break; case TypeAttributes.UnicodeClass: charSet = CharSet.Unicode; break; } var layout = typeDefinition.GetLayout(); LayoutKind defaultLayoutKind = Kind == TypeKind.Struct ? LayoutKind.Sequential : LayoutKind.Auto; if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || layout.PackingSize > 0 || layout.Size > 0) { var structLayout = new AttributeBuilder(module, KnownAttribute.StructLayout); structLayout.AddFixedArg( new TopLevelTypeName("System.Runtime.InteropServices", "LayoutKind"), (int)layoutKind); if (charSet != CharSet.Ansi) { var charSetType = Compilation.FindType(new TopLevelTypeName("System.Runtime.InteropServices", "CharSet")); structLayout.AddNamedArg("CharSet", charSetType, (int)charSet); } if (layout.PackingSize > 0) { structLayout.AddNamedArg("Pack", KnownTypeCode.Int32, (int)layout.PackingSize); } if (layout.Size > 0) { structLayout.AddNamedArg("Size", KnownTypeCode.Int32, (int)layout.Size); } b.Add(structLayout.Build()); } #endregion b.Add(typeDefinition.GetCustomAttributes(), SymbolKind.TypeDefinition); b.AddSecurityAttributes(typeDefinition.GetDeclarativeSecurityAttributes()); return b.Build(); } public bool HasAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().Any(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetTypeDefinition(handle); return b.HasAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.TypeDefinition); } public IAttribute GetAttribute(KnownAttribute attribute) { if (!attribute.IsCustomAttribute()) { return GetAttributes().FirstOrDefault(attr => attr.AttributeType.IsKnownType(attribute)); } var b = new AttributeListBuilder(module); var metadata = module.metadata; var def = metadata.GetTypeDefinition(handle); return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.TypeDefinition); } public string DefaultMemberName { get { string defaultMemberName = LazyInit.VolatileRead(ref this.defaultMemberName); if (defaultMemberName != null || defaultMemberNameInitialized) return defaultMemberName; var metadata = module.metadata; var typeDefinition = metadata.GetTypeDefinition(handle); foreach (var h in typeDefinition.GetCustomAttributes()) { var a = metadata.GetCustomAttribute(h); if (!a.IsKnownAttribute(metadata, KnownAttribute.DefaultMember)) continue; var value = a.DecodeValue(module.TypeProvider); if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is string name) { defaultMemberName = name; break; } } defaultMemberName = LazyInit.GetOrSet(ref this.defaultMemberName, defaultMemberName); defaultMemberNameInitialized = true; return defaultMemberName; } } #endregion public Accessibility Accessibility { get { switch (attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.NotPublic: case TypeAttributes.NestedAssembly: return Accessibility.Internal; case TypeAttributes.Public: case TypeAttributes.NestedPublic: return Accessibility.Public; case TypeAttributes.NestedPrivate: return Accessibility.Private; case TypeAttributes.NestedFamily: return Accessibility.Protected; case TypeAttributes.NestedFamANDAssem: return Accessibility.ProtectedAndInternal; case TypeAttributes.NestedFamORAssem: return Accessibility.ProtectedOrInternal; default: return Accessibility.None; } } } public bool IsStatic => (attributes & (TypeAttributes.Abstract | TypeAttributes.Sealed)) == (TypeAttributes.Abstract | TypeAttributes.Sealed); public bool IsAbstract => (attributes & TypeAttributes.Abstract) != 0; public bool IsSealed => (attributes & TypeAttributes.Sealed) != 0; public SymbolKind SymbolKind => SymbolKind.TypeDefinition; public ICompilation Compilation => module.Compilation; public string FullName { get { if (DeclaringType != null) return DeclaringType.FullName + "." + Name; else if (!string.IsNullOrEmpty(this.Namespace)) return this.Namespace + "." + Name; else return Name; } } public string ReflectionName => fullTypeName.ReflectionName; public string Namespace => fullTypeName.TopLevelTypeName.Namespace; ITypeDefinition IType.GetDefinition() => this; ITypeDefinitionOrUnknown IType.GetDefinitionOrUnknown() => this; TypeParameterSubstitution IType.GetSubstitution() => TypeParameterSubstitution.Identity; public IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTypeDefinition(this); } IType IType.VisitChildren(TypeVisitor visitor) { return this; } public override bool Equals(object obj) { if (obj is MetadataTypeDefinition td) { return handle == td.handle && module.MetadataFile == td.module.MetadataFile; } return false; } public override int GetHashCode() { return 0x2e0520f2 ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } bool IEquatable.Equals(IType other) { return Equals(other); } #region GetNestedTypes public IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { const GetMemberOptions opt = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; if ((options & opt) == opt) { return GetFiltered(this.NestedTypes, filter); } else { return GetMembersHelper.GetNestedTypes(this, filter, options); } } public IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return GetMembersHelper.GetNestedTypes(this, typeArguments, filter, options); } #endregion #region GetMembers() IEnumerable GetFiltered(IEnumerable input, Predicate filter) where T : class { if (filter == null) return input; else return ApplyFilter(input, filter); } IEnumerable ApplyFilter(IEnumerable input, Predicate filter) where T : class { foreach (var member in input) { if (filter(member)) yield return member; } } public IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Methods, ExtensionMethods.And(m => !m.IsConstructor, filter)); } else { return GetMembersHelper.GetMethods(this, filter, options); } } public IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; return GetMembersHelper.GetMethods(this, typeArguments, filter, options); } public IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { if (Kind == TypeKind.Void) return EmptyList.Instance; if (ComHelper.IsComImport(this)) { IType coClass = ComHelper.GetCoClass(this); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { return coClass.GetConstructors(filter, options) .Select(m => new SpecializedMethod(m, m.Substitution) { DeclaringType = this }); } } return EmptyList.Instance; } if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Methods, ExtensionMethods.And(m => m.IsConstructor && !m.IsStatic, filter)); } else { return GetMembersHelper.GetConstructors(this, filter, options); } } public IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Properties, filter); } else { return GetMembersHelper.GetProperties(this, filter, options); } } public IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Fields, filter); } else { return GetMembersHelper.GetFields(this, filter, options); } } public IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Events, filter); } else { return GetMembersHelper.GetEvents(this, filter, options); } } public IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Members, filter); } else { return GetMembersHelper.GetMembers(this, filter, options); } } public IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if (Kind == TypeKind.Void) return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFilteredAccessors(filter); } else { return GetMembersHelper.GetAccessors(this, filter, options); } } IEnumerable GetFilteredAccessors(Predicate filter) { foreach (var prop in this.Properties) { var getter = prop.Getter; if (getter != null && (filter == null || filter(getter))) yield return getter; var setter = prop.Setter; if (setter != null && (filter == null || filter(setter))) yield return setter; } foreach (var ev in this.Events) { var adder = ev.AddAccessor; if (adder != null && (filter == null || filter(adder))) yield return adder; var remover = ev.RemoveAccessor; if (remover != null && (filter == null || filter(remover))) yield return remover; var invoker = ev.InvokeAccessor; if (invoker != null && (filter == null || filter(invoker))) yield return remover; } } #endregion #region GetOverrides internal IEnumerable GetOverrides(MethodDefinitionHandle method) { var metadata = module.metadata; var td = metadata.GetTypeDefinition(handle); foreach (var implHandle in td.GetMethodImplementations()) { var impl = metadata.GetMethodImplementation(implHandle); if (impl.MethodBody == method) yield return module.ResolveMethod(impl.MethodDeclaration, new GenericContext(this.TypeParameters)); } } internal bool HasOverrides(MethodDefinitionHandle method) { var metadata = module.metadata; var td = metadata.GetTypeDefinition(handle); foreach (var implHandle in td.GetMethodImplementations()) { var impl = metadata.GetMethodImplementation(implHandle); if (impl.MethodBody == method) return true; } return false; } #endregion #region IsRecord byte isRecord = ThreeState.Unknown; public bool IsRecord { get { if (isRecord == ThreeState.Unknown) { isRecord = ThreeState.From(ComputeIsRecord()); } return isRecord == ThreeState.True; } } private bool ComputeIsRecord() { if (Kind != TypeKind.Class && Kind != TypeKind.Struct) return false; bool isStruct = Kind == TypeKind.Struct; var metadata = module.metadata; var typeDef = metadata.GetTypeDefinition(handle); bool getEqualityContract = isStruct; bool toString = false; bool printMembers = false; bool getHashCode = false; bool equals = false; bool opEquality = false; bool opInequality = false; bool clone = isStruct; foreach (var methodHandle in typeDef.GetMethods()) { var method = metadata.GetMethodDefinition(methodHandle); if (metadata.StringComparer.Equals(method.Name, "Clone")) { // error CS8859: Members named 'Clone' are disallowed in records. return false; } getEqualityContract |= metadata.StringComparer.Equals(method.Name, "get_EqualityContract"); toString |= metadata.StringComparer.Equals(method.Name, "ToString"); printMembers |= metadata.StringComparer.Equals(method.Name, "PrintMembers"); getHashCode |= metadata.StringComparer.Equals(method.Name, "GetHashCode"); equals |= metadata.StringComparer.Equals(method.Name, "Equals"); opEquality |= metadata.StringComparer.Equals(method.Name, "op_Equality"); opInequality |= metadata.StringComparer.Equals(method.Name, "op_Inequality"); clone |= metadata.StringComparer.Equals(method.Name, "$"); } // relaxed check for toString: // record classes may have their ToString implementation only in the base class, // so we cannot check for it here, as the type hierarchy is not yet known. // usually, the existence of a "$" and "get_EqualityContract" method should // be a good enough indicator. // in record structs we require a ToString implementation, because the PrintMembers // method needs to be called. if (isStruct && !toString) { return false; } return getEqualityContract & printMembers & getHashCode & equals & opEquality & opInequality & clone; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class MetadataTypeParameter : AbstractTypeParameter { readonly MetadataModule module; readonly GenericParameterHandle handle; readonly GenericParameterAttributes attr; // lazy-loaded: IReadOnlyList constraints; byte unmanagedConstraint = ThreeState.Unknown; const byte nullabilityNotYetLoaded = 255; byte nullabilityConstraint = nullabilityNotYetLoaded; public static ITypeParameter[] Create(MetadataModule module, ITypeDefinition copyFromOuter, IEntity owner, GenericParameterHandleCollection handles) { if (handles.Count == 0) return Empty.Array; var outerTps = copyFromOuter.TypeParameters; var tps = new ITypeParameter[handles.Count]; int i = 0; foreach (var handle in handles) { if (i < outerTps.Count) tps[i] = outerTps[i]; else tps[i] = Create(module, owner, i, handle); i++; } return tps; } public static ITypeParameter[] Create(MetadataModule module, IEntity owner, GenericParameterHandleCollection handles) { if (handles.Count == 0) return Empty.Array; var tps = new ITypeParameter[handles.Count]; int i = 0; foreach (var handle in handles) { tps[i] = Create(module, owner, i, handle); i++; } return tps; } public static MetadataTypeParameter Create(MetadataModule module, IEntity owner, int index, GenericParameterHandle handle) { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); Debug.Assert(gp.Index == index); return new MetadataTypeParameter(module, owner, index, module.GetString(gp.Name), handle, gp.Attributes); } private MetadataTypeParameter(MetadataModule module, IEntity owner, int index, string name, GenericParameterHandle handle, GenericParameterAttributes attr) : base(owner, index, name, GetVariance(attr)) { this.module = module; this.handle = handle; this.attr = attr; } private static VarianceModifier GetVariance(GenericParameterAttributes attr) { switch (attr & GenericParameterAttributes.VarianceMask) { case GenericParameterAttributes.Contravariant: return VarianceModifier.Contravariant; case GenericParameterAttributes.Covariant: return VarianceModifier.Covariant; default: return VarianceModifier.Invariant; } } public GenericParameterHandle MetadataToken => handle; public override IEnumerable GetAttributes() { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); var attributes = gp.GetCustomAttributes(); var b = new AttributeListBuilder(module, attributes.Count); b.Add(attributes, SymbolKind.TypeParameter); return b.Build(); } public override bool HasDefaultConstructorConstraint => (attr & GenericParameterAttributes.DefaultConstructorConstraint) != 0; public override bool HasReferenceTypeConstraint => (attr & GenericParameterAttributes.ReferenceTypeConstraint) != 0; public override bool HasValueTypeConstraint => (attr & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; public override bool AllowsRefLikeType => (attr & SRMExtensions.AllowByRefLike) != 0; public override bool HasUnmanagedConstraint { get { if (unmanagedConstraint == ThreeState.Unknown) { unmanagedConstraint = ThreeState.From(LoadUnmanagedConstraint()); } return unmanagedConstraint == ThreeState.True; } } private bool LoadUnmanagedConstraint() { if ((module.TypeSystemOptions & TypeSystemOptions.UnmanagedConstraints) == 0) return false; var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); return gp.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsUnmanaged); } public override Nullability NullabilityConstraint { get { if (nullabilityConstraint == nullabilityNotYetLoaded) { nullabilityConstraint = (byte)LoadNullabilityConstraint(); } return (Nullability)nullabilityConstraint; } } Nullability LoadNullabilityConstraint() { if (!module.ShouldDecodeNullableAttributes(Owner)) return Nullability.Oblivious; var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); foreach (var handle in gp.GetCustomAttributes()) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.Nullable)) { var attrVal = customAttribute.DecodeValue(module.TypeProvider); if (attrVal.FixedArguments.Length == 1) { if (attrVal.FixedArguments[0].Value is byte b && b <= 2) { return (Nullability)b; } } } } if (Owner is MetadataMethod method) { return method.NullableContext; } else if (Owner is ITypeDefinition td) { return td.NullableContext; } else { return Nullability.Oblivious; } } public override IReadOnlyList TypeConstraints { get { var constraints = LazyInit.VolatileRead(ref this.constraints); if (constraints == null) { constraints = LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); } return constraints; } } private IReadOnlyList DecodeConstraints() { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); Nullability nullableContext; if (Owner is ITypeDefinition typeDef) { nullableContext = typeDef.NullableContext; } else if (Owner is MetadataMethod method) { nullableContext = method.NullableContext; } else { nullableContext = Nullability.Oblivious; } var constraintHandleCollection = gp.GetConstraints(); var result = new List(constraintHandleCollection.Count + 1); bool hasNonInterfaceConstraint = false; foreach (var constraintHandle in constraintHandleCollection) { var constraint = metadata.GetGenericParameterConstraint(constraintHandle); var attrs = constraint.GetCustomAttributes(); var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), attrs, nullableContext); if (attrs.Count == 0) { result.Add(new TypeConstraint(ty)); } else { AttributeListBuilder b = new AttributeListBuilder(module); b.Add(attrs, SymbolKind.Constraint); result.Add(new TypeConstraint(ty, b.Build())); } hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface); } if (this.HasValueTypeConstraint) { result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.ValueType))); } else if (!hasNonInterfaceConstraint) { result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.Object))); } return result; } public override int GetHashCode() { return 0x51fc5b83 ^ module.MetadataFile.GetHashCode() ^ handle.GetHashCode(); } public override bool Equals(IType other) { return other is MetadataTypeParameter tp && handle == tp.handle && module.MetadataFile == tp.module.MetadataFile; } public override string ToString() { return $"{MetadataTokens.GetToken(handle):X8} {ReflectionName}"; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// An artificial "assembly" that contains all known types () and no other types. /// It does not contain any members. /// public sealed class MinimalCorlib : IModule { /// /// Minimal corlib instance containing all known types. /// public static readonly IModuleReference Instance = new CorlibModuleReference(KnownTypeReference.AllKnownTypes); public static IModuleReference CreateWithTypes(IEnumerable types) { return new CorlibModuleReference(types); } public ICompilation Compilation { get; } CorlibTypeDefinition[] typeDefinitions; readonly CorlibNamespace rootNamespace; readonly Version asmVersion = new Version(0, 0, 0, 0); private MinimalCorlib(ICompilation compilation, IEnumerable types) { this.Compilation = compilation; this.typeDefinitions = types.Select(ktr => new CorlibTypeDefinition(this, ktr.KnownTypeCode)).ToArray(); this.rootNamespace = new CorlibNamespace(this, null, string.Empty, string.Empty); } bool IModule.IsMainModule => Compilation.MainModule == this; string IModule.AssemblyName => "corlib"; Version IModule.AssemblyVersion => asmVersion; string IModule.FullAssemblyName => "corlib"; string ISymbol.Name => "corlib"; SymbolKind ISymbol.SymbolKind => SymbolKind.Module; Metadata.MetadataFile IModule.MetadataFile => null; INamespace IModule.RootNamespace => rootNamespace; public IEnumerable TopLevelTypeDefinitions => typeDefinitions.Where(td => td != null); public IEnumerable TypeDefinitions => TopLevelTypeDefinitions; public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { foreach (var typeDef in typeDefinitions) { if (typeDef.FullTypeName == topLevelTypeName) return typeDef; } return null; } IEnumerable IModule.GetAssemblyAttributes() => EmptyList.Instance; IEnumerable IModule.GetModuleAttributes() => EmptyList.Instance; bool IModule.InternalsVisibleTo(IModule module) { return module == this; } sealed class CorlibModuleReference : IModuleReference { readonly IEnumerable types; public CorlibModuleReference(IEnumerable types) { this.types = types; } IModule IModuleReference.Resolve(ITypeResolveContext context) { return new MinimalCorlib(context.Compilation, types); } } sealed class CorlibNamespace : INamespace { readonly MinimalCorlib corlib; internal List childNamespaces = new List(); public INamespace ParentNamespace { get; } public string FullName { get; } public string Name { get; } public CorlibNamespace(MinimalCorlib corlib, INamespace parentNamespace, string fullName, string name) { this.corlib = corlib; this.ParentNamespace = parentNamespace; this.FullName = fullName; this.Name = name; } string INamespace.ExternAlias => string.Empty; IEnumerable INamespace.ChildNamespaces => childNamespaces; IEnumerable INamespace.Types => corlib.TopLevelTypeDefinitions.Where(td => td.Namespace == FullName); IEnumerable INamespace.ContributingModules => new[] { corlib }; SymbolKind ISymbol.SymbolKind => SymbolKind.Namespace; ICompilation ICompilationProvider.Compilation => corlib.Compilation; INamespace INamespace.GetChildNamespace(string name) { return childNamespaces.FirstOrDefault(ns => ns.Name == name); } ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) { return corlib.GetTypeDefinition(this.FullName, name, typeParameterCount); } } sealed class CorlibTypeDefinition : ITypeDefinition { readonly MinimalCorlib corlib; readonly KnownTypeCode typeCode; readonly TypeKind typeKind; public CorlibTypeDefinition(MinimalCorlib corlib, KnownTypeCode typeCode) { this.corlib = corlib; this.typeCode = typeCode; KnownTypeReference ktr = KnownTypeReference.Get(typeCode); this.typeKind = ktr.typeKind; this.MetadataName = ktr.Name + (ktr.TypeParameterCount > 0 ? "`" + ktr.TypeParameterCount : ""); } IReadOnlyList ITypeDefinition.NestedTypes => EmptyList.Instance; IReadOnlyList ITypeDefinition.Members => EmptyList.Instance; IEnumerable ITypeDefinition.Fields => EmptyList.Instance; IEnumerable ITypeDefinition.Methods => EmptyList.Instance; IEnumerable ITypeDefinition.Properties => EmptyList.Instance; IEnumerable ITypeDefinition.Events => EmptyList.Instance; KnownTypeCode ITypeDefinition.KnownTypeCode => typeCode; IType ITypeDefinition.EnumUnderlyingType => SpecialType.UnknownType; public FullTypeName FullTypeName => KnownTypeReference.Get(typeCode).TypeName; public string MetadataName { get; } ITypeDefinition IEntity.DeclaringTypeDefinition => null; IType ITypeDefinition.DeclaringType => null; IType IType.DeclaringType => null; IType IEntity.DeclaringType => null; bool ITypeDefinition.HasExtensions => false; ExtensionInfo ITypeDefinition.ExtensionInfo => null; bool ITypeDefinition.IsReadOnly => false; TypeKind IType.Kind => typeKind; bool? IType.IsReferenceType { get { switch (typeKind) { case TypeKind.Class: case TypeKind.Interface: return true; case TypeKind.Struct: case TypeKind.Enum: return false; default: return null; } } } bool IType.IsByRefLike => false; Nullability IType.Nullability => Nullability.Oblivious; Nullability ITypeDefinition.NullableContext => Nullability.Oblivious; IType IType.ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious) return this; else return new NullabilityAnnotatedType(this, nullability); } int IType.TypeParameterCount => KnownTypeReference.Get(typeCode).TypeParameterCount; IReadOnlyList IType.TypeParameters => DummyTypeParameter.GetClassTypeParameterList(KnownTypeReference.Get(typeCode).TypeParameterCount); IReadOnlyList IType.TypeArguments => DummyTypeParameter.GetClassTypeParameterList(KnownTypeReference.Get(typeCode).TypeParameterCount); IEnumerable IType.DirectBaseTypes { get { var baseType = KnownTypeReference.Get(typeCode).baseType; if (baseType != KnownTypeCode.None) return new[] { corlib.Compilation.FindType(baseType) }; else return EmptyList.Instance; } } EntityHandle IEntity.MetadataToken => MetadataTokens.TypeDefinitionHandle(0); public string Name => KnownTypeReference.Get(typeCode).Name; IModule IEntity.ParentModule => corlib; Accessibility IEntity.Accessibility => Accessibility.Public; bool IEntity.IsStatic => false; bool IEntity.IsAbstract => typeKind == TypeKind.Interface; bool IEntity.IsSealed => typeKind == TypeKind.Struct; SymbolKind ISymbol.SymbolKind => SymbolKind.TypeDefinition; ICompilation ICompilationProvider.Compilation => corlib.Compilation; string INamedElement.FullName { get { var ktr = KnownTypeReference.Get(typeCode); return ktr.Namespace + "." + ktr.Name; } } string INamedElement.ReflectionName => KnownTypeReference.Get(typeCode).TypeName.ReflectionName; string INamedElement.Namespace => KnownTypeReference.Get(typeCode).Namespace; bool IEquatable.Equals(IType other) { return this == other; } IEnumerable IType.GetAccessors(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IEntity.GetAttributes() => EmptyList.Instance; bool IEntity.HasAttribute(KnownAttribute attribute) => false; IAttribute IEntity.GetAttribute(KnownAttribute attribute) => null; IEnumerable IType.GetConstructors(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetEvents(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetFields(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetMembers(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetMethods(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetMethods(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetNestedTypes(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetNestedTypes(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } IEnumerable IType.GetProperties(Predicate filter, GetMemberOptions options) { return EmptyList.Instance; } bool ITypeDefinition.IsRecord => false; ITypeDefinition IType.GetDefinition() => this; ITypeDefinitionOrUnknown IType.GetDefinitionOrUnknown() => this; TypeParameterSubstitution IType.GetSubstitution() => TypeParameterSubstitution.Identity; IType IType.AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTypeDefinition(this); } IType IType.VisitChildren(TypeVisitor visitor) { return this; } public override string ToString() { return $"[MinimalCorlibType {typeCode}]"; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/NestedTypeReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Type reference used to reference nested types. /// [Serializable] public sealed class NestedTypeReference : ITypeReference, ISupportsInterning { readonly ITypeReference declaringTypeRef; readonly string name; readonly int additionalTypeParameterCount; readonly bool? isReferenceType; /// /// Creates a new NestedTypeReference. /// /// Reference to the declaring type. /// Name of the nested class /// Number of type parameters on the inner class (without type parameters on baseTypeRef) /// /// must be exactly the (unbound) declaring type, not a derived type, not a parameterized type. /// NestedTypeReference thus always resolves to a type definition, never to (partially) parameterized types. /// public NestedTypeReference(ITypeReference declaringTypeRef, string name, int additionalTypeParameterCount, bool? isReferenceType = null) { if (declaringTypeRef == null) throw new ArgumentNullException(nameof(declaringTypeRef)); if (name == null) throw new ArgumentNullException(nameof(name)); this.declaringTypeRef = declaringTypeRef; this.name = name; this.additionalTypeParameterCount = additionalTypeParameterCount; this.isReferenceType = isReferenceType; } public ITypeReference DeclaringTypeReference { get { return declaringTypeRef; } } public string Name { get { return name; } } public int AdditionalTypeParameterCount { get { return additionalTypeParameterCount; } } public IType Resolve(ITypeResolveContext context) { ITypeDefinition declaringType = declaringTypeRef.Resolve(context) as ITypeDefinition; if (declaringType != null) { int tpc = declaringType.TypeParameterCount; foreach (IType type in declaringType.NestedTypes) { if (type.Name == name && type.TypeParameterCount == tpc + additionalTypeParameterCount) return type; } } return new UnknownType(null, name, additionalTypeParameterCount); } public override string ToString() { if (additionalTypeParameterCount == 0) return declaringTypeRef + "+" + name; else return declaringTypeRef + "+" + name + "`" + additionalTypeParameterCount; } int ISupportsInterning.GetHashCodeForInterning() { return declaringTypeRef.GetHashCode() ^ name.GetHashCode() ^ additionalTypeParameterCount; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { NestedTypeReference o = other as NestedTypeReference; return o != null && declaringTypeRef == o.declaringTypeRef && name == o.name && additionalTypeParameterCount == o.additionalTypeParameterCount && isReferenceType == o.isReferenceType; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs ================================================ using System.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// A decorator that annotates the nullability status for a type. /// Note: ArrayType does not use a decorator, but has direct support for nullability. /// public class NullabilityAnnotatedType : DecoratedType, IType { readonly Nullability nullability; internal NullabilityAnnotatedType(IType type, Nullability nullability) : base(type) { Debug.Assert(type.Nullability == Nullability.Oblivious); Debug.Assert(nullability != Nullability.Oblivious); // Due to IType -> concrete type casts all over the type system, we can insert // the NullabilityAnnotatedType wrapper only in some limited places. Debug.Assert(type is ITypeDefinition { IsReferenceType: not false } || type.Kind == TypeKind.Dynamic || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } public Nullability Nullability => nullability; public IType TypeWithoutAnnotation => baseType; public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitNullabilityAnnotatedType(this); } public override bool Equals(IType other) { return other is NullabilityAnnotatedType nat && nat.nullability == nullability && nat.baseType.Equals(baseType); } public override IType ChangeNullability(Nullability nullability) { if (nullability == this.nullability) return this; else return baseType.ChangeNullability(nullability); } public override IType VisitChildren(TypeVisitor visitor) { IType newBase = baseType.AcceptVisitor(visitor); if (newBase != baseType) { if (newBase.Nullability == Nullability.Nullable) { // `T!` with substitution T=`U?` becomes `U?` // This happens during type substitution for generic methods. return newBase; } if (newBase.Kind == TypeKind.TypeParameter || newBase.IsReferenceType == true) { return newBase.ChangeNullability(nullability); } else { // `T!` with substitution T=`int` becomes `int`, not `int!` return newBase; } } else { return this; } } public override string ToString() { switch (nullability) { case Nullability.Nullable: return $"{baseType.ToString()}?"; case Nullability.NotNullable: return $"{baseType.ToString()}!"; default: Debug.Assert(nullability == Nullability.Oblivious); return $"{baseType.ToString()}~"; } } } public sealed class NullabilityAnnotatedTypeParameter : NullabilityAnnotatedType, ITypeParameter { readonly new ITypeParameter baseType; internal NullabilityAnnotatedTypeParameter(ITypeParameter type, Nullability nullability) : base(type, nullability) { this.baseType = type; } public ITypeParameter OriginalTypeParameter => baseType; SymbolKind ITypeParameter.OwnerType => baseType.OwnerType; IEntity ITypeParameter.Owner => baseType.Owner; int ITypeParameter.Index => baseType.Index; string ITypeParameter.Name => baseType.Name; string ISymbol.Name => baseType.Name; VarianceModifier ITypeParameter.Variance => baseType.Variance; IType ITypeParameter.EffectiveBaseClass => baseType.EffectiveBaseClass; IReadOnlyCollection ITypeParameter.EffectiveInterfaceSet => baseType.EffectiveInterfaceSet; bool ITypeParameter.HasDefaultConstructorConstraint => baseType.HasDefaultConstructorConstraint; bool ITypeParameter.HasReferenceTypeConstraint => baseType.HasReferenceTypeConstraint; bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint; bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint; bool ITypeParameter.AllowsRefLikeType => baseType.AllowsRefLikeType; Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint; IReadOnlyList ITypeParameter.TypeConstraints => baseType.TypeConstraints; SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter; IEnumerable ITypeParameter.GetAttributes() => baseType.GetAttributes(); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/PinnedType.cs ================================================ // Copyright (c) 2017 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public sealed class PinnedType : TypeWithElementType { public PinnedType(IType elementType) : base(elementType) { } public override string NameSuffix => " pinned"; public override bool? IsReferenceType => elementType.IsReferenceType; public override bool IsByRefLike => elementType.IsByRefLike; public override TypeKind Kind => TypeKind.Other; public override IType VisitChildren(TypeVisitor visitor) { var newType = elementType.AcceptVisitor(visitor); if (newType == elementType) return this; return new PinnedType(newType); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Simple compilation implementation. /// public class SimpleCompilation : ICompilation { readonly CacheManager cacheManager = new CacheManager(); IModule mainModule; KnownTypeCache knownTypeCache; IReadOnlyList assemblies; IReadOnlyList referencedAssemblies; bool initialized; INamespace rootNamespace; public SimpleCompilation(IModuleReference mainAssembly, params IModuleReference[] assemblyReferences) { Init(mainAssembly, assemblyReferences); } public SimpleCompilation(IModuleReference mainAssembly, IEnumerable assemblyReferences) { Init(mainAssembly, assemblyReferences); } protected SimpleCompilation() { } protected void Init(IModuleReference mainAssembly, IEnumerable assemblyReferences) { if (mainAssembly == null) throw new ArgumentNullException(nameof(mainAssembly)); if (assemblyReferences == null) throw new ArgumentNullException(nameof(assemblyReferences)); var context = new SimpleTypeResolveContext(this); this.mainModule = mainAssembly.Resolve(context); List assemblies = new List(); assemblies.Add(this.mainModule); List referencedAssemblies = new List(); foreach (var asmRef in assemblyReferences) { IModule asm; try { asm = asmRef.Resolve(context); } catch (InvalidOperationException) { throw new InvalidOperationException("Tried to initialize compilation with an invalid assembly reference. (Forgot to load the assembly reference ? - see CecilLoader)"); } if (asm != null && !assemblies.Contains(asm)) assemblies.Add(asm); if (asm != null && !referencedAssemblies.Contains(asm)) referencedAssemblies.Add(asm); } this.assemblies = assemblies.AsReadOnly(); this.referencedAssemblies = referencedAssemblies.AsReadOnly(); this.knownTypeCache = new KnownTypeCache(this); this.initialized = true; } public IModule MainModule { get { if (!initialized) throw new InvalidOperationException("Compilation isn't initialized yet"); return mainModule; } } public IReadOnlyList Modules { get { if (!initialized) throw new InvalidOperationException("Compilation isn't initialized yet"); return assemblies; } } public IReadOnlyList ReferencedModules { get { if (!initialized) throw new InvalidOperationException("Compilation isn't initialized yet"); return referencedAssemblies; } } public INamespace RootNamespace { get { INamespace ns = LazyInit.VolatileRead(ref this.rootNamespace); if (ns != null) { return ns; } else { if (!initialized) throw new InvalidOperationException("Compilation isn't initialized yet"); return LazyInit.GetOrSet(ref this.rootNamespace, CreateRootNamespace()); } } } protected virtual INamespace CreateRootNamespace() { // SimpleCompilation does not support extern aliases; but derived classes might. // CreateRootNamespace() is virtual so that derived classes can change the global namespace. INamespace[] namespaces = new INamespace[referencedAssemblies.Count + 1]; namespaces[0] = mainModule.RootNamespace; for (int i = 0; i < referencedAssemblies.Count; i++) { namespaces[i + 1] = referencedAssemblies[i].RootNamespace; } return new MergedNamespace(this, namespaces); } public CacheManager CacheManager { get { return cacheManager; } } public virtual INamespace GetNamespaceForExternAlias(string alias) { if (string.IsNullOrEmpty(alias)) return this.RootNamespace; // SimpleCompilation does not support extern aliases; but derived classes might. return null; } public IType FindType(KnownTypeCode typeCode) { return knownTypeCache.FindType(typeCode); } public StringComparer NameComparer { get { return StringComparer.Ordinal; } } public virtual TypeSystemOptions TypeSystemOptions => TypeSystemOptions.Default; public override string ToString() { return "[" + GetType().Name + " " + mainModule.AssemblyName + "]"; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedEvent.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a specialized IEvent (event after type substitution). /// public class SpecializedEvent : SpecializedMember, IEvent { public static IEvent Create(IEvent ev, TypeParameterSubstitution substitution) { if (TypeParameterSubstitution.Identity.Equals(substitution) || ev.DeclaringType.TypeParameterCount == 0) { return ev; } if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); return new SpecializedEvent(ev, substitution); } readonly IEvent eventDefinition; public SpecializedEvent(IEvent eventDefinition, TypeParameterSubstitution substitution) : base(eventDefinition) { this.eventDefinition = eventDefinition; AddSubstitution(substitution); } public bool CanAdd { get { return eventDefinition.CanAdd; } } public bool CanRemove { get { return eventDefinition.CanRemove; } } public bool CanInvoke { get { return eventDefinition.CanInvoke; } } IMethod addAccessor, removeAccessor, invokeAccessor; public IMethod AddAccessor { get { return WrapAccessor(ref this.addAccessor, eventDefinition.AddAccessor); } } public IMethod RemoveAccessor { get { return WrapAccessor(ref this.removeAccessor, eventDefinition.RemoveAccessor); } } public IMethod InvokeAccessor { get { return WrapAccessor(ref this.invokeAccessor, eventDefinition.InvokeAccessor); } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a specialized IField (field after type substitution). /// public class SpecializedField : SpecializedMember, IField { internal static IField Create(IField fieldDefinition, TypeParameterSubstitution substitution) { if (TypeParameterSubstitution.Identity.Equals(substitution) || fieldDefinition.DeclaringType.TypeParameterCount == 0) { return fieldDefinition; } if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); return new SpecializedField(fieldDefinition, substitution); } readonly IField fieldDefinition; public SpecializedField(IField fieldDefinition, TypeParameterSubstitution substitution) : base(fieldDefinition) { this.fieldDefinition = fieldDefinition; AddSubstitution(substitution); } public bool IsReadOnly => fieldDefinition.IsReadOnly; public bool ReturnTypeIsRefReadOnly => fieldDefinition.ReturnTypeIsRefReadOnly; public bool IsVolatile => fieldDefinition.IsVolatile; IType IVariable.Type { get { return this.ReturnType; } } public bool IsConst { get { return fieldDefinition.IsConst; } } public object GetConstantValue(bool throwOnInvalidMetadata) { return fieldDefinition.GetConstantValue(throwOnInvalidMetadata); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Text; using System.Threading; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a SpecializedMember (a member on which type substitution has been performed). /// public abstract class SpecializedMember : IMember { protected readonly IMember baseMember; TypeParameterSubstitution substitution; IType declaringType; IType returnType; protected SpecializedMember(IMember memberDefinition) { if (memberDefinition == null) throw new ArgumentNullException(nameof(memberDefinition)); if (memberDefinition is SpecializedMember) throw new ArgumentException("Member definition cannot be specialized. Please use IMember.Specialize() instead of directly constructing SpecializedMember instances."); this.baseMember = memberDefinition; this.substitution = TypeParameterSubstitution.Identity; } /// /// Performs a substitution. This method may only be called by constructors in derived classes. /// protected void AddSubstitution(TypeParameterSubstitution newSubstitution) { Debug.Assert(declaringType == null); Debug.Assert(returnType == null); this.substitution = TypeParameterSubstitution.Compose(newSubstitution, this.substitution); } internal IMethod WrapAccessor(ref IMethod cachingField, IMethod accessorDefinition) { if (accessorDefinition == null) return null; var result = LazyInit.VolatileRead(ref cachingField); if (result != null) { return result; } else { var sm = accessorDefinition.Specialize(substitution); //sm.AccessorOwner = this; return LazyInit.GetOrSet(ref cachingField, sm); } } /// /// Gets the substitution belonging to this specialized member. /// public TypeParameterSubstitution Substitution { get { return substitution; } } public IType DeclaringType { get { var result = LazyInit.VolatileRead(ref this.declaringType); if (result != null) return result; IType definitionDeclaringType = baseMember.DeclaringType; ITypeDefinition definitionDeclaringTypeDef = definitionDeclaringType as ITypeDefinition; if (definitionDeclaringTypeDef != null && definitionDeclaringType.TypeParameterCount > 0) { if (substitution.ClassTypeArguments != null && substitution.ClassTypeArguments.Count == definitionDeclaringType.TypeParameterCount) { result = new ParameterizedType(definitionDeclaringTypeDef, substitution.ClassTypeArguments); } else { result = new ParameterizedType(definitionDeclaringTypeDef, definitionDeclaringTypeDef.TypeParameters).AcceptVisitor(substitution); } } else if (definitionDeclaringType != null) { result = definitionDeclaringType.AcceptVisitor(substitution); } return LazyInit.GetOrSet(ref this.declaringType, result); } internal set { // This setter is used as an optimization when the code constructing // the SpecializedMember already knows the declaring type. Debug.Assert(this.declaringType == null); // As this setter is used only during construction before the member is published // to other threads, we don't need a volatile write. this.declaringType = value; } } public IMember MemberDefinition { get { return baseMember.MemberDefinition; } } public IType ReturnType { get { var result = LazyInit.VolatileRead(ref this.returnType); if (result != null) return result; else return LazyInit.GetOrSet(ref this.returnType, baseMember.ReturnType.AcceptVisitor(substitution)); } protected set { // This setter is used for LiftedUserDefinedOperator, a special case of specialized member // (not a normal type parameter substitution). // As this setter is used only during construction before the member is published // to other threads, we don't need a volatile write. this.returnType = value; } } public System.Reflection.Metadata.EntityHandle MetadataToken => baseMember.MetadataToken; public bool IsVirtual { get { return baseMember.IsVirtual; } } public bool IsOverride { get { return baseMember.IsOverride; } } public bool IsOverridable { get { return baseMember.IsOverridable; } } public SymbolKind SymbolKind { get { return baseMember.SymbolKind; } } public ITypeDefinition DeclaringTypeDefinition { get { return baseMember.DeclaringTypeDefinition; } } IEnumerable IEntity.GetAttributes() => baseMember.GetAttributes(); bool IEntity.HasAttribute(KnownAttribute attribute) => baseMember.HasAttribute(attribute); IAttribute IEntity.GetAttribute(KnownAttribute attribute) => baseMember.GetAttribute(attribute); public IEnumerable ExplicitlyImplementedInterfaceMembers { get { // Note: if the interface is generic, then the interface members should already be specialized, // so we only need to append our substitution. return baseMember.ExplicitlyImplementedInterfaceMembers.Select(m => m.Specialize(substitution)); } } public bool IsExplicitInterfaceImplementation { get { return baseMember.IsExplicitInterfaceImplementation; } } public Accessibility Accessibility { get { return baseMember.Accessibility; } } public bool IsStatic { get { return baseMember.IsStatic; } } public bool IsAbstract { get { return baseMember.IsAbstract; } } public bool IsSealed { get { return baseMember.IsSealed; } } public string FullName { get { return baseMember.FullName; } } public string Name { get { return baseMember.Name; } } public string Namespace { get { return baseMember.Namespace; } } public string ReflectionName { get { return baseMember.ReflectionName; } } public ICompilation Compilation { get { return baseMember.Compilation; } } public IModule ParentModule { get { return baseMember.ParentModule; } } public virtual IMember Specialize(TypeParameterSubstitution newSubstitution) { return baseMember.Specialize(TypeParameterSubstitution.Compose(newSubstitution, this.substitution)); } public virtual bool Equals(IMember obj, TypeVisitor typeNormalization) { SpecializedMember other = obj as SpecializedMember; if (other == null) return false; return this.baseMember.Equals(other.baseMember, typeNormalization) && this.substitution.Equals(other.substitution, typeNormalization); } public override bool Equals(object obj) { SpecializedMember other = obj as SpecializedMember; if (other == null) return false; return this.baseMember.Equals(other.baseMember) && this.substitution.Equals(other.substitution); } public override int GetHashCode() { unchecked { return 1000000007 * baseMember.GetHashCode() + 1000000009 * substitution.GetHashCode(); } } public override string ToString() { StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); b.Append(this.DeclaringType.ToString()); b.Append('.'); b.Append(this.Name); b.Append(':'); b.Append(this.ReturnType.ToString()); b.Append(']'); return b.ToString(); } } public abstract class SpecializedParameterizedMember : SpecializedMember, IParameterizedMember { IReadOnlyList parameters; protected SpecializedParameterizedMember(IParameterizedMember memberDefinition) : base(memberDefinition) { } public IReadOnlyList Parameters { get { var result = LazyInit.VolatileRead(ref this.parameters); if (result != null) return result; else return LazyInit.GetOrSet(ref this.parameters, CreateParameters(t => t.AcceptVisitor(this.Substitution))); } protected set { // This setter is used for LiftedUserDefinedOperator, a special case of specialized member // (not a normal type parameter substitution). // As this setter is used only during construction before the member is published // to other threads, we don't need a volatile write. this.parameters = value; } } protected IParameter[] CreateParameters(Func substitution) { var paramDefs = ((IParameterizedMember)this.baseMember).Parameters; if (paramDefs.Count == 0) { return Empty.Array; } else { var parameters = new IParameter[paramDefs.Count]; for (int i = 0; i < parameters.Length; i++) { var p = paramDefs[i]; IType newType = substitution(p.Type); parameters[i] = new SpecializedParameter(p, newType, this); } return parameters; } } public override string ToString() { StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); b.Append(this.DeclaringType.ReflectionName); b.Append('.'); b.Append(this.Name); b.Append('('); for (int i = 0; i < this.Parameters.Count; i++) { if (i > 0) b.Append(", "); b.Append(this.Parameters[i].ToString()); } b.Append("):"); b.Append(this.ReturnType.ReflectionName); b.Append(']'); return b.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Reflection; using System.Text; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a specialized IMethod (e.g. after type substitution). /// public class SpecializedMethod : SpecializedParameterizedMember, IMethod { internal static IMethod Create(IMethod methodDefinition, TypeParameterSubstitution substitution) { if (TypeParameterSubstitution.Identity.Equals(substitution)) return methodDefinition; if (methodDefinition.DeclaringType is ArrayType) return new SpecializedMethod(methodDefinition, substitution); if (methodDefinition.TypeParameters.Count == 0) { if (methodDefinition.DeclaringType.TypeParameterCount == 0) return methodDefinition; if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); } return new SpecializedMethod(methodDefinition, substitution); } readonly IMethod methodDefinition; readonly ITypeParameter[] specializedTypeParameters; readonly bool isParameterized; readonly TypeParameterSubstitution substitutionWithoutSpecializedTypeParameters; public SpecializedMethod(IMethod methodDefinition, TypeParameterSubstitution substitution) : base(methodDefinition) { if (substitution == null) throw new ArgumentNullException(nameof(substitution)); this.methodDefinition = methodDefinition; this.isParameterized = substitution.MethodTypeArguments != null; if (methodDefinition.TypeParameters.Count > 0) { // The method is generic, so we need to specialize the type parameters // (for specializing the constraints, and also to set the correct Owner) specializedTypeParameters = new ITypeParameter[methodDefinition.TypeParameters.Count]; for (int i = 0; i < specializedTypeParameters.Length; i++) { specializedTypeParameters[i] = new SpecializedTypeParameter(methodDefinition.TypeParameters[i], this); } if (!isParameterized) { // Add substitution that replaces the base method's type parameters with our specialized version // but do this only if the type parameters on the baseMember have not already been substituted substitutionWithoutSpecializedTypeParameters = this.Substitution; AddSubstitution(new TypeParameterSubstitution(null, specializedTypeParameters)); } } // Add the main substitution after the method type parameter specialization. AddSubstitution(substitution); if (substitutionWithoutSpecializedTypeParameters != null) { // If we already have a substitution without specialized type parameters, update that: substitutionWithoutSpecializedTypeParameters = TypeParameterSubstitution.Compose(substitution, substitutionWithoutSpecializedTypeParameters); } else { // Otherwise just use the whole substitution, as that doesn't contain specialized type parameters // in this case. substitutionWithoutSpecializedTypeParameters = this.Substitution; } if (specializedTypeParameters != null) { // Set the substitution on the type parameters to the final composed substitution foreach (var tp in specializedTypeParameters.OfType()) { if (tp.Owner == this) tp.substitution = base.Substitution; } } } public IReadOnlyList TypeArguments { get { return this.Substitution.MethodTypeArguments ?? EmptyList.Instance; } } public IEnumerable GetReturnTypeAttributes() => methodDefinition.GetReturnTypeAttributes(); public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly; bool IMethod.IsInitOnly => methodDefinition.IsInitOnly; public IReadOnlyList TypeParameters { get { return specializedTypeParameters ?? methodDefinition.TypeParameters; } } public bool IsExtensionMethod { get { return methodDefinition.IsExtensionMethod; } } public bool IsLocalFunction { get { return methodDefinition.IsLocalFunction; } } public bool IsConstructor { get { return methodDefinition.IsConstructor; } } public bool IsDestructor { get { return methodDefinition.IsDestructor; } } public bool IsOperator { get { return methodDefinition.IsOperator; } } public bool HasBody { get { return methodDefinition.HasBody; } } public bool IsAccessor { get { return methodDefinition.IsAccessor; } } public MethodSemanticsAttributes AccessorKind => methodDefinition.AccessorKind; public IMethod ReducedFrom { get { return null; } } IMember accessorOwner; public IMember AccessorOwner { get { var result = LazyInit.VolatileRead(ref accessorOwner); if (result != null) { return result; } else { var ownerDefinition = methodDefinition.AccessorOwner; if (ownerDefinition == null) return null; result = ownerDefinition.Specialize(this.Substitution); return LazyInit.GetOrSet(ref accessorOwner, result); } } internal set { accessorOwner = value; } } public override bool Equals(IMember obj, TypeVisitor typeNormalization) { SpecializedMethod other = obj as SpecializedMethod; if (other == null) return false; return this.baseMember.Equals(other.baseMember, typeNormalization) && this.substitutionWithoutSpecializedTypeParameters.Equals(other.substitutionWithoutSpecializedTypeParameters, typeNormalization); } public override bool Equals(object obj) { SpecializedMethod other = obj as SpecializedMethod; if (other == null) return false; return this.baseMember.Equals(other.baseMember) && this.substitutionWithoutSpecializedTypeParameters.Equals(other.substitutionWithoutSpecializedTypeParameters); } public override int GetHashCode() { unchecked { return 1000000013 * baseMember.GetHashCode() + 1000000009 * substitutionWithoutSpecializedTypeParameters.GetHashCode(); } } public override IMember Specialize(TypeParameterSubstitution newSubstitution) { return methodDefinition.Specialize(TypeParameterSubstitution.Compose(newSubstitution, substitutionWithoutSpecializedTypeParameters)); } IMethod IMethod.Specialize(TypeParameterSubstitution newSubstitution) { return methodDefinition.Specialize(TypeParameterSubstitution.Compose(newSubstitution, substitutionWithoutSpecializedTypeParameters)); } public override string ToString() { StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); b.Append(this.DeclaringType.ReflectionName); b.Append('.'); b.Append(this.Name); if (this.TypeArguments.Count > 0) { b.Append('['); for (int i = 0; i < this.TypeArguments.Count; i++) { if (i > 0) b.Append(", "); b.Append(this.TypeArguments[i].ToString()); } b.Append(']'); } else if (this.TypeParameters.Count > 0) { b.Append("``"); b.Append(this.TypeParameters.Count); } b.Append('('); for (int i = 0; i < this.Parameters.Count; i++) { if (i > 0) b.Append(", "); b.Append(this.Parameters[i].ToString()); } b.Append("):"); b.Append(this.ReturnType.ToString()); b.Append(']'); return b.ToString(); } sealed class SpecializedTypeParameter : AbstractTypeParameter { readonly ITypeParameter baseTp; // The substition is set at the end of SpecializedMethod constructor internal TypeVisitor substitution; public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner) : base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance) { // We don't have to consider already-specialized baseTps because // we read the baseTp directly from the unpacked memberDefinition. this.baseTp = baseTp; } public override IEnumerable GetAttributes() => baseTp.GetAttributes(); public override int GetHashCode() { return baseTp.GetHashCode() ^ this.Owner.GetHashCode(); } public override bool Equals(IType other) { // Compare the owner, not the substitution, because the substitution may contain this specialized type parameter recursively SpecializedTypeParameter o = other as SpecializedTypeParameter; return o != null && baseTp.Equals(o.baseTp) && this.Owner.Equals(o.Owner); } public override bool HasValueTypeConstraint => baseTp.HasValueTypeConstraint; public override bool HasReferenceTypeConstraint => baseTp.HasReferenceTypeConstraint; public override bool HasDefaultConstructorConstraint => baseTp.HasDefaultConstructorConstraint; public override bool HasUnmanagedConstraint => baseTp.HasUnmanagedConstraint; public override bool AllowsRefLikeType => baseTp.AllowsRefLikeType; public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint; IReadOnlyList typeConstraints; public override IReadOnlyList TypeConstraints { get { var typeConstraints = LazyInit.VolatileRead(ref this.typeConstraints); if (typeConstraints == null) { typeConstraints = baseTp.TypeConstraints.SelectReadOnlyArray(c => new TypeConstraint(c.Type.AcceptVisitor(substitution), c.Attributes)); typeConstraints = LazyInit.GetOrSet(ref this.typeConstraints, typeConstraints); } return typeConstraints; } } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { sealed class SpecializedParameter : IParameter { readonly IParameter baseParameter; readonly IType newType; readonly IParameterizedMember newOwner; public SpecializedParameter(IParameter baseParameter, IType newType, IParameterizedMember newOwner) { Debug.Assert(baseParameter != null && newType != null); this.baseParameter = baseParameter; this.newType = newType; this.newOwner = newOwner; } IEnumerable IParameter.GetAttributes() => baseParameter.GetAttributes(); ReferenceKind IParameter.ReferenceKind => baseParameter.ReferenceKind; bool IParameter.IsParams => baseParameter.IsParams; bool IParameter.IsOptional => baseParameter.IsOptional; bool IParameter.HasConstantValueInSignature => baseParameter.HasConstantValueInSignature; IParameterizedMember IParameter.Owner => newOwner; string IVariable.Name => baseParameter.Name; string ISymbol.Name => baseParameter.Name; IType IVariable.Type => newType; bool IVariable.IsConst => baseParameter.IsConst; object IVariable.GetConstantValue(bool throwOnInvalidMetadata) => baseParameter.GetConstantValue(throwOnInvalidMetadata); SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter; public LifetimeAnnotation Lifetime => baseParameter.Lifetime; public override string ToString() { return DefaultParameter.ToString(this); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a specialized IProperty (property after type substitution). /// public class SpecializedProperty : SpecializedParameterizedMember, IProperty { internal static IProperty Create(IProperty propertyDefinition, TypeParameterSubstitution substitution) { if (TypeParameterSubstitution.Identity.Equals(substitution) || propertyDefinition.DeclaringType.TypeParameterCount == 0) { return propertyDefinition; } if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); return new SpecializedProperty(propertyDefinition, substitution); } readonly IProperty propertyDefinition; public SpecializedProperty(IProperty propertyDefinition, TypeParameterSubstitution substitution) : base(propertyDefinition) { this.propertyDefinition = propertyDefinition; AddSubstitution(substitution); } public bool CanGet { get { return propertyDefinition.CanGet; } } public bool CanSet { get { return propertyDefinition.CanSet; } } IMethod getter, setter; public IMethod Getter { get { return WrapAccessor(ref this.getter, propertyDefinition.Getter); } } public IMethod Setter { get { return WrapAccessor(ref this.setter, propertyDefinition.Setter); } } public bool IsIndexer { get { return propertyDefinition.IsIndexer; } } public bool ReturnTypeIsRefReadOnly => propertyDefinition.ReturnTypeIsRefReadOnly; } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/SyntheticRangeIndexer.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Synthetic method representing a compiler-generated indexer /// with the signature 'get_Item(System.Index)' or 'get_Item(System.Range)'. /// Can also be a setter. /// Used for the "Implicit Index support"/"Implicit Range support" for the C# 8 ranges feature. /// class SyntheticRangeIndexAccessor : IMethod { /// /// The underlying method: `get_Item(int)`, `set_Item(int, T)` or `Slice(int, int)`. /// readonly IMethod underlyingMethod; readonly IType indexOrRangeType; readonly IReadOnlyList parameters; readonly bool slicing; public SyntheticRangeIndexAccessor(IMethod underlyingMethod, IType indexOrRangeType, bool slicing) { Debug.Assert(underlyingMethod != null); Debug.Assert(indexOrRangeType != null); this.underlyingMethod = underlyingMethod; this.indexOrRangeType = indexOrRangeType; this.slicing = slicing; var parameters = new List(); parameters.Add(new DefaultParameter(indexOrRangeType, "")); if (slicing) { Debug.Assert(underlyingMethod.Parameters.Count == 2); } else { parameters.AddRange(underlyingMethod.Parameters.Skip(1)); } this.parameters = parameters; } public bool IsSlicing => slicing; bool IMethod.ReturnTypeIsRefReadOnly => underlyingMethod.ReturnTypeIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => underlyingMethod.ThisIsRefReadOnly; bool IMethod.IsInitOnly => underlyingMethod.IsInitOnly; IReadOnlyList IMethod.TypeParameters => EmptyList.Instance; IReadOnlyList IMethod.TypeArguments => EmptyList.Instance; bool IMethod.IsExtensionMethod => false; bool IMethod.IsLocalFunction => false; bool IMethod.IsConstructor => false; bool IMethod.IsDestructor => false; bool IMethod.IsOperator => false; bool IMethod.HasBody => underlyingMethod.HasBody; bool IMethod.IsAccessor => underlyingMethod.IsAccessor; IMember IMethod.AccessorOwner => underlyingMethod.AccessorOwner; MethodSemanticsAttributes IMethod.AccessorKind => underlyingMethod.AccessorKind; IMethod IMethod.ReducedFrom => underlyingMethod.ReducedFrom; IReadOnlyList IParameterizedMember.Parameters => parameters; IMember IMember.MemberDefinition => underlyingMethod.MemberDefinition; IType IMember.ReturnType => underlyingMethod.ReturnType; IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => EmptyList.Instance; bool IMember.IsExplicitInterfaceImplementation => false; bool IMember.IsVirtual => underlyingMethod.IsVirtual; bool IMember.IsOverride => underlyingMethod.IsOverride; bool IMember.IsOverridable => underlyingMethod.IsOverridable; TypeParameterSubstitution IMember.Substitution => underlyingMethod.Substitution; EntityHandle IEntity.MetadataToken => underlyingMethod.MetadataToken; public string Name => underlyingMethod.Name; public IType DeclaringType => underlyingMethod.DeclaringType; ITypeDefinition IEntity.DeclaringTypeDefinition => underlyingMethod.DeclaringTypeDefinition; IModule IEntity.ParentModule => underlyingMethod.ParentModule; Accessibility IEntity.Accessibility => underlyingMethod.Accessibility; bool IEntity.IsStatic => underlyingMethod.IsStatic; bool IEntity.IsAbstract => underlyingMethod.IsAbstract; bool IEntity.IsSealed => underlyingMethod.IsSealed; SymbolKind ISymbol.SymbolKind => SymbolKind.Method; ICompilation ICompilationProvider.Compilation => underlyingMethod.Compilation; string INamedElement.FullName => underlyingMethod.FullName; string INamedElement.ReflectionName => underlyingMethod.ReflectionName; string INamedElement.Namespace => underlyingMethod.Namespace; public override bool Equals(object obj) { return obj is SyntheticRangeIndexAccessor g && this.underlyingMethod.Equals(g.underlyingMethod) && this.indexOrRangeType.Equals(g.indexOrRangeType) && this.slicing == g.slicing; } public override int GetHashCode() { return underlyingMethod.GetHashCode() ^ indexOrRangeType.GetHashCode(); } bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) { return obj is SyntheticRangeIndexAccessor g && this.underlyingMethod.Equals(g.underlyingMethod, typeNormalization) && this.indexOrRangeType.AcceptVisitor(typeNormalization).Equals(g.indexOrRangeType.AcceptVisitor(typeNormalization)); } IEnumerable IEntity.GetAttributes() => underlyingMethod.GetAttributes(); bool IEntity.HasAttribute(KnownAttribute attribute) => underlyingMethod.HasAttribute(attribute); IAttribute IEntity.GetAttribute(KnownAttribute attribute) => underlyingMethod.GetAttribute(attribute); IEnumerable IMethod.GetReturnTypeAttributes() => underlyingMethod.GetReturnTypeAttributes(); IMethod IMethod.Specialize(TypeParameterSubstitution substitution) { return new SyntheticRangeIndexAccessor(underlyingMethod.Specialize(substitution), indexOrRangeType, slicing); } IMember IMember.Specialize(TypeParameterSubstitution substitution) { return new SyntheticRangeIndexAccessor(underlyingMethod.Specialize(substitution), indexOrRangeType, slicing); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Constants used instead of bool? /// in multithreaded code, as bool? might produce torn reads. /// static class ThreeState { public const byte Unknown = 0; public const byte False = 1; public const byte True = 2; public static byte From(bool value) => value ? True : False; } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/TypeParameterReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Globalization; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { [Serializable] public sealed class TypeParameterReference : ITypeReference { static readonly TypeParameterReference[] classTypeParameterReferences = new TypeParameterReference[8]; static readonly TypeParameterReference[] methodTypeParameterReferences = new TypeParameterReference[8]; /// /// Creates a type parameter reference. /// For common type parameter references, this method may return a shared instance. /// public static TypeParameterReference Create(SymbolKind ownerType, int index) { if (index >= 0 && index < 8 && (ownerType == SymbolKind.TypeDefinition || ownerType == SymbolKind.Method)) { TypeParameterReference[] arr = (ownerType == SymbolKind.TypeDefinition) ? classTypeParameterReferences : methodTypeParameterReferences; TypeParameterReference result = LazyInit.VolatileRead(ref arr[index]); if (result == null) { result = LazyInit.GetOrSet(ref arr[index], new TypeParameterReference(ownerType, index)); } return result; } else { return new TypeParameterReference(ownerType, index); } } readonly SymbolKind ownerType; readonly int index; public int Index { get { return index; } } public TypeParameterReference(SymbolKind ownerType, int index) { this.ownerType = ownerType; this.index = index; } public IType Resolve(ITypeResolveContext context) { if (ownerType == SymbolKind.Method) { IMethod method = context.CurrentMember as IMethod; if (method != null && index < method.TypeParameters.Count) { return method.TypeParameters[index]; } return DummyTypeParameter.GetMethodTypeParameter(index); } else if (ownerType == SymbolKind.TypeDefinition) { ITypeDefinition typeDef = context.CurrentTypeDefinition; if (typeDef != null && index < typeDef.TypeParameters.Count) { return typeDef.TypeParameters[index]; } return DummyTypeParameter.GetClassTypeParameter(index); } else { return SpecialType.UnknownType; } } public override string ToString() { if (ownerType == SymbolKind.Method) return "!!" + index.ToString(CultureInfo.InvariantCulture); else return "!" + index.ToString(CultureInfo.InvariantCulture); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { public abstract class TypeWithElementType : AbstractType { protected IType elementType; protected TypeWithElementType(IType elementType) { if (elementType == null) throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } public override string Name { get { return elementType.Name + NameSuffix; } } public override string Namespace { get { return elementType.Namespace; } } public override string FullName { get { return elementType.FullName + NameSuffix; } } public override string ReflectionName { get { return elementType.ReflectionName + NameSuffix; } } public override string ToString() { return elementType.ToString() + NameSuffix; } public abstract string NameSuffix { get; } public IType ElementType { get { return elementType; } } // Force concrete implementations to override VisitChildren - the base implementation // in AbstractType assumes there are no children, but we know there is (at least) 1. public abstract override IType VisitChildren(TypeVisitor visitor); } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// An unknown type where (part) of the name is known. /// [Serializable] public class UnknownType : AbstractType, ITypeDefinitionOrUnknown, ITypeReference { readonly bool namespaceKnown; readonly FullTypeName fullTypeName; readonly bool? isReferenceType = null; /// /// Creates a new unknown type. /// /// Namespace name, if known. Can be null if unknown. /// Name of the type, must not be null. /// Type parameter count, zero if unknown. public UnknownType(string namespaceName, string name, int typeParameterCount = 0, bool? isReferenceType = null) { if (name == null) throw new ArgumentNullException(nameof(name)); this.namespaceKnown = namespaceName != null; this.fullTypeName = new TopLevelTypeName(namespaceName ?? string.Empty, name, typeParameterCount); this.isReferenceType = isReferenceType; } /// /// Creates a new unknown type. /// /// Full name of the unknown type. public UnknownType(FullTypeName fullTypeName, bool? isReferenceType = null) { this.isReferenceType = isReferenceType; if (fullTypeName.Name == null) { Debug.Assert(fullTypeName == default(FullTypeName)); this.namespaceKnown = false; this.fullTypeName = new TopLevelTypeName(string.Empty, "?", 0); } else { this.namespaceKnown = true; this.fullTypeName = fullTypeName; } } public override TypeKind Kind { get { return TypeKind.Unknown; } } IType ITypeReference.Resolve(ITypeResolveContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return this; } public override ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return this; } public override string Name { get { return fullTypeName.Name; } } public override string Namespace { get { return fullTypeName.TopLevelTypeName.Namespace; } } public override string ReflectionName { get { return namespaceKnown ? fullTypeName.ReflectionName : "?"; } } public override string FullName { get { return namespaceKnown ? fullTypeName.FullName : "?"; } } public FullTypeName FullTypeName => fullTypeName; public override int TypeParameterCount => fullTypeName.TypeParameterCount; public override IReadOnlyList TypeParameters => DummyTypeParameter.GetClassTypeParameterList(TypeParameterCount); public override IReadOnlyList TypeArguments => TypeParameters; public override bool? IsReferenceType { get { return isReferenceType; } } public override IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious || isReferenceType == false) return this; else return new NullabilityAnnotatedType(this, nullability); } public override int GetHashCode() { return (namespaceKnown ? 812571 : 12651) ^ fullTypeName.GetHashCode(); } public override bool Equals(IType other) { UnknownType o = other as UnknownType; if (o == null) return false; return this.namespaceKnown == o.namespaceKnown && this.fullTypeName == o.fullTypeName && this.isReferenceType == o.isReferenceType; } public override string ToString() { return "[UnknownType " + fullTypeName.ReflectionName + "]"; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs ================================================ // Copyright (c) 2010-2013 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 // 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; #nullable enable namespace ICSharpCode.Decompiler.TypeSystem { /// /// Provides helper methods for inheritance. /// public static class InheritanceHelper { // TODO: maybe these should be extension methods? // or even part of the interface itself? (would allow for easy caching) #region GetBaseMember /// /// Gets the base member that has the same signature. /// public static IMember? GetBaseMember(IMember member) { return GetBaseMembers(member, false).FirstOrDefault(); } /// /// Gets all base members that have the same signature. /// /// /// List of base members with the same signature. The member from the derived-most base class is returned first. /// public static IEnumerable GetBaseMembers(IMember member, bool includeImplementedInterfaces) { if (member == null) throw new ArgumentNullException(nameof(member)); if (includeImplementedInterfaces) { if (member.IsExplicitInterfaceImplementation && member.ExplicitlyImplementedInterfaceMembers.Count() == 1) { // C#-style explicit interface implementation member = member.ExplicitlyImplementedInterfaceMembers.First(); yield return member; } } // Remove generic specialization var substitution = member.Substitution; member = member.MemberDefinition; if (member.DeclaringTypeDefinition == null) { // For global methods, return empty list. (prevent SharpDevelop UDC crash 4524) yield break; } IEnumerable allBaseTypes; if (includeImplementedInterfaces) { allBaseTypes = member.DeclaringTypeDefinition.GetAllBaseTypes(); } else { allBaseTypes = member.DeclaringTypeDefinition.GetNonInterfaceBaseTypes(); } foreach (IType baseType in allBaseTypes.Reverse()) { if (baseType == member.DeclaringTypeDefinition) continue; IEnumerable baseMembers; if (member.SymbolKind == SymbolKind.Accessor) { baseMembers = baseType.GetAccessors(m => m.Name == member.Name && m.Accessibility > Accessibility.Private, GetMemberOptions.IgnoreInheritedMembers); } else { baseMembers = baseType.GetMembers(m => m.Name == member.Name && m.Accessibility > Accessibility.Private, GetMemberOptions.IgnoreInheritedMembers); } foreach (IMember baseMember in baseMembers) { System.Diagnostics.Debug.Assert(baseMember.Accessibility != Accessibility.Private); if (SignatureComparer.Ordinal.Equals(member, baseMember)) { yield return baseMember.Specialize(substitution); } } } } #endregion #region GetDerivedMember /// /// Finds the member declared in 'derivedType' that has the same signature (could override) 'baseMember'. /// public static IMember? GetDerivedMember(IMember baseMember, ITypeDefinition derivedType) { if (baseMember == null) throw new ArgumentNullException(nameof(baseMember)); if (derivedType == null) throw new ArgumentNullException(nameof(derivedType)); if (baseMember.Compilation != derivedType.Compilation) throw new ArgumentException("baseMember and derivedType must be from the same compilation"); baseMember = baseMember.MemberDefinition; bool includeInterfaces = baseMember.DeclaringTypeDefinition?.Kind == TypeKind.Interface; if (baseMember is IMethod method) { foreach (IMethod derivedMethod in derivedType.Methods) { if (derivedMethod.Name == method.Name && derivedMethod.Parameters.Count == method.Parameters.Count) { if (derivedMethod.TypeParameters.Count == method.TypeParameters.Count) { // The method could override the base method: if (GetBaseMembers(derivedMethod, includeInterfaces).Any(m => m.MemberDefinition == baseMember)) return derivedMethod; } } } } if (baseMember is IProperty property) { foreach (IProperty derivedProperty in derivedType.Properties) { if (derivedProperty.Name == property.Name && derivedProperty.Parameters.Count == property.Parameters.Count) { // The property could override the base property: if (GetBaseMembers(derivedProperty, includeInterfaces).Any(m => m.MemberDefinition == baseMember)) return derivedProperty; } } } if (baseMember is IEvent) { foreach (IEvent derivedEvent in derivedType.Events) { if (derivedEvent.Name == baseMember.Name) return derivedEvent; } } if (baseMember is IField) { foreach (IField derivedField in derivedType.Fields) { if (derivedField.Name == baseMember.Name) return derivedField; } } return null; } #endregion #region Attributes internal static IEnumerable GetAttributes(ITypeDefinition typeDef) { foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) { ITypeDefinition? baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) continue; foreach (var attr in baseTypeDef.GetAttributes()) { yield return attr; } } } internal static IAttribute? GetAttribute(ITypeDefinition typeDef, KnownAttribute attributeType) { foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) { ITypeDefinition? baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) continue; var attr = baseTypeDef.GetAttribute(attributeType); if (attr != null) return attr; } return null; } internal static IEnumerable GetAttributes(IMember member) { HashSet visitedMembers = new HashSet(); while (true) { member = member.MemberDefinition; // it's sufficient to look at the definitions if (!visitedMembers.Add(member)) { // abort if we seem to be in an infinite loop (cyclic inheritance) break; } foreach (var attr in member.GetAttributes()) { yield return attr; } if (!member.IsOverride) break; var baseMember = GetBaseMember(member); if (baseMember == null) break; member = baseMember; } } internal static IAttribute? GetAttribute(IMember member, KnownAttribute attributeType) { HashSet visitedMembers = new HashSet(); while (true) { member = member.MemberDefinition; // it's sufficient to look at the definitions if (!visitedMembers.Add(member)) { // abort if we seem to be in an infinite loop (cyclic inheritance) break; } var attr = member.GetAttribute(attributeType); if (attr != null) return attr; if (!member.IsOverride) break; var baseMember = GetBaseMember(member); if (baseMember == null) break; member = baseMember; } return null; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/IntersectionType.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents the intersection of several types. /// public class IntersectionType : AbstractType { readonly ReadOnlyCollection types; public ReadOnlyCollection Types { get { return types; } } private IntersectionType(IType[] types) { Debug.Assert(types.Length >= 2); this.types = Array.AsReadOnly(types); } public static IType Create(IEnumerable types) { IType[] arr = types.Distinct().ToArray(); foreach (IType type in arr) { if (type == null) throw new ArgumentNullException(); } if (arr.Length == 0) return SpecialType.UnknownType; else if (arr.Length == 1) return arr[0]; else return new IntersectionType(arr); } public override TypeKind Kind { get { return TypeKind.Intersection; } } public override string Name { get { StringBuilder b = new StringBuilder(); foreach (var t in types) { if (b.Length > 0) b.Append(" & "); b.Append(t.Name); } return b.ToString(); } } public override string ReflectionName { get { StringBuilder b = new StringBuilder(); foreach (var t in types) { if (b.Length > 0) b.Append(" & "); b.Append(t.ReflectionName); } return b.ToString(); } } public override bool? IsReferenceType { get { foreach (var t in types) { bool? isReferenceType = t.IsReferenceType; if (isReferenceType.HasValue) return isReferenceType.Value; } return null; } } public override int GetHashCode() { int hashCode = 0; unchecked { foreach (var t in types) { hashCode *= 7137517; hashCode += t.GetHashCode(); } } return hashCode; } public override bool Equals(IType other) { IntersectionType o = other as IntersectionType; if (o != null && types.Count == o.types.Count) { for (int i = 0; i < types.Count; i++) { if (!types[i].Equals(o.types[i])) return false; } return true; } return false; } public override IEnumerable DirectBaseTypes { get { return types; } } public override IEnumerable GetMethods(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetMethods(this, FilterNonStatic(filter), options); } public override IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetMethods(this, typeArguments, filter, options); } public override IEnumerable GetProperties(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetProperties(this, FilterNonStatic(filter), options); } public override IEnumerable GetFields(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetFields(this, FilterNonStatic(filter), options); } public override IEnumerable GetEvents(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetEvents(this, FilterNonStatic(filter), options); } public override IEnumerable GetMembers(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetMembers(this, FilterNonStatic(filter), options); } public override IEnumerable GetAccessors(Predicate filter, GetMemberOptions options) { return GetMembersHelper.GetAccessors(this, FilterNonStatic(filter), options); } static Predicate FilterNonStatic(Predicate filter) where T : class, IMember { if (filter == null) return member => !member.IsStatic; else return member => !member.IsStatic && filter(member); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents some well-known types. /// public enum KnownTypeCode { // Note: DefaultResolvedTypeDefinition uses (KnownTypeCode)-1 as special value for "not yet calculated". // The order of type codes at the beginning must correspond to those in System.TypeCode. /// /// Not one of the known types. /// None, /// object (System.Object) Object, /// System.DBNull DBNull, /// bool (System.Boolean) Boolean, /// char (System.Char) Char, /// sbyte (System.SByte) SByte, /// byte (System.Byte) Byte, /// short (System.Int16) Int16, /// ushort (System.UInt16) UInt16, /// int (System.Int32) Int32, /// uint (System.UInt32) UInt32, /// long (System.Int64) Int64, /// ulong (System.UInt64) UInt64, /// float (System.Single) Single, /// double (System.Double) Double, /// decimal (System.Decimal) Decimal, /// System.DateTime DateTime, /// string (System.String) String = 18, // String was the last element from System.TypeCode, now our additional known types start /// void (System.Void) Void, /// System.Type Type, /// System.Array Array, /// System.Attribute Attribute, /// System.ValueType ValueType, /// System.Enum Enum, /// System.Delegate Delegate, /// System.MulticastDelegate MulticastDelegate, /// System.Exception Exception, /// System.IntPtr IntPtr, /// System.UIntPtr UIntPtr, /// System.Collections.IEnumerable IEnumerable, /// System.Collections.IEnumerator IEnumerator, /// System.Collections.Generic.IEnumerable{T} IEnumerableOfT, /// System.Collections.Generic.IEnumerator{T} IEnumeratorOfT, /// System.Collections.Generic.ICollection ICollection, /// System.Collections.Generic.ICollection{T} ICollectionOfT, /// System.Collections.Generic.IList IList, /// System.Collections.Generic.IList{T} IListOfT, /// System.Collections.Generic.IReadOnlyCollection{T} IReadOnlyCollectionOfT, /// System.Collections.Generic.IReadOnlyList{T} IReadOnlyListOfT, /// System.Threading.Tasks.Task Task, /// System.Threading.Tasks.Task{T} TaskOfT, /// System.Threading.Tasks.ValueTask ValueTask, /// System.Threading.Tasks.ValueTask{T} ValueTaskOfT, /// System.Nullable{T} NullableOfT, /// System.IDisposable IDisposable, /// System.IAsyncDisposable IAsyncDisposable, /// System.Runtime.CompilerServices.INotifyCompletion INotifyCompletion, /// System.Runtime.CompilerServices.ICriticalNotifyCompletion ICriticalNotifyCompletion, /// System.TypedReference TypedReference, /// System.IFormattable IFormattable, /// System.FormattableString FormattableString, /// System.Runtime.CompilerServices.DefaultInterpolatedStringHandler DefaultInterpolatedStringHandler, /// System.Span{T} SpanOfT, /// System.ReadOnlySpan{T} ReadOnlySpanOfT, /// System.Memory{T} MemoryOfT, /// System.Runtime.CompilerServices.Unsafe Unsafe, /// System.Collections.Generic.IAsyncEnumerable{T} IAsyncEnumerableOfT, /// System.Collections.Generic.IAsyncEnumerator{T} IAsyncEnumeratorOfT, /// System.Index Index, /// System.Range Range } /// /// Contains well-known type references. /// [Serializable] public sealed class KnownTypeReference : ITypeReference { internal const int KnownTypeCodeCount = (int)KnownTypeCode.Range + 1; static readonly KnownTypeReference?[] knownTypeReferences = new KnownTypeReference?[KnownTypeCodeCount] { null, // None new KnownTypeReference(KnownTypeCode.Object, TypeKind.Class, "System", "Object", baseType: KnownTypeCode.None), new KnownTypeReference(KnownTypeCode.DBNull, TypeKind.Class, "System", "DBNull"), new KnownTypeReference(KnownTypeCode.Boolean, TypeKind.Struct, "System", "Boolean"), new KnownTypeReference(KnownTypeCode.Char, TypeKind.Struct, "System", "Char"), new KnownTypeReference(KnownTypeCode.SByte, TypeKind.Struct, "System", "SByte"), new KnownTypeReference(KnownTypeCode.Byte, TypeKind.Struct, "System", "Byte"), new KnownTypeReference(KnownTypeCode.Int16, TypeKind.Struct, "System", "Int16"), new KnownTypeReference(KnownTypeCode.UInt16, TypeKind.Struct, "System", "UInt16"), new KnownTypeReference(KnownTypeCode.Int32, TypeKind.Struct, "System", "Int32"), new KnownTypeReference(KnownTypeCode.UInt32, TypeKind.Struct, "System", "UInt32"), new KnownTypeReference(KnownTypeCode.Int64, TypeKind.Struct, "System", "Int64"), new KnownTypeReference(KnownTypeCode.UInt64, TypeKind.Struct, "System", "UInt64"), new KnownTypeReference(KnownTypeCode.Single, TypeKind.Struct, "System", "Single"), new KnownTypeReference(KnownTypeCode.Double, TypeKind.Struct, "System", "Double"), new KnownTypeReference(KnownTypeCode.Decimal, TypeKind.Struct, "System", "Decimal"), new KnownTypeReference(KnownTypeCode.DateTime, TypeKind.Struct, "System", "DateTime"), null, new KnownTypeReference(KnownTypeCode.String, TypeKind.Class, "System", "String"), new KnownTypeReference(KnownTypeCode.Void, TypeKind.Void, "System", "Void", baseType: KnownTypeCode.ValueType), new KnownTypeReference(KnownTypeCode.Type, TypeKind.Class, "System", "Type"), new KnownTypeReference(KnownTypeCode.Array, TypeKind.Class, "System", "Array"), new KnownTypeReference(KnownTypeCode.Attribute, TypeKind.Class, "System", "Attribute"), new KnownTypeReference(KnownTypeCode.ValueType, TypeKind.Class, "System", "ValueType"), new KnownTypeReference(KnownTypeCode.Enum, TypeKind.Class, "System", "Enum", baseType: KnownTypeCode.ValueType), new KnownTypeReference(KnownTypeCode.Delegate, TypeKind.Class, "System", "Delegate"), new KnownTypeReference(KnownTypeCode.MulticastDelegate, TypeKind.Class, "System", "MulticastDelegate", baseType: KnownTypeCode.Delegate), new KnownTypeReference(KnownTypeCode.Exception, TypeKind.Class, "System", "Exception"), new KnownTypeReference(KnownTypeCode.IntPtr, TypeKind.Struct, "System", "IntPtr"), new KnownTypeReference(KnownTypeCode.UIntPtr, TypeKind.Struct, "System", "UIntPtr"), new KnownTypeReference(KnownTypeCode.IEnumerable, TypeKind.Interface, "System.Collections", "IEnumerable"), new KnownTypeReference(KnownTypeCode.IEnumerator, TypeKind.Interface, "System.Collections", "IEnumerator"), new KnownTypeReference(KnownTypeCode.IEnumerableOfT, TypeKind.Interface, "System.Collections.Generic", "IEnumerable", 1), new KnownTypeReference(KnownTypeCode.IEnumeratorOfT, TypeKind.Interface, "System.Collections.Generic", "IEnumerator", 1), new KnownTypeReference(KnownTypeCode.ICollection, TypeKind.Interface, "System.Collections", "ICollection"), new KnownTypeReference(KnownTypeCode.ICollectionOfT, TypeKind.Interface, "System.Collections.Generic", "ICollection", 1), new KnownTypeReference(KnownTypeCode.IList, TypeKind.Interface, "System.Collections", "IList"), new KnownTypeReference(KnownTypeCode.IListOfT, TypeKind.Interface, "System.Collections.Generic", "IList", 1), new KnownTypeReference(KnownTypeCode.IReadOnlyCollectionOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyCollection", 1), new KnownTypeReference(KnownTypeCode.IReadOnlyListOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyList", 1), new KnownTypeReference(KnownTypeCode.Task, TypeKind.Class, "System.Threading.Tasks", "Task"), new KnownTypeReference(KnownTypeCode.TaskOfT, TypeKind.Class, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task), new KnownTypeReference(KnownTypeCode.ValueTask, TypeKind.Struct, "System.Threading.Tasks", "ValueTask"), new KnownTypeReference(KnownTypeCode.ValueTaskOfT, TypeKind.Struct, "System.Threading.Tasks", "ValueTask", 1), new KnownTypeReference(KnownTypeCode.NullableOfT, TypeKind.Struct, "System", "Nullable", 1), new KnownTypeReference(KnownTypeCode.IDisposable, TypeKind.Interface, "System", "IDisposable"), new KnownTypeReference(KnownTypeCode.IAsyncDisposable, TypeKind.Interface, "System", "IAsyncDisposable"), new KnownTypeReference(KnownTypeCode.INotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "INotifyCompletion"), new KnownTypeReference(KnownTypeCode.ICriticalNotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "ICriticalNotifyCompletion"), new KnownTypeReference(KnownTypeCode.TypedReference, TypeKind.Struct, "System", "TypedReference"), new KnownTypeReference(KnownTypeCode.IFormattable, TypeKind.Interface, "System", "IFormattable"), new KnownTypeReference(KnownTypeCode.FormattableString, TypeKind.Class, "System", "FormattableString", baseType: KnownTypeCode.IFormattable), new KnownTypeReference(KnownTypeCode.DefaultInterpolatedStringHandler, TypeKind.Struct, "System.Runtime.CompilerServices", "DefaultInterpolatedStringHandler"), new KnownTypeReference(KnownTypeCode.SpanOfT, TypeKind.Struct, "System", "Span", 1), new KnownTypeReference(KnownTypeCode.ReadOnlySpanOfT, TypeKind.Struct, "System", "ReadOnlySpan", 1), new KnownTypeReference(KnownTypeCode.MemoryOfT, TypeKind.Struct, "System", "Memory", 1), new KnownTypeReference(KnownTypeCode.Unsafe, TypeKind.Class, "System.Runtime.CompilerServices", "Unsafe", 0), new KnownTypeReference(KnownTypeCode.IAsyncEnumerableOfT, TypeKind.Interface, "System.Collections.Generic", "IAsyncEnumerable", 1), new KnownTypeReference(KnownTypeCode.IAsyncEnumeratorOfT, TypeKind.Interface, "System.Collections.Generic", "IAsyncEnumerator", 1), new KnownTypeReference(KnownTypeCode.Index, TypeKind.Struct, "System", "Index", 0), new KnownTypeReference(KnownTypeCode.Range, TypeKind.Struct, "System", "Range", 0), }; /// /// Gets the known type reference for the specified type code. /// Returns null for KnownTypeCode.None. /// public static KnownTypeReference? Get(KnownTypeCode typeCode) { return knownTypeReferences[(int)typeCode]; } public static IEnumerable AllKnownTypes { get { for (int i = 0; i < KnownTypeCodeCount; i++) { var ktr = Get((KnownTypeCode)i); if (ktr == null) continue; yield return ktr; } } } readonly KnownTypeCode knownTypeCode; readonly string namespaceName; readonly string name; readonly int typeParameterCount; internal readonly KnownTypeCode baseType; internal readonly TypeKind typeKind; private KnownTypeReference(KnownTypeCode knownTypeCode, TypeKind typeKind, string namespaceName, string name, int typeParameterCount = 0, KnownTypeCode baseType = KnownTypeCode.Object) { if (typeKind == TypeKind.Struct && baseType == KnownTypeCode.Object) baseType = KnownTypeCode.ValueType; this.knownTypeCode = knownTypeCode; this.namespaceName = namespaceName; this.name = name; this.typeParameterCount = typeParameterCount; this.typeKind = typeKind; this.baseType = baseType; } public KnownTypeCode KnownTypeCode { get { return knownTypeCode; } } public string Namespace { get { return namespaceName; } } public string Name { get { return name; } } public int TypeParameterCount { get { return typeParameterCount; } } public TopLevelTypeName TypeName => new TopLevelTypeName(namespaceName, name, typeParameterCount); public IType Resolve(ITypeResolveContext context) { return context.Compilation.FindType(knownTypeCode); } public override string ToString() { return GetCSharpNameByTypeCode(knownTypeCode) ?? (this.Namespace + "." + this.Name); } /// /// Gets the C# primitive type name from the known type code. /// Returns null if there is no primitive name for the specified type. /// public static string? GetCSharpNameByTypeCode(KnownTypeCode knownTypeCode) { switch (knownTypeCode) { case KnownTypeCode.Object: return "object"; case KnownTypeCode.Boolean: return "bool"; case KnownTypeCode.Char: return "char"; case KnownTypeCode.SByte: return "sbyte"; case KnownTypeCode.Byte: return "byte"; case KnownTypeCode.Int16: return "short"; case KnownTypeCode.UInt16: return "ushort"; case KnownTypeCode.Int32: return "int"; case KnownTypeCode.UInt32: return "uint"; case KnownTypeCode.Int64: return "long"; case KnownTypeCode.UInt64: return "ulong"; case KnownTypeCode.Single: return "float"; case KnownTypeCode.Double: return "double"; case KnownTypeCode.Decimal: return "decimal"; case KnownTypeCode.String: return "string"; case KnownTypeCode.Void: return "void"; default: return null; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Type system implementation for Metadata.PEFile. /// [DebuggerDisplay("")] public class MetadataModule : IModule { public ICompilation Compilation { get; } internal readonly MetadataReader metadata; readonly TypeSystemOptions options; internal readonly TypeProvider TypeProvider; internal readonly Nullability NullableContext; readonly MetadataNamespace rootNamespace; readonly MetadataTypeDefinition[] typeDefs; readonly MetadataField[] fieldDefs; readonly MetadataMethod[] methodDefs; readonly MetadataProperty[] propertyDefs; readonly MetadataEvent[] eventDefs; readonly IModule[] referencedAssemblies; internal MetadataModule(ICompilation compilation, MetadataFile peFile, TypeSystemOptions options) { this.Compilation = compilation; this.MetadataFile = peFile; this.metadata = peFile.Metadata; this.options = options; this.TypeProvider = new TypeProvider(this); // assembly metadata if (metadata.IsAssembly) { var asmdef = metadata.GetAssemblyDefinition(); try { this.AssemblyName = metadata.GetString(asmdef.Name); this.AssemblyVersion = asmdef.Version; this.FullAssemblyName = metadata.GetFullAssemblyName(); } catch (BadImageFormatException) { this.AssemblyName = ""; this.FullAssemblyName = ""; } } else { try { var moddef = metadata.GetModuleDefinition(); this.AssemblyName = metadata.GetString(moddef.Name); } catch (BadImageFormatException) { this.AssemblyName = ""; } this.FullAssemblyName = this.AssemblyName; } var customAttrs = metadata.GetModuleDefinition().GetCustomAttributes(); this.NullableContext = customAttrs.GetNullableContext(metadata) ?? Nullability.Oblivious; this.minAccessibilityForNRT = FindMinimumAccessibilityForNRT(metadata, customAttrs); this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); if (!options.HasFlag(TypeSystemOptions.Uncached)) { // create arrays for resolved entities, indexed by row index this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1]; this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1]; this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1]; this.referencedAssemblies = new IModule[metadata.AssemblyReferences.Count + 1]; } } internal string GetString(StringHandle name) { return metadata.GetString(name); } public TypeSystemOptions TypeSystemOptions => options; #region IModule interface public MetadataFile MetadataFile { get; } public bool IsMainModule => this == Compilation.MainModule; public string AssemblyName { get; } public Version AssemblyVersion { get; } public string FullAssemblyName { get; } string ISymbol.Name => AssemblyName; SymbolKind ISymbol.SymbolKind => SymbolKind.Module; public INamespace RootNamespace => rootNamespace; public IEnumerable TopLevelTypeDefinitions => TypeDefinitions.Where(td => td.DeclaringTypeDefinition == null); public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { var typeDefHandle = MetadataFile.GetTypeDefinition(topLevelTypeName); if (typeDefHandle.IsNil) { var forwarderHandle = MetadataFile.GetTypeForwarder(topLevelTypeName); if (!forwarderHandle.IsNil) { var forwarder = metadata.GetExportedType(forwarderHandle); return ResolveForwardedType(forwarder).GetDefinition(); } } return GetDefinition(typeDefHandle); } #endregion #region InternalsVisibleTo public bool InternalsVisibleTo(IModule module) { if (this == module) return true; foreach (string shortName in GetInternalsVisibleTo()) { if (string.Equals(module.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) return true; } return false; } string[] internalsVisibleTo; string[] GetInternalsVisibleTo() { var result = LazyInit.VolatileRead(ref this.internalsVisibleTo); if (result != null) { return result; } if (metadata.IsAssembly) { var list = new List(); foreach (var attrHandle in metadata.GetAssemblyDefinition().GetCustomAttributes()) { var attr = metadata.GetCustomAttribute(attrHandle); if (attr.IsKnownAttribute(metadata, KnownAttribute.InternalsVisibleTo)) { var attrValue = attr.DecodeValue(this.TypeProvider); if (attrValue.FixedArguments.Length == 1) { if (attrValue.FixedArguments[0].Value is string s) { list.Add(GetShortName(s)); } } } } result = list.ToArray(); } else { result = Empty.Array; } return LazyInit.GetOrSet(ref this.internalsVisibleTo, result); } static string GetShortName(string fullAssemblyName) { if (fullAssemblyName == null) return null; int pos = fullAssemblyName.IndexOf(','); if (pos < 0) return fullAssemblyName; else return fullAssemblyName.Substring(0, pos); } #endregion #region GetDefinition /// /// Gets all types in the assembly, including nested types. /// public IEnumerable TypeDefinitions { get { foreach (var tdHandle in metadata.TypeDefinitions) { yield return GetDefinition(tdHandle); } } } public ITypeDefinition GetDefinition(TypeDefinitionHandle handle) { if (handle.IsNil) return null; if (typeDefs == null) return new MetadataTypeDefinition(this, handle); int row = MetadataTokens.GetRowNumber(handle); if (row >= typeDefs.Length) HandleOutOfRange(handle); var typeDef = LazyInit.VolatileRead(ref typeDefs[row]); if (typeDef != null) return typeDef; typeDef = new MetadataTypeDefinition(this, handle); return LazyInit.GetOrSet(ref typeDefs[row], typeDef); } public IField GetDefinition(FieldDefinitionHandle handle) { if (handle.IsNil) return null; if (fieldDefs == null) return new MetadataField(this, handle); int row = MetadataTokens.GetRowNumber(handle); if (row >= fieldDefs.Length) HandleOutOfRange(handle); var field = LazyInit.VolatileRead(ref fieldDefs[row]); if (field != null) return field; field = new MetadataField(this, handle); return LazyInit.GetOrSet(ref fieldDefs[row], field); } public IMethod GetDefinition(MethodDefinitionHandle handle) { if (handle.IsNil) return null; if (methodDefs == null) return new MetadataMethod(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var method = LazyInit.VolatileRead(ref methodDefs[row]); if (method != null) return method; method = new MetadataMethod(this, handle); return LazyInit.GetOrSet(ref methodDefs[row], method); } public IProperty GetDefinition(PropertyDefinitionHandle handle) { if (handle.IsNil) return null; if (propertyDefs == null) return new MetadataProperty(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var property = LazyInit.VolatileRead(ref propertyDefs[row]); if (property != null) return property; property = new MetadataProperty(this, handle); return LazyInit.GetOrSet(ref propertyDefs[row], property); } public IEvent GetDefinition(EventDefinitionHandle handle) { if (handle.IsNil) return null; if (eventDefs == null) return new MetadataEvent(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var ev = LazyInit.VolatileRead(ref eventDefs[row]); if (ev != null) return ev; ev = new MetadataEvent(this, handle); return LazyInit.GetOrSet(ref eventDefs[row], ev); } void HandleOutOfRange(EntityHandle handle) { throw new BadImageFormatException("Handle with invalid row number."); } #endregion #region Resolve Module public IModule ResolveModule(AssemblyReferenceHandle handle) { if (handle.IsNil) return null; if (referencedAssemblies == null) return ResolveModuleUncached(handle); int row = metadata.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= referencedAssemblies.Length) HandleOutOfRange(handle); var module = LazyInit.VolatileRead(ref referencedAssemblies[row]); if (module != null) return module; module = ResolveModuleUncached(handle); return LazyInit.GetOrSet(ref referencedAssemblies[row], module); } IModule ResolveModuleUncached(AssemblyReferenceHandle handle) { var asmRef = new Metadata.AssemblyReference(metadata, handle); return Compilation.FindModuleByReference(asmRef); } public IModule ResolveModule(ModuleReferenceHandle handle) { if (handle.IsNil) return null; var modRef = metadata.GetModuleReference(handle); string name = metadata.GetString(modRef.Name); foreach (var mod in Compilation.Modules) { if (mod.Name == name) { return mod; } } return null; } public IModule GetDeclaringModule(TypeReferenceHandle handle) { if (handle.IsNil) return null; var tr = metadata.GetTypeReference(handle); switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return GetDeclaringModule((TypeReferenceHandle)tr.ResolutionScope); case HandleKind.AssemblyReference: return ResolveModule((AssemblyReferenceHandle)tr.ResolutionScope); case HandleKind.ModuleReference: return ResolveModule((ModuleReferenceHandle)tr.ResolutionScope); default: return this; } } #endregion #region Resolve Type public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { return ResolveType(typeRefDefSpec, context, options, typeAttributes, nullableContext); } public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { if (typeRefDefSpec.IsNil) return SpecialType.UnknownType; IType ty; switch (typeRefDefSpec.Kind) { case HandleKind.TypeDefinition: ty = TypeProvider.GetTypeFromDefinition(metadata, (TypeDefinitionHandle)typeRefDefSpec, 0); break; case HandleKind.TypeReference: ty = TypeProvider.GetTypeFromReference(metadata, (TypeReferenceHandle)typeRefDefSpec, 0); break; case HandleKind.TypeSpecification: var typeSpec = metadata.GetTypeSpecification((TypeSpecificationHandle)typeRefDefSpec); ty = typeSpec.DecodeSignature(TypeProvider, context); break; case HandleKind.ExportedType: return ResolveForwardedType(metadata.GetExportedType((ExportedTypeHandle)typeRefDefSpec)); default: throw new BadImageFormatException("Not a type handle"); } ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions, nullableContext); return ty; } IType ResolveDeclaringType(EntityHandle declaringTypeReference, GenericContext context) { // resolve without substituting dynamic/tuple types const TypeSystemOptions removedOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations | TypeSystemOptions.NativeIntegers | TypeSystemOptions.NativeIntegersWithoutAttribute; var ty = ResolveType(declaringTypeReference, context, options & ~removedOptions); // but substitute tuple types in type arguments: ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true); return ty; } IType IntroduceTupleTypes(IType ty) { // run ApplyAttributeTypeVisitor without attributes, in order to introduce tuple types return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious); } #endregion #region Resolve Method public IMethod ResolveMethod(EntityHandle methodReference, GenericContext context) { if (methodReference.IsNil) throw new ArgumentNullException(nameof(methodReference)); switch (methodReference.Kind) { case HandleKind.MethodDefinition: return ResolveMethodDefinition((MethodDefinitionHandle)methodReference, expandVarArgs: true); case HandleKind.MemberReference: return ResolveMethodReference((MemberReferenceHandle)methodReference, context, expandVarArgs: true); case HandleKind.MethodSpecification: return ResolveMethodSpecification((MethodSpecificationHandle)methodReference, context, expandVarArgs: true); default: throw new BadImageFormatException("Metadata token must be either a methoddef, memberref or methodspec"); } } IMethod ResolveMethodDefinition(MethodDefinitionHandle methodDefHandle, bool expandVarArgs) { var method = GetDefinition(methodDefHandle); if (expandVarArgs && method.Parameters.LastOrDefault()?.Type.Kind == TypeKind.ArgList) { method = new VarArgInstanceMethod(method, EmptyList.Instance); } return method; } IMethod ResolveMethodSpecification(MethodSpecificationHandle methodSpecHandle, GenericContext context, bool expandVarArgs) { var methodSpec = metadata.GetMethodSpecification(methodSpecHandle); var methodTypeArgs = methodSpec.DecodeSignature(TypeProvider, context) .SelectReadOnlyArray(IntroduceTupleTypes); IMethod method; if (methodSpec.Method.Kind == HandleKind.MethodDefinition) { // generic instance of a methoddef (=generic method in non-generic class in current assembly) method = ResolveMethodDefinition((MethodDefinitionHandle)methodSpec.Method, expandVarArgs); method = method.Specialize(new TypeParameterSubstitution(null, methodTypeArgs)); } else { method = ResolveMethodReference((MemberReferenceHandle)methodSpec.Method, context, methodTypeArgs, expandVarArgs); } return method; } /// /// Resolves a method reference. /// /// /// Class type arguments are provided by the declaring type stored in the memberRef. /// Method type arguments are provided by the caller. /// IMethod ResolveMethodReference(MemberReferenceHandle memberRefHandle, GenericContext context, IReadOnlyList methodTypeArguments = null, bool expandVarArgs = true) { var memberRef = metadata.GetMemberReference(memberRefHandle); if (memberRef.GetKind() != MemberReferenceKind.Method) { throw new BadImageFormatException($"Member reference must be method, but was: {memberRef.GetKind()}"); } MethodSignature signature; IReadOnlyList classTypeArguments = null; IMethod method; if (memberRef.Parent.Kind == HandleKind.MethodDefinition) { method = ResolveMethodDefinition((MethodDefinitionHandle)memberRef.Parent, expandVarArgs: false); signature = memberRef.DecodeMethodSignature(TypeProvider, context); } else { var declaringType = ResolveDeclaringType(memberRef.Parent, context); var declaringTypeDefinition = declaringType.GetDefinition(); if (declaringType.TypeArguments.Count > 0) { classTypeArguments = declaringType.TypeArguments; } // Note: declaringType might be parameterized, but the signature is for the original method definition. // We'll have to search the member directly on declaringTypeDefinition. string name = metadata.GetString(memberRef.Name); signature = memberRef.DecodeMethodSignature(TypeProvider, new GenericContext(declaringTypeDefinition?.TypeParameters)); if (declaringTypeDefinition != null) { // Find the set of overloads to search: IEnumerable methods; if (name == ".ctor") { methods = declaringTypeDefinition.GetConstructors(); } else if (name == ".cctor") { methods = declaringTypeDefinition.Methods.Where(m => m.IsConstructor && m.IsStatic); } else { methods = declaringTypeDefinition.GetMethods(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers) .Concat(declaringTypeDefinition.GetAccessors(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers)); } // Determine the expected parameters from the signature: ImmutableArray parameterTypes; if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { parameterTypes = signature.ParameterTypes .Take(signature.RequiredParameterCount) .Concat(new[] { SpecialType.ArgList }) .ToImmutableArray(); } else { parameterTypes = signature.ParameterTypes; } // Search for the matching method: method = null; foreach (var m in methods) { if (m.TypeParameters.Count != signature.GenericParameterCount) continue; if (signature.Header.IsInstance != !m.IsStatic) continue; if (CompareSignatures(m.Parameters, parameterTypes) && CompareTypes(m.ReturnType, signature.ReturnType)) { method = m; break; } } } else { method = null; } if (method == null) { method = CreateFakeMethod(declaringType, name, signature); } } if (classTypeArguments != null || methodTypeArguments != null) { method = method.Specialize(new TypeParameterSubstitution(classTypeArguments, methodTypeArguments)); } if (expandVarArgs && signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { method = new VarArgInstanceMethod(method, signature.ParameterTypes.Skip(signature.RequiredParameterCount)); } return method; } static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = true, ReplaceMethodTypeParametersWithDummy = true, }; static bool CompareTypes(IType a, IType b) { IType type1 = a.AcceptVisitor(normalizeTypeVisitor); IType type2 = b.AcceptVisitor(normalizeTypeVisitor); return type1.Equals(type2); } static bool CompareSignatures(IReadOnlyList parameters, ImmutableArray parameterTypes) { if (parameterTypes.Length != parameters.Count) return false; for (int i = 0; i < parameterTypes.Length; i++) { if (!CompareTypes(parameterTypes[i], parameters[i].Type)) return false; } return true; } /// /// Create a dummy IMethod from the specified MethodReference /// IMethod CreateFakeMethod(IType declaringType, string name, MethodSignature signature) { SymbolKind symbolKind = SymbolKind.Method; if (name == ".ctor" || name == ".cctor") symbolKind = SymbolKind.Constructor; var m = new FakeMethod(Compilation, symbolKind); m.DeclaringType = declaringType; m.Name = name; m.ReturnType = signature.ReturnType; m.IsStatic = !signature.Header.IsInstance; TypeParameterSubstitution substitution = null; if (signature.GenericParameterCount > 0) { var typeParameters = new List(); for (int i = 0; i < signature.GenericParameterCount; i++) { typeParameters.Add(new DefaultTypeParameter(m, i)); } m.TypeParameters = typeParameters; substitution = new TypeParameterSubstitution(declaringType.TypeArguments, typeParameters); } else if (declaringType.TypeArguments.Count > 0) { substitution = declaringType.GetSubstitution(); } var parameters = new List(); for (int i = 0; i < signature.RequiredParameterCount; i++) { var type = signature.ParameterTypes[i]; if (substitution != null) { // replace the dummy method type parameters with the owned instances we just created type = type.AcceptVisitor(substitution); } parameters.Add(new DefaultParameter(type, "")); } m.Parameters = parameters; GuessFakeMethodAccessor(declaringType, name, signature, m, parameters); return m; } private void GuessFakeMethodAccessor(IType declaringType, string name, MethodSignature signature, FakeMethod m, List parameters) { if (signature.GenericParameterCount > 0) return; var guessedGetter = name.StartsWith("get_", StringComparison.Ordinal); var guessedSetter = name.StartsWith("set_", StringComparison.Ordinal); if (guessedGetter || guessedSetter) { var propertyName = name.Substring(4); var fakeProperty = new FakeProperty(Compilation) { Name = propertyName, DeclaringType = declaringType, IsStatic = m.IsStatic, }; if (guessedGetter) { if (signature.ReturnType.Kind == TypeKind.Void) return; m.AccessorKind = MethodSemanticsAttributes.Getter; m.AccessorOwner = fakeProperty; fakeProperty.Getter = m; fakeProperty.ReturnType = signature.ReturnType; fakeProperty.IsIndexer = parameters.Count > 0; fakeProperty.Parameters = parameters; return; } if (guessedSetter) { if (parameters.Count < 1 || signature.ReturnType.Kind != TypeKind.Void) return; m.AccessorKind = MethodSemanticsAttributes.Setter; m.AccessorOwner = fakeProperty; fakeProperty.Setter = m; fakeProperty.ReturnType = parameters.Last().Type; fakeProperty.IsIndexer = parameters.Count > 1; fakeProperty.Parameters = parameters.SkipLast(1).ToArray(); return; } } const string addPrefix = "add_"; const string removePrefix = "remove_"; const string raisePrefix = "raise_"; var guessedAdd = name.StartsWith(addPrefix, StringComparison.Ordinal); var guessedRemove = name.StartsWith(removePrefix, StringComparison.Ordinal); var guessedRaise = name.StartsWith(raisePrefix, StringComparison.Ordinal); if (guessedAdd || guessedRemove || guessedRaise) { var fakeEvent = new FakeEvent(Compilation) { DeclaringType = declaringType, IsStatic = m.IsStatic, }; if (guessedAdd) { if (parameters.Count != 1) return; m.AccessorKind = MethodSemanticsAttributes.Adder; m.AccessorOwner = fakeEvent; fakeEvent.Name = name.Substring(addPrefix.Length); fakeEvent.AddAccessor = m; fakeEvent.ReturnType = parameters.Single().Type; return; } if (guessedRemove) { if (parameters.Count != 1) return; m.AccessorKind = MethodSemanticsAttributes.Remover; m.AccessorOwner = fakeEvent; fakeEvent.Name = name.Substring(removePrefix.Length); fakeEvent.RemoveAccessor = m; fakeEvent.ReturnType = parameters.Single().Type; return; } if (guessedRaise) { fakeEvent.Name = name.Substring(raisePrefix.Length); fakeEvent.InvokeAccessor = m; m.AccessorKind = MethodSemanticsAttributes.Raiser; m.AccessorOwner = fakeEvent; return; } } } #endregion #region Resolve Entity /// /// Resolves a symbol. /// /// /// * Types are resolved to their definition, as IType does not implement ISymbol. /// * types without definition will resolve to null /// * use ResolveType() to properly resolve types /// * When resolving methods, varargs signatures are not expanded. /// * use ResolveMethod() instead to get an IMethod instance suitable for call-sites /// * May return specialized members, where generics are involved. /// * Other types of handles that don't correspond to TS entities, will return null. /// public IEntity ResolveEntity(EntityHandle entityHandle, GenericContext context = default) { switch (entityHandle.Kind) { case HandleKind.TypeReference: case HandleKind.TypeDefinition: case HandleKind.TypeSpecification: case HandleKind.ExportedType: // Using ResolveDeclaringType() here because ResolveType() might return // nint/nuint which are SpecialTypes without a definition. return ResolveDeclaringType(entityHandle, context).GetDefinition(); case HandleKind.MemberReference: var memberReferenceHandle = (MemberReferenceHandle)entityHandle; switch (metadata.GetMemberReference(memberReferenceHandle).GetKind()) { case MemberReferenceKind.Method: // for consistency with the MethodDefinition case, never expand varargs return ResolveMethodReference(memberReferenceHandle, context, expandVarArgs: false); case MemberReferenceKind.Field: return ResolveFieldReference(memberReferenceHandle, context); default: throw new BadImageFormatException("Unknown MemberReferenceKind"); } case HandleKind.MethodDefinition: return GetDefinition((MethodDefinitionHandle)entityHandle); case HandleKind.MethodSpecification: return ResolveMethodSpecification((MethodSpecificationHandle)entityHandle, context, expandVarArgs: false); case HandleKind.FieldDefinition: return GetDefinition((FieldDefinitionHandle)entityHandle); case HandleKind.EventDefinition: return GetDefinition((EventDefinitionHandle)entityHandle); case HandleKind.PropertyDefinition: return GetDefinition((PropertyDefinitionHandle)entityHandle); default: return null; } } IField ResolveFieldReference(MemberReferenceHandle memberReferenceHandle, GenericContext context) { var memberRef = metadata.GetMemberReference(memberReferenceHandle); var declaringType = ResolveDeclaringType(memberRef.Parent, context); var declaringTypeDefinition = declaringType.GetDefinition(); string name = metadata.GetString(memberRef.Name); // field signature is for the definition, not the generic instance var signature = memberRef.DecodeFieldSignature(TypeProvider, new GenericContext(declaringTypeDefinition?.TypeParameters)); // 'f' in the predicate is also the definition, even if declaringType is a ParameterizedType var field = declaringType.GetFields(f => f.Name == name && CompareTypes(f.ReturnType, signature), GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); if (field == null) { // If it's a field in a generic type, we need to substitute the type arguments: if (declaringType.TypeArguments.Count > 0) { signature = signature.AcceptVisitor(declaringType.GetSubstitution()); } field = new FakeField(Compilation) { ReturnType = signature, Name = name, DeclaringType = declaringType, }; } return field; } #endregion #region Decode Standalone Signature public (SignatureHeader, FunctionPointerType) DecodeMethodSignature(StandaloneSignatureHandle handle, GenericContext genericContext) { var standaloneSignature = metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != StandaloneSignatureKind.Method) throw new BadImageFormatException("Expected Method signature"); var sig = standaloneSignature.DecodeMethodSignature(TypeProvider, genericContext); var fpt = FunctionPointerType.FromSignature(sig, this); return (sig.Header, (FunctionPointerType)IntroduceTupleTypes(fpt)); } public ImmutableArray DecodeLocalSignature(StandaloneSignatureHandle handle, GenericContext genericContext) { var standaloneSignature = metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != StandaloneSignatureKind.LocalVariables) throw new BadImageFormatException("Expected LocalVariables signature"); var types = standaloneSignature.DecodeLocalSignature(TypeProvider, genericContext); return ImmutableArray.CreateRange(types, IntroduceTupleTypes); } #endregion #region Module / Assembly attributes /// /// Gets the list of all assembly attributes in the project. /// public IEnumerable GetAssemblyAttributes() { var b = new AttributeListBuilder(this); if (metadata.IsAssembly) { var assembly = metadata.GetAssemblyDefinition(); b.Add(metadata.GetCustomAttributes(Handle.AssemblyDefinition), SymbolKind.Module); b.AddSecurityAttributes(assembly.GetDeclarativeSecurityAttributes()); // AssemblyVersionAttribute if (assembly.Version != null) { b.Add(KnownAttribute.AssemblyVersion, KnownTypeCode.String, assembly.Version.ToString()); } AddTypeForwarderAttributes(ref b); } return b.Build(); } /// /// Gets the list of all module attributes in the project. /// public IEnumerable GetModuleAttributes() { var b = new AttributeListBuilder(this); b.Add(metadata.GetCustomAttributes(Handle.ModuleDefinition), SymbolKind.Module); if (!metadata.IsAssembly) { AddTypeForwarderAttributes(ref b); } return b.Build(); } void AddTypeForwarderAttributes(ref AttributeListBuilder b) { foreach (ExportedTypeHandle t in metadata.ExportedTypes) { var type = metadata.GetExportedType(t); if (type.IsForwarder) { b.Add(KnownAttribute.TypeForwardedTo, KnownTypeCode.Type, ResolveForwardedType(type)); } } } IType ResolveForwardedType(ExportedType forwarder) { IModule module = ResolveModule(forwarder); var typeName = forwarder.GetFullTypeName(metadata); if (module == null) return new UnknownType(typeName); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { var td = module.GetTypeDefinition(typeName); if (td != null) { return td; } } } return new UnknownType(typeName); IModule ResolveModule(ExportedType type) { switch (type.Implementation.Kind) { case HandleKind.AssemblyFile: // TODO : Resolve assembly file (module)... return this; case HandleKind.ExportedType: var outerType = metadata.GetExportedType((ExportedTypeHandle)type.Implementation); return ResolveModule(outerType); case HandleKind.AssemblyReference: var asmRef = metadata.GetAssemblyReference((AssemblyReferenceHandle)type.Implementation); string shortName = metadata.GetString(asmRef.Name); foreach (var asm in Compilation.Modules) { if (string.Equals(asm.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) { return asm; } } return null; default: throw new BadImageFormatException("Expected implementation to be either an AssemblyFile, ExportedType or AssemblyReference."); } } } #endregion #region Attribute Helpers /// /// Cache for parameterless known attribute types. /// readonly IType[] knownAttributeTypes = new IType[KnownAttributes.Count]; internal IType GetAttributeType(KnownAttribute attr) { var ty = LazyInit.VolatileRead(ref knownAttributeTypes[(int)attr]); if (ty != null) return ty; ty = Compilation.FindType(attr.GetTypeName()); return LazyInit.GetOrSet(ref knownAttributeTypes[(int)attr], ty); } /// /// Cache for parameterless known attributes. /// readonly IAttribute[] knownAttributes = new IAttribute[KnownAttributes.Count]; /// /// Construct a builtin attribute. /// internal IAttribute MakeAttribute(KnownAttribute type) { var attr = LazyInit.VolatileRead(ref knownAttributes[(int)type]); if (attr != null) return attr; attr = new DefaultAttribute(GetAttributeType(type), ImmutableArray.Create>(), ImmutableArray.Create>()); return LazyInit.GetOrSet(ref knownAttributes[(int)type], attr); } #endregion #region Visibility Filter internal bool IncludeInternalMembers => (options & TypeSystemOptions.OnlyPublicAPI) == 0; internal bool IsVisible(FieldAttributes att) { att &= FieldAttributes.FieldAccessMask; return IncludeInternalMembers || att == FieldAttributes.Public || att == FieldAttributes.Family || att == FieldAttributes.FamORAssem; } internal bool IsVisible(MethodAttributes att) { att &= MethodAttributes.MemberAccessMask; return IncludeInternalMembers || att == MethodAttributes.Public || att == MethodAttributes.Family || att == MethodAttributes.FamORAssem; } #endregion #region Nullability Reference Type Support readonly Accessibility minAccessibilityForNRT; static Accessibility FindMinimumAccessibilityForNRT(MetadataReader metadata, CustomAttributeHandleCollection customAttributes) { // Determine the minimum effective accessibility an entity must have, so that the metadata stores the nullability for its type. foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullablePublicOnly)) { CustomAttributeValue value; try { value = customAttribute.DecodeValue(Metadata.MetadataExtensions.MinimalAttributeTypeProvider); } catch (BadImageFormatException) { continue; } catch (EnumUnderlyingTypeResolveException) { continue; } if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is bool includesInternals) { return includesInternals ? Accessibility.ProtectedAndInternal : Accessibility.Protected; } } } return Accessibility.None; } internal bool ShouldDecodeNullableAttributes(IEntity entity) { if ((options & TypeSystemOptions.NullabilityAnnotations) == 0) return false; if (minAccessibilityForNRT == Accessibility.None || entity == null) return true; return minAccessibilityForNRT.LessThanOrEqual(entity.EffectiveAccessibility()); } internal TypeSystemOptions OptionsForEntity(IEntity entity) { var opt = this.options; if ((opt & TypeSystemOptions.NullabilityAnnotations) != 0) { if (!ShouldDecodeNullableAttributes(entity)) { opt &= ~TypeSystemOptions.NullabilityAnnotations; } } return opt; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs ================================================ // Copyright (c) 2018 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// /// Represents a modopt or modreq type. /// public class ModifiedType : TypeWithElementType, IType { readonly TypeKind kind; readonly IType modifier; public ModifiedType(IType modifier, IType unmodifiedType, bool isRequired) : base(unmodifiedType) { this.kind = isRequired ? TypeKind.ModReq : TypeKind.ModOpt; this.modifier = modifier ?? throw new ArgumentNullException(nameof(modifier)); } public IType Modifier => modifier; public override TypeKind Kind => kind; public override string NameSuffix => (kind == TypeKind.ModReq ? " modreq" : " modopt") + $"({modifier.FullName})"; public override bool? IsReferenceType => elementType.IsReferenceType; public override bool IsByRefLike => elementType.IsByRefLike; public override Nullability Nullability => elementType.Nullability; public override IType ChangeNullability(Nullability nullability) { IType newElementType = elementType.ChangeNullability(nullability); if (newElementType == elementType) return this; else return new ModifiedType(modifier, newElementType, kind == TypeKind.ModReq); } public override ITypeDefinition GetDefinition() { return elementType.GetDefinition(); } public override ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return elementType.GetDefinitionOrUnknown(); } public override IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetAccessors(filter, options); } public override IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { return elementType.GetConstructors(filter, options); } public override IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetEvents(filter, options); } public override IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetFields(filter, options); } public override IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetMembers(filter, options); } public override IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetMethods(typeArguments, filter, options); } public override IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetMethods(filter, options); } public override IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetNestedTypes(typeArguments, filter, options); } public override IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetNestedTypes(filter, options); } public override IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return elementType.GetProperties(filter, options); } public override IType VisitChildren(TypeVisitor visitor) { var newElementType = elementType.AcceptVisitor(visitor); var newModifier = modifier.AcceptVisitor(visitor); if (newModifier != modifier || newElementType != elementType) { return new ModifiedType(newModifier, newElementType, kind == TypeKind.ModReq); } return this; } public override IType AcceptVisitor(TypeVisitor visitor) { if (kind == TypeKind.ModReq) return visitor.VisitModReq(this); else return visitor.VisitModOpt(this); } public override bool Equals(IType other) { return other is ModifiedType o && kind == o.kind && modifier.Equals(o.modifier) && elementType.Equals(o.elementType); } public override int GetHashCode() { unchecked { return (int)kind ^ (elementType.GetHashCode() * 1344795899) ^ (modifier.GetHashCode() * 901375117); } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs ================================================ // Copyright (c) 2019 Daniel Grunwald // // 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. #nullable enable using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { sealed class NormalizeTypeVisitor : TypeVisitor { /// /// NormalizeTypeVisitor that does not normalize type parameters, /// but performs type erasure (object->dynamic; tuple->underlying type). /// internal static readonly NormalizeTypeVisitor TypeErasure = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = false, ReplaceMethodTypeParametersWithDummy = false, DynamicAndObject = true, IntPtrToNInt = true, TupleToUnderlyingType = true, RemoveModOpt = true, RemoveModReq = true, RemoveNullability = true, }; internal static readonly NormalizeTypeVisitor IgnoreNullabilityAndTuples = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = false, ReplaceMethodTypeParametersWithDummy = false, DynamicAndObject = false, IntPtrToNInt = false, TupleToUnderlyingType = true, RemoveModOpt = true, RemoveModReq = true, RemoveNullability = true, }; internal static readonly NormalizeTypeVisitor IgnoreNullability = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = false, ReplaceMethodTypeParametersWithDummy = false, DynamicAndObject = false, IntPtrToNInt = false, TupleToUnderlyingType = false, RemoveModOpt = true, RemoveModReq = true, RemoveNullability = true, }; public bool EquivalentTypes(IType a, IType b) { a = a.AcceptVisitor(this); b = b.AcceptVisitor(this); return a.Equals(b); } public bool RemoveModOpt = true; public bool RemoveModReq = true; public bool ReplaceClassTypeParametersWithDummy = true; public bool ReplaceMethodTypeParametersWithDummy = true; public bool DynamicAndObject = true; public bool IntPtrToNInt = true; public bool TupleToUnderlyingType = true; public bool RemoveNullability = true; public override IType VisitTypeParameter(ITypeParameter type) { if (type.OwnerType == SymbolKind.Method && ReplaceMethodTypeParametersWithDummy) { return DummyTypeParameter.GetMethodTypeParameter(type.Index); } else if (type.OwnerType == SymbolKind.TypeDefinition && ReplaceClassTypeParametersWithDummy) { return DummyTypeParameter.GetClassTypeParameter(type.Index); } else if (RemoveNullability && type is NullabilityAnnotatedTypeParameter natp) { return natp.TypeWithoutAnnotation.AcceptVisitor(this); } else { return base.VisitTypeParameter(type); } } public override IType VisitTypeDefinition(ITypeDefinition type) { switch (type.KnownTypeCode) { case KnownTypeCode.Object when DynamicAndObject: // Instead of normalizing dynamic->object, // we do this the opposite direction, so that we don't need a compilation to find the object type. if (RemoveNullability) return SpecialType.Dynamic; else return SpecialType.Dynamic.ChangeNullability(type.Nullability); case KnownTypeCode.IntPtr when IntPtrToNInt: return SpecialType.NInt; case KnownTypeCode.UIntPtr when IntPtrToNInt: return SpecialType.NUInt; } return base.VisitTypeDefinition(type); } public override IType VisitTupleType(TupleType type) { if (TupleToUnderlyingType) { return type.UnderlyingType.AcceptVisitor(this); } else { return base.VisitTupleType(type); } } public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) { if (RemoveNullability) return type.TypeWithoutAnnotation.AcceptVisitor(this); else return base.VisitNullabilityAnnotatedType(type); } public override IType VisitArrayType(ArrayType type) { if (RemoveNullability) return base.VisitArrayType(type).ChangeNullability(Nullability.Oblivious); else return base.VisitArrayType(type); } public override IType VisitModOpt(ModifiedType type) { if (RemoveModOpt) { return type.ElementType.AcceptVisitor(this); } else { return base.VisitModOpt(type); } } public override IType VisitModReq(ModifiedType type) { if (RemoveModReq) { return type.ElementType.AcceptVisitor(this); } else { return base.VisitModReq(type); } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/Nullability.cs ================================================ // Copyright (c) 2019 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.TypeSystem { public enum Nullability : byte { Oblivious = 0, NotNullable = 1, Nullable = 2 } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/NullableType.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Static helper methods for working with nullable types. /// public static class NullableType { /// /// Gets whether the specified type is a nullable type. /// public static bool IsNullable(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); ParameterizedType pt = type.SkipModifiers() as ParameterizedType; return pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT); } public static bool IsNonNullableValueType(IType type) { return type.IsReferenceType == false && !IsNullable(type); } /// /// Returns the element type, if is a nullable type. /// Otherwise, returns the type itself. /// public static IType GetUnderlyingType(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); ParameterizedType pt = type.SkipModifiers() as ParameterizedType; if (pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT)) return pt.GetTypeArgument(0); else return type; } /// /// Creates a nullable type. /// public static IType Create(ICompilation compilation, IType elementType) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (elementType == null) throw new ArgumentNullException(nameof(elementType)); IType nullableType = compilation.FindType(KnownTypeCode.NullableOfT); ITypeDefinition nullableTypeDef = nullableType.GetDefinition(); if (nullableTypeDef != null) return new ParameterizedType(nullableTypeDef, new[] { elementType }); else return nullableType; } /// /// Creates a nullable type reference. /// public static ParameterizedTypeReference Create(ITypeReference elementType) { if (elementType == null) throw new ArgumentNullException(nameof(elementType)); return new ParameterizedTypeReference(KnownTypeReference.Get(KnownTypeCode.NullableOfT), new[] { elementType }); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Compares parameter lists by comparing the types of all parameters. /// /// /// 'ref int' and 'out int' are considered to be equal - unless is set to true. /// 'object' and 'dynamic' are also equal. /// For generic methods, "Method{T}(T a)" and "Method{S}(S b)" are considered equal. /// However, "Method(T a)" and "Method(S b)" are not considered equal when the type parameters T and S belong to classes. /// public sealed class ParameterListComparer : IEqualityComparer> { public static readonly ParameterListComparer Instance = new ParameterListComparer(); static readonly NormalizeTypeVisitor normalizationVisitor = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = false, ReplaceMethodTypeParametersWithDummy = true, DynamicAndObject = true, TupleToUnderlyingType = true, }; bool includeModifiers; public static ParameterListComparer WithOptions(bool includeModifiers = false) { return new ParameterListComparer() { includeModifiers = includeModifiers }; } public bool Equals(IReadOnlyList x, IReadOnlyList y) { if (x == y) return true; if (x == null || y == null || x.Count != y.Count) return false; for (int i = 0; i < x.Count; i++) { var a = x[i]; var b = y[i]; if (a == null && b == null) continue; if (a == null || b == null) return false; if (includeModifiers) { if (a.ReferenceKind != b.ReferenceKind) return false; if (a.IsParams != b.IsParams) return false; } // We want to consider the parameter lists "Method(T a)" and "Method(S b)" as equal. // However, the parameter types are not considered equal, as T is a different type parameter than S. // In order to compare the method signatures, we will normalize all method type parameters. IType aType = a.Type.AcceptVisitor(normalizationVisitor); IType bType = b.Type.AcceptVisitor(normalizationVisitor); if (!aType.Equals(bType)) return false; } return true; } public int GetHashCode(IReadOnlyList obj) { int hashCode = obj.Count; unchecked { foreach (IParameter p in obj) { hashCode *= 27; IType type = p.Type.AcceptVisitor(normalizationVisitor); hashCode += type.GetHashCode(); } } return hashCode; } } /// /// Compares member signatures. /// /// /// This comparer checks for equal short name, equal type parameter count, and equal parameter types (using ParameterListComparer). /// public sealed class SignatureComparer : IEqualityComparer { StringComparer nameComparer; public SignatureComparer(StringComparer nameComparer) { if (nameComparer == null) throw new ArgumentNullException(nameof(nameComparer)); this.nameComparer = nameComparer; } /// /// Gets a signature comparer that uses an ordinal comparison for the member name. /// public static readonly SignatureComparer Ordinal = new SignatureComparer(StringComparer.Ordinal); public bool Equals(IMember x, IMember y) { if (x == y) return true; if (x == null || y == null || x.SymbolKind != y.SymbolKind || !nameComparer.Equals(x.Name, y.Name)) return false; IParameterizedMember px = x as IParameterizedMember; IParameterizedMember py = y as IParameterizedMember; if (px != null && py != null) { IMethod mx = x as IMethod; IMethod my = y as IMethod; if (mx != null && my != null && mx.TypeParameters.Count != my.TypeParameters.Count) return false; return ParameterListComparer.Instance.Equals(px.Parameters, py.Parameters); } else { return true; } } public int GetHashCode(IMember obj) { unchecked { int hash = (int)obj.SymbolKind * 33 + nameComparer.GetHashCode(obj.Name); IParameterizedMember pm = obj as IParameterizedMember; if (pm != null) { hash *= 27; hash += ParameterListComparer.Instance.GetHashCode(pm.Parameters); IMethod m = pm as IMethod; if (m != null) hash += m.TypeParameters.Count; } return hash; } } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Text; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// ParameterizedType represents an instance of a generic type. /// Example: List<string> /// /// /// When getting the members, this type modifies the lists so that /// type parameters in the signatures of the members are replaced with /// the type arguments. /// [Serializable] public sealed class ParameterizedType : IType { readonly IType genericType; readonly IType[] typeArguments; public ParameterizedType(IType genericType, IEnumerable typeArguments) { if (genericType == null) throw new ArgumentNullException(nameof(genericType)); if (typeArguments == null) throw new ArgumentNullException(nameof(typeArguments)); this.genericType = genericType; this.typeArguments = typeArguments.ToArray(); // copy input array to ensure it isn't modified if (this.typeArguments.Length == 0) throw new ArgumentException("Cannot use ParameterizedType with 0 type arguments."); if (genericType.TypeParameterCount != this.typeArguments.Length) throw new ArgumentException("Number of type arguments must match the type definition's number of type parameters"); ICompilationProvider gp = genericType as ICompilationProvider; for (int i = 0; i < this.typeArguments.Length; i++) { if (this.typeArguments[i] == null) throw new ArgumentNullException("typeArguments[" + i + "]"); ICompilationProvider p = this.typeArguments[i] as ICompilationProvider; if (p != null && gp != null && p.Compilation != gp.Compilation) throw new InvalidOperationException("Cannot parameterize a type with type arguments from a different compilation."); } } /// /// Fast internal version of the constructor. (no safety checks) /// Keeps the array that was passed and assumes it won't be modified. /// internal ParameterizedType(IType genericType, params IType[] typeArguments) { Debug.Assert(genericType.TypeParameterCount == typeArguments.Length); this.genericType = genericType; this.typeArguments = typeArguments; } public TypeKind Kind { get { return genericType.Kind; } } public IType GenericType { get { return genericType; } } public bool? IsReferenceType => genericType.IsReferenceType; public bool IsByRefLike => genericType.IsByRefLike; public Nullability Nullability => genericType.Nullability; public IType ChangeNullability(Nullability nullability) { IType newGenericType = genericType.ChangeNullability(nullability); if (newGenericType == genericType) return this; else return new ParameterizedType(newGenericType, typeArguments); } public IType DeclaringType { get { IType declaringType = genericType.DeclaringType; if (declaringType != null && declaringType.TypeParameterCount > 0 && declaringType.TypeParameterCount <= genericType.TypeParameterCount) { IType[] newTypeArgs = new IType[declaringType.TypeParameterCount]; Array.Copy(this.typeArguments, 0, newTypeArgs, 0, newTypeArgs.Length); return new ParameterizedType(declaringType, newTypeArgs); } return declaringType; } } public int TypeParameterCount { get { return typeArguments.Length; } } public string FullName { get { return genericType.FullName; } } public string Name { get { return genericType.Name; } } public string Namespace { get { return genericType.Namespace; } } public string ReflectionName { get { StringBuilder b = new StringBuilder(genericType.ReflectionName); b.Append('['); for (int i = 0; i < typeArguments.Length; i++) { if (i > 0) b.Append(','); b.Append('['); b.Append(typeArguments[i].ReflectionName); b.Append(']'); } b.Append(']'); return b.ToString(); } } public override string ToString() { StringBuilder b = new StringBuilder(genericType.ToString()); b.Append('['); for (int i = 0; i < typeArguments.Length; i++) { if (i > 0) b.Append(','); b.Append('['); b.Append(typeArguments[i].ToString()); b.Append(']'); } b.Append(']'); return b.ToString(); } public IReadOnlyList TypeArguments => typeArguments; /// /// Same as 'parameterizedType.TypeArguments[index]'. /// public IType GetTypeArgument(int index) { return typeArguments[index]; } public IReadOnlyList TypeParameters => genericType.TypeParameters; /// /// Gets the definition of the generic type. /// For ParameterizedType, this method never returns null. /// public ITypeDefinition GetDefinition() { return genericType.GetDefinition(); } public ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return genericType.GetDefinitionOrUnknown(); } /// /// Gets a type visitor that performs the substitution of class type parameters with the type arguments /// of this parameterized type. /// public TypeParameterSubstitution GetSubstitution() { return new TypeParameterSubstitution(typeArguments, null); } /// /// Gets a type visitor that performs the substitution of class type parameters with the type arguments /// of this parameterized type, /// and also substitutes method type parameters with the specified method type arguments. /// public TypeParameterSubstitution GetSubstitution(IReadOnlyList methodTypeArguments) { return new TypeParameterSubstitution(typeArguments, methodTypeArguments); } public IEnumerable DirectBaseTypes { get { var substitution = GetSubstitution(); return genericType.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); } } public IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetNestedTypes(filter, options); else return GetMembersHelper.GetNestedTypes(this, filter, options); } public IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetNestedTypes(typeArguments, filter, options); else return GetMembersHelper.GetNestedTypes(this, typeArguments, filter, options); } public IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetConstructors(filter, options); else return GetMembersHelper.GetConstructors(this, filter, options); } public IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetMethods(filter, options); else return GetMembersHelper.GetMethods(this, filter, options); } public IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetMethods(typeArguments, filter, options); else return GetMembersHelper.GetMethods(this, typeArguments, filter, options); } public IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetProperties(filter, options); else return GetMembersHelper.GetProperties(this, filter, options); } public IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetFields(filter, options); else return GetMembersHelper.GetFields(this, filter, options); } public IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetEvents(filter, options); else return GetMembersHelper.GetEvents(this, filter, options); } public IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetMembers(filter, options); else return GetMembersHelper.GetMembers(this, filter, options); } public IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { if ((options & GetMemberOptions.ReturnMemberDefinitions) == GetMemberOptions.ReturnMemberDefinitions) return genericType.GetAccessors(filter, options); else return GetMembersHelper.GetAccessors(this, filter, options); } public override bool Equals(object obj) { return Equals(obj as IType); } public bool Equals(IType other) { if (this == other) return true; ParameterizedType c = other as ParameterizedType; if (c == null || !genericType.Equals(c.genericType) || typeArguments.Length != c.typeArguments.Length) return false; for (int i = 0; i < typeArguments.Length; i++) { if (!typeArguments[i].Equals(c.typeArguments[i])) return false; } return true; } public override int GetHashCode() { int hashCode = genericType.GetHashCode(); unchecked { foreach (var ta in typeArguments) { hashCode *= 1000000007; hashCode += 1000000009 * ta.GetHashCode(); } } return hashCode; } public IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitParameterizedType(this); } public IType VisitChildren(TypeVisitor visitor) { IType g = genericType.AcceptVisitor(visitor); // Keep ta == null as long as no elements changed, allocate the array only if necessary. IType[] ta = (g != genericType) ? new IType[typeArguments.Length] : null; for (int i = 0; i < typeArguments.Length; i++) { IType r = typeArguments[i].AcceptVisitor(visitor); if (r == null) throw new NullReferenceException("TypeVisitor.Visit-method returned null"); if (ta == null && r != typeArguments[i]) { // we found a difference, so we need to allocate the array ta = new IType[typeArguments.Length]; for (int j = 0; j < i; j++) { ta[j] = typeArguments[j]; } } if (ta != null) ta[i] = r; } if (ta == null) return this; else return new ParameterizedType(g, ta); } } /// /// ParameterizedTypeReference is a reference to generic class that specifies the type parameters. /// Example: List<string> /// [Serializable] public sealed class ParameterizedTypeReference : ITypeReference, ISupportsInterning { readonly ITypeReference genericType; readonly ITypeReference[] typeArguments; public ParameterizedTypeReference(ITypeReference genericType, IEnumerable typeArguments) { if (genericType == null) throw new ArgumentNullException(nameof(genericType)); if (typeArguments == null) throw new ArgumentNullException(nameof(typeArguments)); this.genericType = genericType; this.typeArguments = typeArguments.ToArray(); for (int i = 0; i < this.typeArguments.Length; i++) { if (this.typeArguments[i] == null) throw new ArgumentNullException("typeArguments[" + i + "]"); } } public ITypeReference GenericType { get { return genericType; } } public IReadOnlyList TypeArguments { get { return typeArguments; } } public IType Resolve(ITypeResolveContext context) { IType baseType = genericType.Resolve(context); int tpc = baseType.TypeParameterCount; if (tpc == 0) return baseType; IType[] resolvedTypes = new IType[tpc]; for (int i = 0; i < resolvedTypes.Length; i++) { if (i < typeArguments.Length) resolvedTypes[i] = typeArguments[i].Resolve(context); else resolvedTypes[i] = SpecialType.UnknownType; } return new ParameterizedType(baseType, resolvedTypes); } public override string ToString() { StringBuilder b = new StringBuilder(genericType.ToString()); b.Append('['); for (int i = 0; i < typeArguments.Length; i++) { if (i > 0) b.Append(','); b.Append('['); b.Append(typeArguments[i].ToString()); b.Append(']'); } b.Append(']'); return b.ToString(); } int ISupportsInterning.GetHashCodeForInterning() { int hashCode = genericType.GetHashCode(); unchecked { foreach (ITypeReference t in typeArguments) { hashCode *= 27; hashCode += t.GetHashCode(); } } return hashCode; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { ParameterizedTypeReference o = other as ParameterizedTypeReference; if (o != null && genericType == o.genericType && typeArguments.Length == o.typeArguments.Length) { for (int i = 0; i < typeArguments.Length; i++) { if (typeArguments[i] != o.typeArguments[i]) return false; } return true; } return false; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/PointerType.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { public sealed class PointerType : TypeWithElementType { public PointerType(IType elementType) : base(elementType) { } public override TypeKind Kind { get { return TypeKind.Pointer; } } public override string NameSuffix { get { return "*"; } } public override bool? IsReferenceType { get { return null; } } public override int GetHashCode() { return elementType.GetHashCode() ^ 91725811; } public override bool Equals(IType other) { PointerType a = other as PointerType; return a != null && elementType.Equals(a.elementType); } public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitPointerType(this); } public override IType VisitChildren(TypeVisitor visitor) { IType e = elementType.AcceptVisitor(visitor); if (e == elementType) return this; else return new PointerType(e); } } [Serializable] public sealed class PointerTypeReference : ITypeReference, ISupportsInterning { readonly ITypeReference elementType; public PointerTypeReference(ITypeReference elementType) { if (elementType == null) throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } public ITypeReference ElementType { get { return elementType; } } public IType Resolve(ITypeResolveContext context) { return new PointerType(elementType.Resolve(context)); } public override string ToString() { return elementType.ToString() + "*"; } int ISupportsInterning.GetHashCodeForInterning() { return elementType.GetHashCode() ^ 91725812; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { PointerTypeReference o = other as PointerTypeReference; return o != null && this.elementType == o.elementType; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ReferenceResolvingException.cs ================================================ // 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents an error while resolving a reference to a type or a member. /// [Serializable] public class ReferenceResolvingException : Exception { /// /// Initializes a new instance of the class /// public ReferenceResolvingException() { } /// /// Initializes a new instance of the class /// /// A that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture. public ReferenceResolvingException(string message) : base(message) { } /// /// Initializes a new instance of the class /// /// A that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture. /// The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. public ReferenceResolvingException(string message, Exception inner) : base(message, inner) { } /// /// Initializes a new instance of the class /// /// The object that holds the serialized object data. /// The contextual information about the source or destination. protected ReferenceResolvingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Diagnostics; using System.Reflection.Metadata; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Static helper methods for reflection names. /// public static class ReflectionHelper { #region ICompilation.FindType /// /// Retrieves the specified type in this compilation. /// Returns if the type cannot be found in this compilation. /// /// /// This method cannot be used with open types; all type parameters will be substituted /// with . /// public static IType FindType(this ICompilation compilation, Type type) { return ParseReflectionName(type.AssemblyQualifiedName, new SimpleTypeResolveContext(compilation)); } public static IType FindType(this ICompilation compilation, StackType stackType, Sign sign = Sign.None) { switch (stackType) { case StackType.Unknown: return SpecialType.UnknownType; case StackType.Ref: return new ByReferenceType(SpecialType.UnknownType); default: return compilation.FindType(stackType.ToKnownTypeCode(sign)); } } #endregion #region SplitTypeParameterCountFromReflectionName /// /// Removes the ` with type parameter count from the reflection name. /// /// Do not use this method with the full name of inner classes. public static string SplitTypeParameterCountFromReflectionName(string reflectionName) { int pos = reflectionName.LastIndexOf('`'); if (pos < 0) { return reflectionName; } else { return reflectionName.Substring(0, pos); } } /// /// Removes the ` with type parameter count from the reflection name. /// /// Do not use this method with the full name of inner classes. public static string SplitTypeParameterCountFromReflectionName(string reflectionName, out int typeParameterCount) { int pos = reflectionName.LastIndexOf('`'); if (pos < 0) { typeParameterCount = 0; return reflectionName; } else { string typeCount = reflectionName.Substring(pos + 1); if (int.TryParse(typeCount, out typeParameterCount)) return reflectionName.Substring(0, pos); else return reflectionName; } } #endregion #region TypeCode support /// /// Retrieves a built-in type using the specified type code. /// public static IType FindType(this ICompilation compilation, TypeCode typeCode) { return compilation.FindType((KnownTypeCode)typeCode); } /// /// Gets the type code for the specified type, or TypeCode.Empty if none of the other type codes match. /// public static TypeCode GetTypeCode(this IType type) { ITypeDefinition def = type as ITypeDefinition; if (def != null) { KnownTypeCode typeCode = def.KnownTypeCode; if (typeCode <= KnownTypeCode.String && typeCode != KnownTypeCode.Void) return (TypeCode)typeCode; else return TypeCode.Empty; } return TypeCode.Empty; } #endregion #region ParseReflectionName /// /// Parses a reflection name into a type reference. /// /// The reflection name of the type. /// A type reference that represents the reflection name. /// The syntax of the reflection type name is invalid /// /// If the type is open (contains type parameters '`0' or '``0'), /// an with the appropriate CurrentTypeDefinition/CurrentMember is required /// to resolve the reference to the ITypeParameter. /// For looking up closed, assembly qualified type names, the root type resolve context for the compilation /// is sufficient. /// When looking up a type name that isn't assembly qualified, the type reference will look in /// first, and if the type is not found there, /// it will look in all other assemblies of the compilation. /// /// public static IType ParseReflectionName(string reflectionTypeName, ITypeResolveContext resolveContext) { if (reflectionTypeName == null) throw new ArgumentNullException(nameof(reflectionTypeName)); if (!TypeName.TryParse(reflectionTypeName.AsSpan(), out var result)) { throw new ReflectionNameParseException(0, "Invalid type name: " + reflectionTypeName); } return ResolveTypeName(result, resolveContext); } private static IType ResolveTypeName(TypeName result, ITypeResolveContext resolveContext) { if (result.IsArray) { return new ArrayType( resolveContext.Compilation, ResolveTypeName(result.GetElementType(), resolveContext), result.GetArrayRank() ); } else if (result.IsByRef) { return new ByReferenceType( ResolveTypeName(result.GetElementType(), resolveContext) ); } else if (result.IsConstructedGenericType) { IType genericType = ResolveTypeName(result.GetGenericTypeDefinition(), resolveContext); var genericArgs = result.GetGenericArguments(); if (genericType.TypeParameterCount == 0) { return genericType; } IType[] resolvedTypes = new IType[genericType.TypeParameterCount]; for (int i = 0; i < genericArgs.Length; i++) { if (i < genericArgs.Length) resolvedTypes[i] = ResolveTypeName(genericArgs[i], resolveContext); else resolvedTypes[i] = SpecialType.UnknownType; } return new ParameterizedType(genericType, resolvedTypes); } else if (result.IsNested) { var declaringType = ResolveTypeName(result.DeclaringType, resolveContext).GetDefinition(); var plainName = SplitTypeParameterCountFromReflectionName(result.Name, out int tpc); if (declaringType != null) { foreach (var type in declaringType.NestedTypes) { if (type.Name == plainName && type.TypeParameterCount == tpc + declaringType.TypeParameterCount) return type; } } return new UnknownType(new FullTypeName(result.FullName)); } else if (result.IsPointer) { return new PointerType( ResolveTypeName(result.GetElementType(), resolveContext) ); } else { Debug.Assert(result.IsSimple); if (result.FullName.Length > 1 && result.FullName[0] == '`') { if (result.FullName.Length > 2 && result.FullName[1] == '`') { if (int.TryParse(result.FullName.Substring(2), out int index)) { if (resolveContext.CurrentMember is IMethod m && index < m.TypeParameters.Count) { return m.TypeParameters[index]; } return DummyTypeParameter.GetMethodTypeParameter(index); } } else if (int.TryParse(result.FullName.Substring(1), out int index)) { if (resolveContext.CurrentTypeDefinition != null && index < resolveContext.CurrentTypeDefinition.TypeParameterCount) { return resolveContext.CurrentTypeDefinition.TypeParameters[index]; } return DummyTypeParameter.GetClassTypeParameter(index); } } var topLevelTypeName = new TopLevelTypeName(result.FullName); if (result.AssemblyName != null) { var module = resolveContext.Compilation.FindModuleByAssemblyNameInfo(result.AssemblyName); if (module != null) { return (IType)module.GetTypeDefinition(topLevelTypeName) ?? new UnknownType(topLevelTypeName); } } foreach (var module in resolveContext.Compilation.Modules) { var type = module.GetTypeDefinition(topLevelTypeName); if (type != null) return type; } return new UnknownType(topLevelTypeName); } } internal static int ReadTypeParameterCount(string reflectionTypeName, ref int pos) { int startPos = pos; while (pos < reflectionTypeName.Length) { char c = reflectionTypeName[pos]; if (c < '0' || c > '9') break; pos++; } int tpc; if (!int.TryParse(reflectionTypeName.Substring(startPos, pos - startPos), out tpc)) throw new ReflectionNameParseException(pos, "Expected type parameter count"); return tpc; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/ReflectionNameParseException.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Runtime.Serialization; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Represents an error while parsing a reflection name. /// [Serializable] public class ReflectionNameParseException : Exception { int position; public int Position { get { return position; } } public ReflectionNameParseException(int position) { this.position = position; } public ReflectionNameParseException(int position, string message) : base(message) { this.position = position; } public ReflectionNameParseException(int position, string message, Exception innerException) : base(message, innerException) { this.position = position; } // This constructor is needed for serialization. protected ReflectionNameParseException(SerializationInfo info, StreamingContext context) : base(info, context) { position = info.GetInt32("position"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("position", position); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/SimpleTypeResolveContext.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Default ITypeResolveContext implementation. /// public class SimpleTypeResolveContext : ITypeResolveContext { readonly ICompilation compilation; readonly IModule currentModule; readonly ITypeDefinition currentTypeDefinition; readonly IMember currentMember; public SimpleTypeResolveContext(ICompilation compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; } public SimpleTypeResolveContext(IModule module) { if (module == null) throw new ArgumentNullException(nameof(module)); this.compilation = module.Compilation; this.currentModule = module; } public SimpleTypeResolveContext(IEntity entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); this.compilation = entity.Compilation; this.currentModule = entity.ParentModule; this.currentTypeDefinition = (entity as ITypeDefinition) ?? entity.DeclaringTypeDefinition; this.currentMember = entity as IMember; } private SimpleTypeResolveContext(ICompilation compilation, IModule currentModule, ITypeDefinition currentTypeDefinition, IMember currentMember) { this.compilation = compilation; this.currentModule = currentModule; this.currentTypeDefinition = currentTypeDefinition; this.currentMember = currentMember; } public ICompilation Compilation { get { return compilation; } } public IModule CurrentModule { get { return currentModule; } } public ITypeDefinition CurrentTypeDefinition { get { return currentTypeDefinition; } } public IMember CurrentMember { get { return currentMember; } } public ITypeResolveContext WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { return new SimpleTypeResolveContext(compilation, currentModule, typeDefinition, currentMember); } public ITypeResolveContext WithCurrentMember(IMember member) { return new SimpleTypeResolveContext(compilation, currentModule, currentTypeDefinition, member); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/SpecialType.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Contains static implementations of special types. /// [Serializable] public sealed class SpecialType : AbstractType, ITypeReference { /// /// Gets the type representing resolve errors. /// public readonly static SpecialType UnknownType = new SpecialType(TypeKind.Unknown, "?", isReferenceType: null); /// /// The null type is used as type of the null literal. It is a reference type without any members; and it is a subtype of all reference types. /// public readonly static SpecialType NullType = new SpecialType(TypeKind.Null, "null", isReferenceType: true); /// /// Used for expressions without type, e.g. method groups or lambdas. /// public readonly static SpecialType NoType = new SpecialType(TypeKind.None, "?", isReferenceType: null); /// /// Type representing the C# 'dynamic' type. /// public readonly static SpecialType Dynamic = new SpecialType(TypeKind.Dynamic, "dynamic", isReferenceType: true); /// /// Type representing the C# 9 'nint' type. /// public readonly static SpecialType NInt = new SpecialType(TypeKind.NInt, "nint", isReferenceType: false); /// /// Type representing the C# 9 'nuint' type. /// public readonly static SpecialType NUInt = new SpecialType(TypeKind.NUInt, "nuint", isReferenceType: false); /// /// Type representing the result of the C# '__arglist()' expression. /// public readonly static SpecialType ArgList = new SpecialType(TypeKind.ArgList, "__arglist", isReferenceType: null); /// /// A type used for unbound type arguments in partially parameterized types. /// /// public readonly static SpecialType UnboundTypeArgument = new SpecialType(TypeKind.UnboundTypeArgument, "", isReferenceType: null); readonly TypeKind kind; readonly string name; readonly bool? isReferenceType; private SpecialType(TypeKind kind, string name, bool? isReferenceType) { this.kind = kind; this.name = name; this.isReferenceType = isReferenceType; } public override string Name { get { return name; } } public override TypeKind Kind { get { return kind; } } public override bool? IsReferenceType { get { return isReferenceType; } } IType ITypeReference.Resolve(ITypeResolveContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return this; } #pragma warning disable 809 [Obsolete("Please compare special types using the kind property instead.")] public override bool Equals(IType other) { // We consider a special types equal when they have equal types. // However, an unknown type with additional information is not considered to be equal to the SpecialType with TypeKind.Unknown. return other is SpecialType && other.Kind == kind; } public override int GetHashCode() { return 81625621 ^ (int)kind; } public override IType ChangeNullability(Nullability nullability) { if (nullability == base.Nullability || Kind is not TypeKind.Dynamic) return this; else return new NullabilityAnnotatedType(this, nullability); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TaskType.cs ================================================ // Copyright (c) 2010-2014 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Helper class for dealing with System.Threading.Tasks.Task. /// public static class TaskType { /// /// Gets the T in Task<T>. /// Returns void for non-generic Task. /// Any other type is returned unmodified. /// public static IType UnpackTask(ICompilation compilation, IType type) { if (!IsTask(type)) return type; if (type.TypeParameterCount == 0) return compilation.FindType(KnownTypeCode.Void); else return type.TypeArguments[0]; } /// /// Gets whether the specified type is Task or Task<T>. /// public static bool IsTask(IType type) { ITypeDefinition def = type.GetDefinition(); if (def != null) { if (def.KnownTypeCode == KnownTypeCode.Task) return true; if (def.KnownTypeCode == KnownTypeCode.TaskOfT) return type is ParameterizedType; } return false; } /// /// Gets whether the specified type is a Task-like type. /// public static bool IsCustomTask(IType type, out IType builderType) { builderType = null; ITypeDefinition def = type.GetDefinition(); if (def != null) { if (def.TypeParameterCount > 1) return false; var attribute = def.GetAttribute(KnownAttribute.AsyncMethodBuilder); if (attribute == null || attribute.FixedArguments.Length != 1) return false; var arg = attribute.FixedArguments[0]; if (!arg.Type.IsKnownType(KnownTypeCode.Type)) return false; builderType = (IType)arg.Value; return true; } return false; } const string ns = "System.Runtime.CompilerServices"; /// /// Gets whether the specified type is a non-generic Task-like type. /// /// Returns the full type-name of the builder type, if successful. public static bool IsNonGenericTaskType(IType task, out FullTypeName builderTypeName) { if (task.IsKnownType(KnownTypeCode.Task)) { builderTypeName = new TopLevelTypeName(ns, "AsyncTaskMethodBuilder"); return true; } if (IsCustomTask(task, out var builderType)) { builderTypeName = new FullTypeName(builderType.ReflectionName); return builderTypeName.TypeParameterCount == 0; } builderTypeName = default; return false; } /// /// Gets whether the specified type is a generic Task-like type. /// /// Returns the full type-name of the builder type, if successful. public static bool IsGenericTaskType(IType task, out FullTypeName builderTypeName) { if (task.IsKnownType(KnownTypeCode.TaskOfT)) { builderTypeName = new TopLevelTypeName(ns, "AsyncTaskMethodBuilder", 1); return true; } if (IsCustomTask(task, out var builderType)) { builderTypeName = new FullTypeName(builderType.ReflectionName); return builderTypeName.TypeParameterCount == 1; } builderTypeName = default; return false; } /// /// Creates a task type. /// public static IType Create(ICompilation compilation, IType elementType) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (elementType == null) throw new ArgumentNullException(nameof(elementType)); if (elementType.Kind == TypeKind.Void) return compilation.FindType(KnownTypeCode.Task); IType taskType = compilation.FindType(KnownTypeCode.TaskOfT); ITypeDefinition taskTypeDef = taskType.GetDefinition(); if (taskTypeDef != null) return new ParameterizedType(taskTypeDef, new[] { elementType }); else return taskType; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Holds the name of a top-level type. /// This struct cannot refer to nested classes. /// [Serializable] public readonly struct TopLevelTypeName : IEquatable { readonly string namespaceName; readonly string name; readonly int typeParameterCount; public TopLevelTypeName(string namespaceName, string name, int typeParameterCount = 0) { if (namespaceName == null) throw new ArgumentNullException(nameof(namespaceName)); if (name == null) throw new ArgumentNullException(nameof(name)); this.namespaceName = namespaceName; this.name = name; this.typeParameterCount = typeParameterCount; } public TopLevelTypeName(string reflectionName) { int pos = reflectionName.LastIndexOf('.'); if (pos < 0) { namespaceName = string.Empty; name = reflectionName; } else { namespaceName = reflectionName.Substring(0, pos); name = reflectionName.Substring(pos + 1); } name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); } public string Namespace { get { return namespaceName; } } public string Name { get { return name; } } public int TypeParameterCount { get { return typeParameterCount; } } public string ReflectionName { get { StringBuilder b = new StringBuilder(); if (!string.IsNullOrEmpty(namespaceName)) { b.Append(namespaceName); b.Append('.'); } b.Append(name); if (typeParameterCount > 0) { b.Append('`'); b.Append(typeParameterCount); } return b.ToString(); } } public override string ToString() { return this.ReflectionName; } public override bool Equals(object obj) { return (obj is TopLevelTypeName) && Equals((TopLevelTypeName)obj); } public bool Equals(TopLevelTypeName other) { return this.namespaceName == other.namespaceName && this.name == other.name && this.typeParameterCount == other.typeParameterCount; } public override int GetHashCode() { return (name != null ? name.GetHashCode() : 0) ^ (namespaceName != null ? namespaceName.GetHashCode() : 0) ^ typeParameterCount; } public static bool operator ==(TopLevelTypeName lhs, TopLevelTypeName rhs) { return lhs.Equals(rhs); } public static bool operator !=(TopLevelTypeName lhs, TopLevelTypeName rhs) { return !lhs.Equals(rhs); } } [Serializable] public sealed class TopLevelTypeNameComparer : IEqualityComparer { public static readonly TopLevelTypeNameComparer Ordinal = new TopLevelTypeNameComparer(StringComparer.Ordinal); public static readonly TopLevelTypeNameComparer OrdinalIgnoreCase = new TopLevelTypeNameComparer(StringComparer.OrdinalIgnoreCase); public readonly StringComparer NameComparer; public TopLevelTypeNameComparer(StringComparer nameComparer) { this.NameComparer = nameComparer; } public bool Equals(TopLevelTypeName x, TopLevelTypeName y) { return x.TypeParameterCount == y.TypeParameterCount && NameComparer.Equals(x.Name, y.Name) && NameComparer.Equals(x.Namespace, y.Namespace); } public int GetHashCode(TopLevelTypeName obj) { return NameComparer.GetHashCode(obj.Name) ^ NameComparer.GetHashCode(obj.Namespace) ^ obj.TypeParameterCount; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TupleType.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { public sealed class TupleType : AbstractType, ICompilationProvider { public const int RestPosition = 8; const int RestIndex = RestPosition - 1; public ICompilation Compilation { get; } /// /// Gets the underlying System.ValueType type. /// public ParameterizedType UnderlyingType { get; } /// /// Gets the tuple elements. /// public ImmutableArray ElementTypes { get; } /// /// Gets the cardinality of the tuple. /// public int Cardinality => ElementTypes.Length; /// /// Gets the names of the tuple elements. /// public ImmutableArray ElementNames { get; } public TupleType(ICompilation compilation, ImmutableArray elementTypes, ImmutableArray elementNames = default(ImmutableArray), IModule valueTupleAssembly = null) { this.Compilation = compilation; this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes, valueTupleAssembly); this.ElementTypes = elementTypes; if (elementNames.IsDefault) { this.ElementNames = Enumerable.Repeat(null, elementTypes.Length).ToImmutableArray(); } else { Debug.Assert(elementNames.Length == elementTypes.Length); this.ElementNames = elementNames; } } static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray elementTypes, IModule valueTupleAssembly) { int remainder = (elementTypes.Length - 1) % (RestPosition - 1) + 1; Debug.Assert(remainder >= 1 && remainder < RestPosition); int pos = elementTypes.Length - remainder; var type = new ParameterizedType( FindValueTupleType(compilation, valueTupleAssembly, remainder), elementTypes.Slice(pos)); while (pos > 0) { pos -= (RestPosition - 1); type = new ParameterizedType( FindValueTupleType(compilation, valueTupleAssembly, RestPosition), elementTypes.Slice(pos, RestPosition - 1).Concat(new[] { type })); } Debug.Assert(pos == 0); return type; } private static IType FindValueTupleType(ICompilation compilation, IModule valueTupleAssembly, int tpc) { var typeName = new TopLevelTypeName("System", "ValueTuple", tpc); if (valueTupleAssembly != null) { var typeDef = valueTupleAssembly.GetTypeDefinition(typeName); if (typeDef != null) return typeDef; } return compilation.FindType(typeName); } /// /// Gets whether the specified type is a valid underlying type for a tuple. /// Also returns true for tuple types themselves. /// public static bool IsTupleCompatible(IType type, out int tupleCardinality) { switch (type.Kind) { case TypeKind.Tuple: tupleCardinality = ((TupleType)type).ElementTypes.Length; return true; case TypeKind.Class: case TypeKind.Struct: if (type.Namespace == "System" && type.Name == "ValueTuple") { int tpc = type.TypeParameterCount; if (tpc > 0 && tpc < RestPosition) { tupleCardinality = tpc; return true; } else if (tpc == RestPosition && type is ParameterizedType pt) { if (IsTupleCompatible(pt.TypeArguments[RestIndex], out tupleCardinality)) { tupleCardinality += RestPosition - 1; return true; } } } break; } tupleCardinality = 0; return false; } /// /// Construct a tuple type (without element names) from the given underlying type. /// Returns null if the input is not a valid underlying type. /// public static TupleType FromUnderlyingType(ICompilation compilation, IType type) { var elementTypes = GetTupleElementTypes(type); if (elementTypes.Length > 0) { return new TupleType( compilation, elementTypes, valueTupleAssembly: type.GetDefinition()?.ParentModule ); } else { return null; } } /// /// Gets the tuple element types from a tuple type or tuple underlying type. /// public static ImmutableArray GetTupleElementTypes(IType tupleType) { List output = null; if (Collect(tupleType)) { return output.ToImmutableArray(); } else { return default(ImmutableArray); } bool Collect(IType type) { switch (type.Kind) { case TypeKind.Tuple: if (output == null) output = new List(); output.AddRange(((TupleType)type).ElementTypes); return true; case TypeKind.Class: case TypeKind.Struct: if (type.Namespace == "System" && type.Name == "ValueTuple") { if (output == null) output = new List(); int tpc = type.TypeParameterCount; if (tpc > 0 && tpc < RestPosition) { output.AddRange(type.TypeArguments); return true; } else if (tpc == RestPosition) { output.AddRange(type.TypeArguments.Take(RestPosition - 1)); return Collect(type.TypeArguments[RestIndex]); } } break; } return false; } } public override TypeKind Kind => TypeKind.Tuple; public override bool? IsReferenceType => UnderlyingType.IsReferenceType; public override int TypeParameterCount => 0; public override IReadOnlyList TypeParameters => EmptyList.Instance; public override IReadOnlyList TypeArguments => EmptyList.Instance; public override IEnumerable DirectBaseTypes => UnderlyingType.DirectBaseTypes; public override string FullName => UnderlyingType.FullName; public override string Name => UnderlyingType.Name; public override string ReflectionName => UnderlyingType.ReflectionName; public override string Namespace => UnderlyingType.Namespace; public override bool Equals(IType other) { var o = other as TupleType; if (o == null) return false; if (!UnderlyingType.Equals(o.UnderlyingType)) return false; return UnderlyingType.Equals(o.UnderlyingType) && ElementNames.SequenceEqual(o.ElementNames); } public override int GetHashCode() { unchecked { int hash = UnderlyingType.GetHashCode(); foreach (string name in ElementNames) { hash *= 31; hash += name != null ? name.GetHashCode() : 0; } return hash; } } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append('('); for (int i = 0; i < ElementTypes.Length; i++) { if (i > 0) b.Append(", "); b.Append(ElementTypes[i]); if (ElementNames[i] != null) { b.Append(' '); b.Append(ElementNames[i]); } } b.Append(')'); return b.ToString(); } public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTupleType(this); } public override IType VisitChildren(TypeVisitor visitor) { IType[] newElementTypes = null; for (int i = 0; i < ElementTypes.Length; i++) { IType type = ElementTypes[i]; var newType = type.AcceptVisitor(visitor); if (newType != type) { if (newElementTypes == null) { newElementTypes = ElementTypes.ToArray(); } newElementTypes[i] = newType; } } if (newElementTypes != null) { return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.ElementNames, this.GetDefinition()?.ParentModule); } else { return this; } } public override IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetAccessors(filter, options); } public override IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { // CS8181 'new' cannot be used with tuple type. Use a tuple literal expression instead. return EmptyList.Instance; } public override ITypeDefinition GetDefinition() { return UnderlyingType.GetDefinition(); } public override ITypeDefinitionOrUnknown GetDefinitionOrUnknown() { return UnderlyingType.GetDefinitionOrUnknown(); } public override IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetEvents(filter, options); } public override IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { // The fields from the underlying type (Item1..Item7 and Rest) foreach (var field in UnderlyingType.GetFields(filter, options)) { yield return field; } /*for (int i = 0; i < ElementTypes.Length; i++) { var type = ElementTypes[i]; var name = ElementNames[i]; int pos = i + 1; string itemName = "Item" + pos; if (name != itemName && name != null) yield return MakeField(type, name); if (pos >= RestPosition) yield return MakeField(type, itemName); }*/ } /*private IField MakeField(IType type, string name) { var f = new DefaultUnresolvedField(); f.ReturnType = SpecialType.UnknownType; f.Name = name; return new TupleElementField(f, Compilation.TypeResolveContext); }*/ public override IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetMethods(filter, options); } public override IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetMethods(typeArguments, filter, options); } public override IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetNestedTypes(filter, options); } public override IEnumerable GetNestedTypes(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetNestedTypes(typeArguments, filter, options); } public override IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { return UnderlyingType.GetProperties(filter, options); } } public static class TupleTypeExtensions { public static IType TupleUnderlyingTypeOrSelf(this IType type) { var t = (type as TupleType)?.UnderlyingType ?? type; return t.WithoutNullability(); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeKind.cs ================================================ // Copyright (c) 2010-2013 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 // 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. namespace ICSharpCode.Decompiler.TypeSystem { /// /// . /// public enum TypeKind : byte { /// Language-specific type that is not part of NRefactory.TypeSystem itself. Other, /// A or that is a class. Class, /// A or that is an interface. Interface, /// A or that is a struct. Struct, /// A or that is a delegate. /// System.Delegate itself is TypeKind.Class Delegate, /// A that is an enum. /// System.Enum itself is TypeKind.Class Enum, /// The System.Void type. /// Void, /// Type used for invalid expressions and for types whose definition could not be found. /// Unknown, /// The type of the null literal. /// Null, /// The type of expressions without type (except for null literals, which have TypeKind.Null). /// None, /// Type representing the C# 'dynamic' type. /// Dynamic, /// Represents missing type arguments in partially parameterized types. /// /// IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions) UnboundTypeArgument, /// The type is a type parameter. /// TypeParameter, /// An array type /// Array, /// A pointer type /// Pointer, /// A managed reference type /// ByReference, /// Intersection of several types /// Intersection, /// ArgList, /// A C# 7 tuple type. /// E.g. (string, int) /// Note: System.ValueTuple<string, int> is not considered a tuple type. /// /// Tuple, /// /// Modified type, with optional modifier. /// ModOpt, /// /// Modified type, with required modifier. /// ModReq, /// /// C# 9 nint /// NInt, /// /// C# 9 nuint /// NUInt, /// /// C# 9 delegate* /// FunctionPointer, } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs ================================================ // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Text; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Substitutes class and method type parameters. /// public class TypeParameterSubstitution : TypeVisitor { /// /// The identity function. /// public static readonly TypeParameterSubstitution Identity = new TypeParameterSubstitution(null, null); readonly IReadOnlyList classTypeArguments; readonly IReadOnlyList methodTypeArguments; /// /// Creates a new type parameter substitution. /// /// /// The type arguments to substitute for class type parameters. /// Pass null to keep class type parameters unmodified. /// /// /// The type arguments to substitute for method type parameters. /// Pass null to keep method type parameters unmodified. /// public TypeParameterSubstitution(IReadOnlyList classTypeArguments, IReadOnlyList methodTypeArguments) { this.classTypeArguments = classTypeArguments; this.methodTypeArguments = methodTypeArguments; } /// /// Gets the list of class type arguments. /// Returns null if this substitution keeps class type parameters unmodified. /// public IReadOnlyList ClassTypeArguments { get { return classTypeArguments; } } /// /// Gets the list of method type arguments. /// Returns null if this substitution keeps method type parameters unmodified. /// public IReadOnlyList MethodTypeArguments { get { return methodTypeArguments; } } #region Compose /// /// Computes a single TypeParameterSubstitution so that for all types t: /// t.AcceptVisitor(Compose(g, f)) equals t.AcceptVisitor(f).AcceptVisitor(g) /// /// If you consider type parameter substitution to be a function, this is function composition. public static TypeParameterSubstitution Compose(TypeParameterSubstitution g, TypeParameterSubstitution f) { if (g == null) return f; if (f == null || (f.classTypeArguments == null && f.methodTypeArguments == null)) return g; // The composition is a copy of 'f', with 'g' applied on the array elements. // If 'f' has a null list (keeps type parameters unmodified), we have to treat it as // the identity function, and thus use the list from 'g'. var classTypeArguments = f.classTypeArguments != null ? GetComposedTypeArguments(f.classTypeArguments, g) : g.classTypeArguments; var methodTypeArguments = f.methodTypeArguments != null ? GetComposedTypeArguments(f.methodTypeArguments, g) : g.methodTypeArguments; return new TypeParameterSubstitution(classTypeArguments, methodTypeArguments); } static IReadOnlyList GetComposedTypeArguments(IReadOnlyList input, TypeParameterSubstitution substitution) { IType[] result = new IType[input.Count]; for (int i = 0; i < result.Length; i++) { result[i] = input[i].AcceptVisitor(substitution); } return result; } #endregion #region Equals and GetHashCode implementation public bool Equals(TypeParameterSubstitution other, TypeVisitor normalization) { if (other == null) return false; return TypeListEquals(classTypeArguments, other.classTypeArguments, normalization) && TypeListEquals(methodTypeArguments, other.methodTypeArguments, normalization); } public override bool Equals(object obj) { TypeParameterSubstitution other = obj as TypeParameterSubstitution; if (other == null) return false; return TypeListEquals(classTypeArguments, other.classTypeArguments) && TypeListEquals(methodTypeArguments, other.methodTypeArguments); } public override int GetHashCode() { unchecked { return 1124131 * TypeListHashCode(classTypeArguments) + 1821779 * TypeListHashCode(methodTypeArguments); } } static bool TypeListEquals(IReadOnlyList a, IReadOnlyList b) { if (a == b) return true; if (a == null || b == null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; i++) { if (!a[i].Equals(b[i])) return false; } return true; } static bool TypeListEquals(IReadOnlyList a, IReadOnlyList b, TypeVisitor normalization) { if (a == b) return true; if (a == null || b == null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; i++) { var an = a[i].AcceptVisitor(normalization); var bn = b[i].AcceptVisitor(normalization); if (!an.Equals(bn)) return false; } return true; } static int TypeListHashCode(IReadOnlyList obj) { if (obj == null) return 0; unchecked { int hashCode = 1; foreach (var element in obj) { hashCode *= 27; hashCode += element.GetHashCode(); } return hashCode; } } #endregion public override IType VisitTypeParameter(ITypeParameter type) { int index = type.Index; if (classTypeArguments != null && type.OwnerType == SymbolKind.TypeDefinition) { if (index >= 0 && index < classTypeArguments.Count) return classTypeArguments[index]; else return SpecialType.UnknownType; } else if (methodTypeArguments != null && type.OwnerType == SymbolKind.Method) { if (index >= 0 && index < methodTypeArguments.Count) return methodTypeArguments[index]; else return SpecialType.UnknownType; } else { return base.VisitTypeParameter(type); } } public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) { if (type is NullabilityAnnotatedTypeParameter tp) { if (tp.Nullability == Nullability.Nullable) { return VisitTypeParameter(tp).ChangeNullability(Nullability.Nullable); } else { // T! substituted with T=oblivious string should result in oblivious string return VisitTypeParameter(tp); } } else { return base.VisitNullabilityAnnotatedType(type); } } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append('['); bool first = true; if (classTypeArguments != null) { for (int i = 0; i < classTypeArguments.Count; i++) { if (first) first = false; else b.Append(", "); b.Append('`'); b.Append(i); b.Append(" -> "); b.Append(classTypeArguments[i].ReflectionName); } if (classTypeArguments.Count == 0) { if (first) first = false; else b.Append(", "); b.Append("[]"); } } if (methodTypeArguments != null) { for (int i = 0; i < methodTypeArguments.Count; i++) { if (first) first = false; else b.Append(", "); b.Append("``"); b.Append(i); b.Append(" -> "); b.Append(methodTypeArguments[i].ReflectionName); } if (methodTypeArguments.Count == 0) { if (first) first = false; else b.Append(", "); b.Append("[]"); } } b.Append(']'); return b.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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.Immutable; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Allows decoding signatures using decompiler types. /// sealed class TypeProvider : ICompilationProvider, SRM.ISignatureTypeProvider, SRM.ICustomAttributeTypeProvider { readonly MetadataModule module; readonly ICompilation compilation; public TypeProvider(MetadataModule module) { this.module = module; this.compilation = module.Compilation; } public TypeProvider(ICompilation compilation) { this.compilation = compilation; } public ICompilation Compilation => compilation; public IType GetArrayType(IType elementType, SRM.ArrayShape shape) { return new ArrayType(compilation, elementType, shape.Rank); } public IType GetByReferenceType(IType elementType) { return new ByReferenceType(elementType); } public IType GetFunctionPointerType(SRM.MethodSignature signature) { if (signature.Header.IsInstance) { // pointers to member functions are not supported even in C# 9 return compilation.FindType(KnownTypeCode.IntPtr); } return FunctionPointerType.FromSignature(signature, module); } public IType GetGenericInstantiation(IType genericType, ImmutableArray typeArguments) { int tpc = genericType.TypeParameterCount; if (tpc == 0 || tpc != typeArguments.Length) { // This can occur when the genericType is from another assembly, // doesn't have the typical `1 suffix, and that other assembly is not loaded. return genericType; } return new ParameterizedType(genericType, typeArguments); } public IType GetGenericMethodParameter(GenericContext genericContext, int index) { return genericContext.GetMethodTypeParameter(index); } public IType GetGenericTypeParameter(GenericContext genericContext, int index) { return genericContext.GetClassTypeParameter(index); } public IType GetModifiedType(IType modifier, IType unmodifiedType, bool isRequired) { return new ModifiedType(modifier, unmodifiedType, isRequired); } public IType GetPinnedType(IType elementType) { return new PinnedType(elementType); } public IType GetPointerType(IType elementType) { return new PointerType(elementType); } public IType GetPrimitiveType(SRM.PrimitiveTypeCode typeCode) { return compilation.FindType(typeCode.ToKnownTypeCode()); } public IType GetSystemType() { return compilation.FindType(KnownTypeCode.Type); } public IType GetSZArrayType(IType elementType) { return new ArrayType(compilation, elementType); } bool? IsReferenceType(SRM.MetadataReader reader, SRM.EntityHandle handle, byte rawTypeKind) { switch (reader.ResolveSignatureTypeKind(handle, rawTypeKind)) { case SRM.SignatureTypeKind.ValueType: return false; case SRM.SignatureTypeKind.Class: return true; default: return null; } } public IType GetTypeFromDefinition(SRM.MetadataReader reader, SRM.TypeDefinitionHandle handle, byte rawTypeKind) { ITypeDefinition td = module?.GetDefinition(handle); if (td != null) return td; bool? isReferenceType = IsReferenceType(reader, handle, rawTypeKind); return new UnknownType(handle.GetFullTypeName(reader), isReferenceType); } public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind) { IModule resolvedModule = module?.GetDeclaringModule(handle); var fullTypeName = handle.GetFullTypeName(reader); IType type = null; if (resolvedModule != null) { type = resolvedModule.GetTypeDefinition(fullTypeName); } else { foreach (var asm in compilation.Modules) { type = asm.GetTypeDefinition(fullTypeName); if (type != null) return type; } } return type ?? new UnknownType(fullTypeName, IsReferenceType(reader, handle, rawTypeKind)); } public IType GetTypeFromSerializedName(string name) { if (name == null) { return null; } try { return ReflectionHelper.ParseReflectionName(name, module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); } catch (ReflectionNameParseException ex) { throw new BadImageFormatException($"Invalid type name: \"{name}\": {ex.Message}"); } } public IType GetTypeFromSpecification(SRM.MetadataReader reader, GenericContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) { return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); } public SRM.PrimitiveTypeCode GetUnderlyingEnumType(IType type) { var def = type.GetEnumUnderlyingType().GetDefinition(); if (def == null) throw new EnumUnderlyingTypeResolveException(); return def.KnownTypeCode.ToPrimitiveTypeCode(); } public bool IsSystemType(IType type) { return type.IsKnownType(KnownTypeCode.Type); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Reflection.Metadata; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Contains extension methods for the type system. /// public static class TypeSystemExtensions { #region GetAllBaseTypes /// /// Gets all base types. /// /// This is the reflexive and transitive closure of . /// Note that this method does not return all supertypes - doing so is impossible due to contravariance /// (and undesirable for covariance as the list could become very large). /// /// The output is ordered so that base types occur before derived types. /// public static IEnumerable GetAllBaseTypes(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); BaseTypeCollector collector = new BaseTypeCollector(); collector.CollectBaseTypes(type); return collector; } /// /// Gets all non-interface base types. /// /// /// When is an interface, this method will also return base interfaces (return same output as GetAllBaseTypes()). /// /// The output is ordered so that base types occur before derived types. /// public static IEnumerable GetNonInterfaceBaseTypes(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); BaseTypeCollector collector = new BaseTypeCollector(); collector.SkipImplementedInterfaces = true; collector.CollectBaseTypes(type); return collector; } #endregion #region GetAllBaseTypeDefinitions /// /// Gets all base type definitions. /// The output is ordered so that base types occur before derived types. /// /// /// This is equivalent to type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(). /// public static IEnumerable GetAllBaseTypeDefinitions(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); return type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(); } /// /// Gets whether this type definition is derived from the base type definition. /// public static bool IsDerivedFrom(this ITypeDefinition type, ITypeDefinition baseType) { if (type == null) throw new ArgumentNullException(nameof(type)); if (baseType == null) return false; if (type.Compilation != baseType.Compilation) { throw new InvalidOperationException("Both arguments to IsDerivedFrom() must be from the same compilation."); } return type.GetAllBaseTypeDefinitions().Contains(baseType); } /// /// Gets whether this type definition is derived from a given known type. /// public static bool IsDerivedFrom(this ITypeDefinition type, KnownTypeCode baseType) { if (type == null) throw new ArgumentNullException(nameof(type)); if (baseType == KnownTypeCode.None) return false; return IsDerivedFrom(type, type.Compilation.FindType(baseType).GetDefinition()); } #endregion #region GetDeclaringTypeDefinitionsOrThis /// /// Returns all declaring type definitions of this type definition. /// The output is ordered so that inner types occur before outer types. /// public static IEnumerable GetDeclaringTypeDefinitions(this ITypeDefinition definition) { if (definition == null) { throw new ArgumentNullException(nameof(definition)); } while (definition != null) { yield return definition; definition = definition.DeclaringTypeDefinition; } } #endregion #region IsOpen / IsUnbound / IsUnmanagedType / IsKnownType sealed class TypeClassificationVisitor : TypeVisitor { internal bool isOpen; internal IEntity typeParameterOwner; int typeParameterOwnerNestingLevel; public override IType VisitTypeParameter(ITypeParameter type) { isOpen = true; // If both classes and methods, or different classes (nested types) // are involved, find the most specific one int newNestingLevel = GetNestingLevel(type.Owner); if (newNestingLevel > typeParameterOwnerNestingLevel) { typeParameterOwner = type.Owner; typeParameterOwnerNestingLevel = newNestingLevel; } return base.VisitTypeParameter(type); } static int GetNestingLevel(IEntity entity) { int level = 0; while (entity != null) { level++; entity = entity.DeclaringTypeDefinition; } return level; } } /// /// Gets whether the type is an open type (contains type parameters). /// /// /// /// class X<T> { /// List<T> open; /// X<X<T[]>> open; /// X<string> closed; /// int closed; /// } /// /// public static bool IsOpen(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.isOpen; } /// /// Gets the entity that owns the type parameters occurring in the specified type. /// If both class and method type parameters are present, the method is returned. /// Returns null if the specified type is closed. /// /// static IEntity GetTypeParameterOwner(IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.typeParameterOwner; } /// /// Gets whether the type is unbound (is a generic type, but no type arguments were provided). /// /// /// In "typeof(List<Dictionary<,>>)", only the Dictionary is unbound, the List is considered /// bound despite containing an unbound type. /// This method returns false for partially parameterized types (Dictionary<string, >). /// public static bool IsUnbound(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); return (type is ITypeDefinition || type is UnknownType) && type.TypeParameterCount > 0; } /// /// Gets whether the type is considered unmanaged. /// /// /// The C# 6.0 spec lists the following criteria: An unmanaged type is one of the following /// * sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool /// * any enum type /// * any pointer type /// * any user-defined struct type that is not a constructed (= generic) type and contains fields of unmanaged types only. /// /// C# 8.0 removes the restriction that constructed (= generic) types are not considered unmanaged types. /// public static bool IsUnmanagedType(this IType type, bool allowGenerics) { HashSet types = null; return IsUnmanagedTypeInternal(type); bool IsUnmanagedTypeInternal(IType type) { if (type.Kind is TypeKind.Enum or TypeKind.Pointer or TypeKind.FunctionPointer or TypeKind.NInt or TypeKind.NUInt) { return true; } if (type is ITypeParameter tp) { return tp.HasUnmanagedConstraint; } var def = type.GetDefinition(); if (def == null) { return false; } switch (def.KnownTypeCode) { case KnownTypeCode.Void: case KnownTypeCode.Boolean: case KnownTypeCode.Char: case KnownTypeCode.SByte: case KnownTypeCode.Byte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: case KnownTypeCode.Int32: case KnownTypeCode.UInt32: case KnownTypeCode.Int64: case KnownTypeCode.UInt64: case KnownTypeCode.Decimal: case KnownTypeCode.Single: case KnownTypeCode.Double: case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: case KnownTypeCode.TypedReference: //case KnownTypeCode.ArgIterator: //case KnownTypeCode.RuntimeArgumentHandle: return true; } if (type.Kind == TypeKind.Struct) { if (!allowGenerics && def.TypeParameterCount > 0) { return false; } if (types == null) { types = new HashSet(); } types.Add(type); foreach (var f in type.GetFields(f => !f.IsStatic)) { if (types.Contains(f.Type)) { types.Remove(type); return false; } if (!IsUnmanagedTypeInternal(f.Type)) { types.Remove(type); return false; } } types.Remove(type); return true; } return false; } } public static bool IsArrayInterfaceType(this IType type) { if (type == null || type.TypeParameterCount != 1) return false; switch (type.GetDefinition()?.KnownTypeCode) { case KnownTypeCode.IEnumerableOfT: case KnownTypeCode.ICollectionOfT: case KnownTypeCode.IListOfT: case KnownTypeCode.IReadOnlyCollectionOfT: case KnownTypeCode.IReadOnlyListOfT: return true; default: return false; } } public static bool IsInlineArrayType(this IType type) { if (type.Kind != TypeKind.Struct) return false; var td = type.GetDefinition(); if (td == null) return false; return td.HasAttribute(KnownAttribute.InlineArray); } public static int? GetInlineArrayLength(this IType type) { if (type.Kind != TypeKind.Struct) return null; var td = type.GetDefinition(); if (td == null) return null; var attr = td.GetAttribute(KnownAttribute.InlineArray); return attr?.FixedArguments.FirstOrDefault().Value as int?; } public static IType GetInlineArrayElementType(this IType arrayType) { return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType; } /// /// Gets whether the type is the specified known type. /// For generic known types, this returns true for any parameterization of the type (and also for the definition itself). /// public static bool IsKnownType(this IType type, KnownTypeCode knownType) { var def = type.GetDefinition(); return def != null && def.KnownTypeCode == knownType; } /// /// Gets whether the type is the specified known type. /// For generic known types, this returns true for any parameterization of the type (and also for the definition itself). /// internal static bool IsKnownType(this IType type, KnownAttribute knownType) { var def = type.GetDefinition(); return def != null && def.FullTypeName.IsKnownType(knownType); } public static bool IsKnownType(this FullTypeName typeName, KnownTypeCode knownType) { return typeName == KnownTypeReference.Get(knownType).TypeName; } public static bool IsKnownType(this TopLevelTypeName typeName, KnownTypeCode knownType) { return typeName == KnownTypeReference.Get(knownType).TypeName; } internal static bool IsKnownType(this FullTypeName typeName, KnownAttribute knownType) { return typeName == knownType.GetTypeName(); } internal static bool IsKnownType(this TopLevelTypeName typeName, KnownAttribute knownType) { return typeName == knownType.GetTypeName(); } #endregion #region GetDelegateInvokeMethod /// /// Gets the invoke method for a delegate type. /// /// /// Returns null if the type is not a delegate type; or if the invoke method could not be found. /// public static IMethod GetDelegateInvokeMethod(this IType type) { if (type == null) throw new ArgumentNullException(nameof(type)); if (type.Kind == TypeKind.Delegate) return type.GetMethods(m => m.Name == "Invoke", GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); else return null; } #endregion public static IType SkipModifiers(this IType ty) { while (ty is ModifiedType mt) { ty = mt.ElementType; } return ty; } public static IType UnwrapByRef(this IType type) { if (type is ByReferenceType byRef) { type = byRef.ElementType; } return type; } public static bool HasReadonlyModifier(this IMethod accessor) { return accessor.ThisIsRefReadOnly && accessor.DeclaringTypeDefinition?.IsReadOnly == false; } public static bool IsAnyPointer(this TypeKind typeKind) { return typeKind switch { TypeKind.Pointer => true, TypeKind.FunctionPointer => true, _ => false }; } #region GetType/Member /// /// Gets all type definitions in the compilation. /// This may include types from referenced assemblies that are not accessible in the main assembly. /// public static IEnumerable GetAllTypeDefinitions(this ICompilation compilation) { return compilation.Modules.SelectMany(a => a.TypeDefinitions); } /// /// Gets all top level type definitions in the compilation. /// This may include types from referenced assemblies that are not accessible in the main assembly. /// public static IEnumerable GetTopLevelTypeDefinitions(this ICompilation compilation) { return compilation.Modules.SelectMany(a => a.TopLevelTypeDefinitions); } #endregion #region Resolve on collections public static IReadOnlyList Resolve(this IList typeReferences, ITypeResolveContext context) { if (typeReferences == null) throw new ArgumentNullException(nameof(typeReferences)); if (typeReferences.Count == 0) return EmptyList.Instance; else return new ProjectedList(context, typeReferences, (c, t) => t.Resolve(c)); } // There is intentionally no Resolve() overload for IList: the resulting IList would // contains nulls when there are resolve errors. #endregion #region IAssembly.GetTypeDefinition() /// /// Retrieves the specified type in this compilation. /// Returns an if the type cannot be found in this compilation. /// /// /// There can be multiple types with the same full name in a compilation, as a /// full type name is only unique per assembly. /// If there are multiple possible matches, this method will return just one of them. /// When possible, use instead to /// retrieve a type from a specific assembly. /// public static IType FindType(this ICompilation compilation, FullTypeName fullTypeName) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); foreach (IModule asm in compilation.Modules) { ITypeDefinition def = asm.GetTypeDefinition(fullTypeName); if (def != null) return def; } return new UnknownType(fullTypeName); } /// /// Gets the type definition for the specified unresolved type. /// Returns null if the unresolved type does not belong to this assembly. /// public static ITypeDefinition GetTypeDefinition(this IModule module, FullTypeName fullTypeName) { if (module == null) throw new ArgumentNullException("assembly"); TopLevelTypeName topLevelTypeName = fullTypeName.TopLevelTypeName; ITypeDefinition typeDef = module.GetTypeDefinition(topLevelTypeName); if (typeDef == null) return null; int typeParameterCount = topLevelTypeName.TypeParameterCount; for (int i = 0; i < fullTypeName.NestingLevel; i++) { string name = fullTypeName.GetNestedTypeName(i); typeParameterCount += fullTypeName.GetNestedTypeAdditionalTypeParameterCount(i); typeDef = FindNestedType(typeDef, name, typeParameterCount); if (typeDef == null) break; } return typeDef; } static ITypeDefinition FindNestedType(ITypeDefinition typeDef, string name, int typeParameterCount) { foreach (var nestedType in typeDef.NestedTypes) { if (nestedType.Name == name && nestedType.TypeParameterCount == typeParameterCount) return nestedType; } return null; } #endregion #region IEntity.GetAttribute /// /// Gets whether the entity has an attribute of the specified attribute type. /// /// The entity on which the attributes are declared. /// The attribute type to look for. /// /// Specifies whether attributes inherited from base classes and base members /// (if the given in an override) /// should be returned. /// public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit) { if (!inherit) return entity.HasAttribute(attributeType); return GetAttribute(entity, attributeType, inherit) != null; } /// /// Gets the attribute of the specified attribute type. /// /// The entity on which the attributes are declared. /// The attribute type to look for. /// /// Specifies whether attributes inherited from base classes and base members /// (if the given in an override) /// should be returned. /// /// /// Returns the attribute that was found; or null if none was found. /// If inherit is true, an from the entity itself will be returned if possible; /// and the base entity will only be searched if none exists. /// public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit) { if (inherit) { if (entity is ITypeDefinition td) { return InheritanceHelper.GetAttribute(td, attributeType); } else if (entity is IMember m) { return InheritanceHelper.GetAttribute(m, attributeType); } else { throw new NotSupportedException("Unknown entity type"); } } else { return entity.GetAttribute(attributeType); } } /// /// Gets the attributes on the entity. /// /// The entity on which the attributes are declared. /// /// Specifies whether attributes inherited from base classes and base members /// (if the given in an override) /// should be returned. /// /// /// Returns the list of attributes that were found. /// If inherit is true, attributes from the entity itself are returned first; /// followed by attributes inherited from the base entity. /// public static IEnumerable GetAttributes(this IEntity entity, bool inherit) { if (inherit) { if (entity is ITypeDefinition td) { return InheritanceHelper.GetAttributes(td); } else if (entity is IMember m) { return InheritanceHelper.GetAttributes(m); } else { throw new NotSupportedException("Unknown entity type"); } } else { return entity.GetAttributes(); } } #endregion #region IParameter.GetAttribute /// /// Gets whether the parameter has an attribute of the specified attribute type. /// /// The parameter on which the attributes are declared. /// The attribute type to look for. public static bool HasAttribute(this IParameter parameter, KnownAttribute attributeType) { return GetAttribute(parameter, attributeType) != null; } /// /// Gets the attribute of the specified attribute type. /// /// The parameter on which the attributes are declared. /// The attribute type to look for. /// /// Returns the attribute that was found; or null if none was found. /// public static IAttribute GetAttribute(this IParameter parameter, KnownAttribute attributeType) { return parameter.GetAttributes().FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType)); } #endregion #region IParameter.IsDefaultValueAssignmentAllowed /// /// Checks if the parameter is allowed to be assigned a default value. /// /// /// This checks , , , /// and on this parameter and all subsequent parameters. /// If the parameter has no , it does not check subsequent parameters. /// /// The parameter /// True if the has a default value and is allowed to be assigned a default value. public static bool IsDefaultValueAssignmentAllowed(this IParameter parameter) { if (!DefaultValueAssignmentAllowedIndividual(parameter)) return false; if (parameter.Owner == null) return true; // Shouldn't happen, but we need to check for it. for (int i = parameter.Owner.Parameters.Count - 1; i >= 0; i--) { IParameter otherParameter = parameter.Owner.Parameters[i]; if (otherParameter == parameter) break; if (LocalFunctionDecompiler.IsClosureParameter(otherParameter, otherParameter.Owner.DeclaringTypeDefinition)) continue; if (DefaultValueAssignmentAllowedIndividual(otherParameter) || otherParameter.IsParams) continue; return false; } return true; static bool DefaultValueAssignmentAllowedIndividual(IParameter parameter) { return parameter.IsOptional && parameter.HasConstantValueInSignature && parameter.ReferenceKind is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly; } } #endregion #region IAssembly.GetTypeDefinition(string,string,int) /// /// Gets the type definition for a top-level type. /// /// This method uses ordinal name comparison, not the compilation's name comparer. public static ITypeDefinition GetTypeDefinition(this IModule module, string namespaceName, string name, int typeParameterCount = 0) { if (module == null) throw new ArgumentNullException("assembly"); return module.GetTypeDefinition(new TopLevelTypeName(namespaceName, name, typeParameterCount)); } #endregion #region ResolveResult public static ISymbol GetSymbol(this ResolveResult rr) { if (rr is LocalResolveResult) { return ((LocalResolveResult)rr).Variable; } else if (rr is MemberResolveResult) { return ((MemberResolveResult)rr).Member; } else if (rr is TypeResolveResult) { return ((TypeResolveResult)rr).Type.GetDefinition(); } else if (rr is ConversionResolveResult) { return ((ConversionResolveResult)rr).Input.GetSymbol(); } return null; } #endregion public static IType GetElementTypeFromIEnumerable(this IType collectionType, ICompilation compilation, bool allowIEnumerator, out bool? isGeneric) { bool foundNonGenericIEnumerable = false; foreach (IType baseType in collectionType.GetAllBaseTypes()) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null) { KnownTypeCode typeCode = baseTypeDef.KnownTypeCode; if (typeCode == KnownTypeCode.IEnumerableOfT || (allowIEnumerator && typeCode == KnownTypeCode.IEnumeratorOfT)) { ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { isGeneric = true; return pt.GetTypeArgument(0); } } if (typeCode == KnownTypeCode.IEnumerable || (allowIEnumerator && typeCode == KnownTypeCode.IEnumerator)) foundNonGenericIEnumerable = true; } } // System.Collections.IEnumerable found in type hierarchy -> Object is element type. if (foundNonGenericIEnumerable) { isGeneric = false; return compilation.FindType(KnownTypeCode.Object); } isGeneric = null; return SpecialType.UnknownType; } public static bool FullNameIs(this IMember member, string type, string name) { return member.Name == name && member.DeclaringType?.FullName == type; } public static KnownAttribute IsBuiltinAttribute(this ITypeDefinition type) { return KnownAttributes.IsKnownAttributeType(type); } public static IType WithoutNullability(this IType type) { return type.ChangeNullability(Nullability.Oblivious); } public static bool IsDirectImportOf(this ITypeDefinition type, IModule module) { var moduleReference = type.ParentModule; foreach (var asmRef in module.MetadataFile.AssemblyReferences) { if (asmRef.FullName == moduleReference.FullAssemblyName) return true; if (asmRef.Name == "netstandard" && asmRef.GetPublicKeyToken() != null) { var referencedModule = module.Compilation.FindModuleByReference(asmRef); if (referencedModule != null && !referencedModule.MetadataFile.GetTypeForwarder(type.FullTypeName).IsNil) return true; } } return false; } public static IModule FindModuleByReference(this ICompilation compilation, IAssemblyReference assemblyName) { foreach (var module in compilation.Modules) { if (string.Equals(module.FullAssemblyName, assemblyName.FullName, StringComparison.OrdinalIgnoreCase)) { return module; } } foreach (var module in compilation.Modules) { if (string.Equals(module.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) { return module; } } return null; } public static IModule FindModuleByAssemblyNameInfo(this ICompilation compilation, AssemblyNameInfo assemblyName) { foreach (var module in compilation.Modules) { if (string.Equals(module.FullAssemblyName, assemblyName.FullName, StringComparison.OrdinalIgnoreCase)) { return module; } } foreach (var module in compilation.Modules) { if (string.Equals(module.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) { return module; } } return null; } /// /// When given a generic type definition, returns the self-parameterized type /// (i.e. the type of "this" within the type definition). /// When given a non-generic type definition, returns that definition unchanged. /// public static IType AsParameterizedType(this ITypeDefinition td) { if (td.TypeParameterCount == 0) { return td; } else { return new ParameterizedType(td, td.TypeArguments); } } #nullable enable public static INamespace? GetNamespaceByFullName(this ICompilation compilation, string? name) { if (string.IsNullOrEmpty(name)) return compilation.RootNamespace; var parts = name.Split('.'); var ns = compilation.RootNamespace; foreach (var part in parts) { var child = ns.GetChildNamespace(part); if (child == null) return null; ns = child; } return ns; } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs ================================================ // Copyright (c) 2015 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.Reflection; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { public static class TypeUtils { public const int NativeIntSize = 6; // between 4 (Int32) and 8 (Int64) /// /// Gets the size (in bytes) of the input type. /// Returns NativeIntSize for pointer-sized types. /// Returns 0 for structs and other types of unknown size. /// public static int GetSize(this IType type) { switch (type.Kind) { case TypeKind.Pointer: case TypeKind.ByReference: case TypeKind.Class: case TypeKind.NInt: case TypeKind.NUInt: return NativeIntSize; case TypeKind.Enum: type = type.GetEnumUnderlyingType(); break; case TypeKind.ModOpt: case TypeKind.ModReq: return type.SkipModifiers().GetSize(); } var typeDef = type.GetDefinition(); if (typeDef == null) return 0; switch (typeDef.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.SByte: case KnownTypeCode.Byte: return 1; case KnownTypeCode.Char: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: return 2; case KnownTypeCode.Int32: case KnownTypeCode.UInt32: case KnownTypeCode.Single: return 4; case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: return NativeIntSize; case KnownTypeCode.Int64: case KnownTypeCode.UInt64: case KnownTypeCode.Double: return 8; } return 0; } /// /// Gets the size of the input stack type. /// /// /// * 4 for I4, /// * 8 for I8, /// * NativeIntSize for I and Ref, /// * 0 otherwise (O, F, Void, Unknown). /// public static int GetSize(this StackType type) { switch (type) { case StackType.I4: return 4; case StackType.I8: return 8; case StackType.I: case StackType.Ref: return NativeIntSize; default: return 0; } } public static IType GetLargerType(IType type1, IType type2) { return GetSize(type1) >= GetSize(type2) ? type1 : type2; } /// /// Gets whether the type is a small integer type. /// Small integer types are: /// * bool, sbyte, byte, char, short, ushort /// * any enums that have a small integer type as underlying type /// public static bool IsSmallIntegerType(this IType type) { int size = GetSize(type); return size > 0 && size < 4; } /// /// Gets whether the type is a C# small integer type: byte, sbyte, short or ushort. /// /// Unlike the ILAst, C# does not consider bool, char or enums to be small integers. /// public static bool IsCSharpSmallIntegerType(this IType type) { switch (type.GetDefinition()?.KnownTypeCode) { case KnownTypeCode.Byte: case KnownTypeCode.SByte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: return true; default: return false; } } /// /// Gets whether the type is a C# 9 native integer type: nint or nuint. /// /// Returns false for (U)IntPtr. /// public static bool IsCSharpNativeIntegerType(this IType type) { switch (type.Kind) { case TypeKind.NInt: case TypeKind.NUInt: return true; default: return false; } } /// /// Gets whether the type is a C# primitive integer type: byte, sbyte, short, ushort, int, uint, long and ulong. /// /// Unlike the ILAst, C# does not consider bool, enums, pointers or IntPtr to be integers. /// public static bool IsCSharpPrimitiveIntegerType(this IType type) { switch (type.GetDefinition()?.KnownTypeCode) { case KnownTypeCode.Byte: case KnownTypeCode.SByte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: case KnownTypeCode.Int32: case KnownTypeCode.UInt32: case KnownTypeCode.Int64: case KnownTypeCode.UInt64: return true; default: return false; } } /// /// Gets whether the type is an IL integer type. /// Returns true for I4, I, or I8. /// public static bool IsIntegerType(this StackType type) { switch (type) { case StackType.I4: case StackType.I: case StackType.I8: return true; default: return false; } } /// /// Gets whether the type is an IL floating point type. /// Returns true for F4 or F8. /// public static bool IsFloatType(this StackType type) { switch (type) { case StackType.F4: case StackType.F8: return true; default: return false; } } /// /// Gets whether reading/writing an element of accessType from the pointer /// is equivalent to reading/writing an element of the pointer's element type. /// /// /// The access semantics may sligthly differ on read accesses of small integer types, /// due to zero extension vs. sign extension when the signs differ. /// public static bool IsCompatiblePointerTypeForMemoryAccess(IType pointerType, IType accessType) { IType memoryType; if (pointerType is PointerType || pointerType is ByReferenceType) memoryType = ((TypeWithElementType)pointerType).ElementType; else return false; return IsCompatibleTypeForMemoryAccess(memoryType, accessType); } /// /// Gets whether reading/writing an element of accessType from the pointer /// is equivalent to reading/writing an element of the memoryType. /// /// /// The access semantics may sligthly differ on read accesses of small integer types, /// due to zero extension vs. sign extension when the signs differ. /// public static bool IsCompatibleTypeForMemoryAccess(IType memoryType, IType accessType) { memoryType = memoryType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); accessType = accessType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); if (memoryType.Equals(accessType)) return true; // If the types are not equal, the access still might produce equal results in some cases: // 1) Both types are reference types if (memoryType.IsReferenceType == true && accessType.IsReferenceType == true) return true; // 2) Both types are integer types of equal size StackType memoryStackType = memoryType.GetStackType(); StackType accessStackType = accessType.GetStackType(); if (memoryStackType == accessStackType && memoryStackType.IsIntegerType() && GetSize(memoryType) == GetSize(accessType)) return true; // 3) Any of the types is unknown: we assume they are compatible. return memoryType.Kind == TypeKind.Unknown || accessType.Kind == TypeKind.Unknown; } /// /// Gets the stack type corresponding to this type. /// public static StackType GetStackType(this IType type) { switch (type.Kind) { case TypeKind.Unknown: if (type.IsReferenceType == true) { return StackType.O; } return StackType.Unknown; case TypeKind.ByReference: return StackType.Ref; case TypeKind.Pointer: case TypeKind.NInt: case TypeKind.NUInt: case TypeKind.FunctionPointer: return StackType.I; case TypeKind.TypeParameter: // Type parameters are always considered StackType.O, even // though they might be instantiated with primitive types. return StackType.O; case TypeKind.ModOpt: case TypeKind.ModReq: return type.SkipModifiers().GetStackType(); } ITypeDefinition typeDef = type.GetEnumUnderlyingType().GetDefinition(); if (typeDef == null) return StackType.O; switch (typeDef.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.Char: case KnownTypeCode.SByte: case KnownTypeCode.Byte: case KnownTypeCode.Int16: case KnownTypeCode.UInt16: case KnownTypeCode.Int32: case KnownTypeCode.UInt32: return StackType.I4; case KnownTypeCode.Int64: case KnownTypeCode.UInt64: return StackType.I8; case KnownTypeCode.Single: return StackType.F4; case KnownTypeCode.Double: return StackType.F8; case KnownTypeCode.Void: return StackType.Void; case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: return StackType.I; default: return StackType.O; } } /// /// If type is an enumeration type, returns the underlying type. /// Otherwise, returns type unmodified. /// public static IType GetEnumUnderlyingType(this IType type) { type = type.SkipModifiers(); return (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type; } /// /// Gets the sign of the input type. /// /// /// Integer types (including IntPtr/UIntPtr) return the sign as expected. /// Floating point types and decimal are considered to be signed. /// char, bool and pointer types (e.g. void*) are unsigned. /// Enums have a sign based on their underlying type. /// All other types return Sign.None. /// public static Sign GetSign(this IType type) { type = type.SkipModifiers(); switch (type.Kind) { case TypeKind.Pointer: case TypeKind.NUInt: case TypeKind.FunctionPointer: return Sign.Unsigned; case TypeKind.NInt: return Sign.Signed; } var typeDef = type.GetEnumUnderlyingType().GetDefinition(); if (typeDef == null) return Sign.None; switch (typeDef.KnownTypeCode) { case KnownTypeCode.SByte: case KnownTypeCode.Int16: case KnownTypeCode.Int32: case KnownTypeCode.Int64: case KnownTypeCode.IntPtr: case KnownTypeCode.Single: case KnownTypeCode.Double: case KnownTypeCode.Decimal: return Sign.Signed; case KnownTypeCode.UIntPtr: case KnownTypeCode.Char: case KnownTypeCode.Boolean: case KnownTypeCode.Byte: case KnownTypeCode.UInt16: case KnownTypeCode.UInt32: case KnownTypeCode.UInt64: return Sign.Unsigned; default: return Sign.None; } } /// /// Maps the KnownTypeCode values to the corresponding PrimitiveTypes. /// public static PrimitiveType ToPrimitiveType(this KnownTypeCode knownTypeCode) { switch (knownTypeCode) { case KnownTypeCode.SByte: return PrimitiveType.I1; case KnownTypeCode.Int16: return PrimitiveType.I2; case KnownTypeCode.Int32: return PrimitiveType.I4; case KnownTypeCode.Int64: return PrimitiveType.I8; case KnownTypeCode.Single: return PrimitiveType.R4; case KnownTypeCode.Double: return PrimitiveType.R8; case KnownTypeCode.Byte: return PrimitiveType.U1; case KnownTypeCode.UInt16: case KnownTypeCode.Char: return PrimitiveType.U2; case KnownTypeCode.UInt32: return PrimitiveType.U4; case KnownTypeCode.UInt64: return PrimitiveType.U8; case KnownTypeCode.IntPtr: return PrimitiveType.I; case KnownTypeCode.UIntPtr: return PrimitiveType.U; default: return PrimitiveType.None; } } /// /// Maps the KnownTypeCode values to the corresponding PrimitiveTypes. /// public static PrimitiveType ToPrimitiveType(this IType type) { type = type.SkipModifiers(); switch (type.Kind) { case TypeKind.Unknown: return PrimitiveType.Unknown; case TypeKind.ByReference: return PrimitiveType.Ref; case TypeKind.NInt: case TypeKind.FunctionPointer: return PrimitiveType.I; case TypeKind.NUInt: return PrimitiveType.U; } var def = type.GetEnumUnderlyingType().GetDefinition(); return def != null ? def.KnownTypeCode.ToPrimitiveType() : PrimitiveType.None; } /// /// Maps the PrimitiveType values to the corresponding KnownTypeCodes. /// public static KnownTypeCode ToKnownTypeCode(this PrimitiveType primitiveType) { switch (primitiveType) { case PrimitiveType.I1: return KnownTypeCode.SByte; case PrimitiveType.I2: return KnownTypeCode.Int16; case PrimitiveType.I4: return KnownTypeCode.Int32; case PrimitiveType.I8: return KnownTypeCode.Int64; case PrimitiveType.R4: return KnownTypeCode.Single; case PrimitiveType.R8: case PrimitiveType.R: return KnownTypeCode.Double; case PrimitiveType.U1: return KnownTypeCode.Byte; case PrimitiveType.U2: return KnownTypeCode.UInt16; case PrimitiveType.U4: return KnownTypeCode.UInt32; case PrimitiveType.U8: return KnownTypeCode.UInt64; case PrimitiveType.I: return KnownTypeCode.IntPtr; case PrimitiveType.U: return KnownTypeCode.UIntPtr; default: return KnownTypeCode.None; } } public static KnownTypeCode ToKnownTypeCode(this StackType stackType, Sign sign = Sign.None) { switch (stackType) { case StackType.I4: return sign == Sign.Unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32; case StackType.I8: return sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64; case StackType.I: return sign == Sign.Unsigned ? KnownTypeCode.UIntPtr : KnownTypeCode.IntPtr; case StackType.F4: return KnownTypeCode.Single; case StackType.F8: return KnownTypeCode.Double; case StackType.O: return KnownTypeCode.Object; case StackType.Void: return KnownTypeCode.Void; default: return KnownTypeCode.None; } } public static PrimitiveType ToPrimitiveType(this StackType stackType, Sign sign = Sign.None) { switch (stackType) { case StackType.I4: return sign == Sign.Unsigned ? PrimitiveType.U4 : PrimitiveType.I4; case StackType.I8: return sign == Sign.Unsigned ? PrimitiveType.U8 : PrimitiveType.I8; case StackType.I: return sign == Sign.Unsigned ? PrimitiveType.U : PrimitiveType.I; case StackType.F4: return PrimitiveType.R4; case StackType.F8: return PrimitiveType.R8; case StackType.Ref: return PrimitiveType.Ref; case StackType.Unknown: return PrimitiveType.Unknown; default: return PrimitiveType.None; } } } public enum Sign : byte { None, Signed, Unsigned } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Base class for the visitor pattern on . /// public abstract class TypeVisitor { public virtual IType VisitTypeDefinition(ITypeDefinition type) { return type.VisitChildren(this); } public virtual IType VisitTypeParameter(ITypeParameter type) { return type.VisitChildren(this); } public virtual IType VisitParameterizedType(ParameterizedType type) { return type.VisitChildren(this); } public virtual IType VisitArrayType(ArrayType type) { return type.VisitChildren(this); } public virtual IType VisitPointerType(PointerType type) { return type.VisitChildren(this); } public virtual IType VisitByReferenceType(ByReferenceType type) { return type.VisitChildren(this); } public virtual IType VisitTupleType(TupleType type) { return type.VisitChildren(this); } public virtual IType VisitOtherType(IType type) { return type.VisitChildren(this); } public virtual IType VisitModReq(ModifiedType type) { return type.VisitChildren(this); } public virtual IType VisitModOpt(ModifiedType type) { return type.VisitChildren(this); } public virtual IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) { return type.VisitChildren(this); } public virtual IType VisitFunctionPointerType(FunctionPointerType type) { return type.VisitChildren(this); } } } ================================================ FILE: ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Reflection; using System.Text; using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Used when calling a vararg method. Stores the actual parameter types being passed. /// public class VarArgInstanceMethod : IMethod { readonly IMethod baseMethod; readonly IParameter[] parameters; public VarArgInstanceMethod(IMethod baseMethod, IEnumerable varArgTypes) { this.baseMethod = baseMethod; var paramList = new List(baseMethod.Parameters); Debug.Assert(paramList.Last().Type.Kind == TypeKind.ArgList); paramList.RemoveAt(paramList.Count - 1); foreach (IType varArg in varArgTypes) { paramList.Add(new DefaultParameter(varArg, name: string.Empty, owner: this)); } this.parameters = paramList.ToArray(); } public IMethod BaseMethod => baseMethod; public int RegularParameterCount { get { return baseMethod.Parameters.Count - 1; } } public IReadOnlyList Parameters { get { return parameters; } } public override bool Equals(object obj) { VarArgInstanceMethod other = obj as VarArgInstanceMethod; return other != null && baseMethod.Equals(other.baseMethod); } public override int GetHashCode() { return baseMethod.GetHashCode(); } public bool Equals(IMember obj, TypeVisitor typeNormalization) { VarArgInstanceMethod other = obj as VarArgInstanceMethod; return other != null && baseMethod.Equals(other.baseMethod, typeNormalization); } public override string ToString() { StringBuilder b = new StringBuilder("["); b.Append(this.SymbolKind); if (this.DeclaringType != null) { b.Append(this.DeclaringType.ReflectionName); b.Append('.'); } b.Append(this.Name); if (this.TypeParameters.Count > 0) { b.Append("``"); b.Append(this.TypeParameters.Count); } b.Append('('); for (int i = 0; i < this.Parameters.Count; i++) { if (i > 0) b.Append(", "); if (i == this.RegularParameterCount) b.Append("..., "); b.Append(this.Parameters[i].Type.ReflectionName); } if (this.Parameters.Count == this.RegularParameterCount) { b.Append(", ..."); } b.Append("):"); b.Append(this.ReturnType.ReflectionName); b.Append(']'); return b.ToString(); } #region IMethod implementation public IMethod Specialize(TypeParameterSubstitution substitution) { return new VarArgInstanceMethod( baseMethod.Specialize(substitution), parameters.Skip(baseMethod.Parameters.Count - 1).Select(p => p.Type.AcceptVisitor(substitution)).ToList()); } IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); bool IEntity.HasAttribute(KnownAttribute attribute) => baseMethod.HasAttribute(attribute); IAttribute IEntity.GetAttribute(KnownAttribute attribute) => baseMethod.GetAttribute(attribute); IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly; bool IMethod.IsInitOnly => baseMethod.IsInitOnly; public IReadOnlyList TypeParameters { get { return baseMethod.TypeParameters; } } public IReadOnlyList TypeArguments { get { return baseMethod.TypeArguments; } } public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; public bool IsExtensionMethod { get { return baseMethod.IsExtensionMethod; } } bool IMethod.IsLocalFunction { get { return baseMethod.IsLocalFunction; } } public bool IsConstructor { get { return baseMethod.IsConstructor; } } public bool IsDestructor { get { return baseMethod.IsDestructor; } } public bool IsOperator { get { return baseMethod.IsOperator; } } public bool HasBody { get { return baseMethod.HasBody; } } public bool IsAccessor => baseMethod.IsAccessor; public IMember AccessorOwner => baseMethod.AccessorOwner; public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; public IMethod ReducedFrom { get { return baseMethod.ReducedFrom; } } #endregion #region IMember implementation IMember IMember.Specialize(TypeParameterSubstitution substitution) { return Specialize(substitution); } public IMember MemberDefinition { get { return baseMethod.MemberDefinition; } } public IType ReturnType { get { return baseMethod.ReturnType; } } public IEnumerable ExplicitlyImplementedInterfaceMembers { get { return baseMethod.ExplicitlyImplementedInterfaceMembers; } } public bool IsExplicitInterfaceImplementation { get { return baseMethod.IsExplicitInterfaceImplementation; } } public bool IsVirtual { get { return baseMethod.IsVirtual; } } public bool IsOverride { get { return baseMethod.IsOverride; } } public bool IsOverridable { get { return baseMethod.IsOverridable; } } public TypeParameterSubstitution Substitution { get { return baseMethod.Substitution; } } #endregion #region ISymbol implementation public SymbolKind SymbolKind { get { return baseMethod.SymbolKind; } } public string Name { get { return baseMethod.Name; } } #endregion #region IEntity implementation public ITypeDefinition DeclaringTypeDefinition { get { return baseMethod.DeclaringTypeDefinition; } } public IType DeclaringType { get { return baseMethod.DeclaringType; } } public IModule ParentModule { get { return baseMethod.ParentModule; } } public bool IsStatic { get { return baseMethod.IsStatic; } } public bool IsAbstract { get { return baseMethod.IsAbstract; } } public bool IsSealed { get { return baseMethod.IsSealed; } } #endregion #region IHasAccessibility implementation public Accessibility Accessibility { get { return baseMethod.Accessibility; } } #endregion #region INamedElement implementation public string FullName { get { return baseMethod.FullName; } } public string ReflectionName { get { return baseMethod.ReflectionName; } } public string Namespace { get { return baseMethod.Namespace; } } #endregion #region ICompilationProvider implementation public ICompilation Compilation { get { return baseMethod.Compilation; } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Util/BitOperations.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #if !NET8_0_OR_GREATER namespace System.Numerics { internal static class BitOperations { private static ReadOnlySpan TrailingZeroCountDeBruijn => new byte[32] { 00, 01, 28, 02, 29, 14, 24, 03, 30, 22, 20, 15, 25, 17, 04, 08, 31, 27, 13, 23, 21, 19, 16, 07, 26, 12, 18, 06, 11, 05, 10, 09 }; public static int TrailingZeroCount(uint value) { // Unguarded fallback contract is 0->0, BSF contract is 0->undefined if (value == 0) { return 32; } unchecked { // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check return Unsafe.AddByteOffset( // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u ref MemoryMarshal.GetReference(TrailingZeroCountDeBruijn), // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here (IntPtr)(int)(((value & (uint)-(int)value) * 0x077CB531u) >> 27)); // Multi-cast mitigates redundant conv.u8 } } public static int TrailingZeroCount(ulong value) { unchecked { uint lo = (uint)value; if (lo == 0) { return 32 + TrailingZeroCount((uint)(value >> 32)); } return TrailingZeroCount(lo); } } } } #endif ================================================ FILE: ICSharpCode.Decompiler/Util/BitSet.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using System.Text; namespace ICSharpCode.Decompiler.Util { /// /// Improved version of BitArray /// public class BitSet { const int BitsPerWord = 64; const int Log2BitsPerWord = 6; const ulong Mask = 0xffffffffffffffffUL; readonly ulong[] words; static int WordIndex(int bitIndex) { Debug.Assert(bitIndex >= 0); return bitIndex >> Log2BitsPerWord; } /// /// Creates a new bitset, where initially all bits are zero. /// public BitSet(int capacity) { this.words = new ulong[Math.Max(1, WordIndex(capacity + BitsPerWord - 1))]; } private BitSet(ulong[] bits) { this.words = bits; } public BitSet Clone() { return new BitSet((ulong[])words.Clone()); } public bool this[int index] { get { return (words[WordIndex(index)] & (1UL << index)) != 0; } set { if (value) Set(index); else Clear(index); } } /// /// Gets whether at least one bit is set. /// public bool Any() { for (int i = 0; i < words.Length; i++) { if (words[i] != 0) return true; } return false; } /// /// Gets whether all bits in the specified range are set. /// public bool All(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return true; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { return (words[startWordIndex] & (startMask & endMask)) == (startMask & endMask); } else { if ((words[startWordIndex] & startMask) != startMask) return false; for (int i = startWordIndex + 1; i < endWordIndex; i++) { if (words[i] != ulong.MaxValue) return false; } return (words[endWordIndex] & endMask) == endMask; } } /// /// Gets whether both bitsets have the same content. /// public bool SetEquals(BitSet other) { Debug.Assert(words.Length == other.words.Length); for (int i = 0; i < words.Length; i++) { if (words[i] != other.words[i]) return false; } return true; } /// /// Gets whether this set is a subset of other, or equal. /// public bool IsSubsetOf(BitSet other) { for (int i = 0; i < words.Length; i++) { if ((words[i] & ~other.words[i]) != 0) return false; } return true; } /// /// Gets whether this set is a superset of other, or equal. /// public bool IsSupersetOf(BitSet other) { return other.IsSubsetOf(this); } public bool IsProperSubsetOf(BitSet other) { return IsSubsetOf(other) && !SetEquals(other); } public bool IsProperSupersetOf(BitSet other) { return IsSupersetOf(other) && !SetEquals(other); } /// /// Gets whether at least one bit is set in both bitsets. /// public bool Overlaps(BitSet other) { for (int i = 0; i < words.Length; i++) { if ((words[i] & other.words[i]) != 0) return true; } return false; } public void UnionWith(BitSet other) { Debug.Assert(words.Length == other.words.Length); for (int i = 0; i < words.Length; i++) { words[i] |= other.words[i]; } } public void IntersectWith(BitSet other) { for (int i = 0; i < words.Length; i++) { words[i] &= other.words[i]; } } public void Set(int index) { words[WordIndex(index)] |= (1UL << index); } /// /// Sets all bits i; where startIndex <= i < endIndex. /// public void Set(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { words[startWordIndex] |= (startMask & endMask); } else { words[startWordIndex] |= startMask; for (int i = startWordIndex + 1; i < endWordIndex; i++) { words[i] = ulong.MaxValue; } words[endWordIndex] |= endMask; } } // Note: intentionally no SetAll(), because it would also set the // extra bits (due to the capacity being rounded up to a full word). public void Clear(int index) { words[WordIndex(index)] &= ~(1UL << index); } /// /// Clear all bits i; where startIndex <= i < endIndex. /// public void Clear(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { words[startWordIndex] &= ~(startMask & endMask); } else { words[startWordIndex] &= ~startMask; for (int i = startWordIndex + 1; i < endWordIndex; i++) { words[i] = 0; } words[endWordIndex] &= ~endMask; } } public void ClearAll() { for (int i = 0; i < words.Length; i++) { words[i] = 0; } } public int NextSetBit(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return -1; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { ulong maskedWord = words[startWordIndex] & startMask & endMask; if (maskedWord != 0) { return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } } else { ulong maskedWord = words[startWordIndex] & startMask; if (maskedWord != 0) { return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } for (int i = startWordIndex + 1; i < endWordIndex; i++) { maskedWord = words[i]; if (maskedWord != 0) { return i * 64 + BitOperations.TrailingZeroCount(maskedWord); } } maskedWord = words[endWordIndex] & endMask; if (maskedWord != 0) { return endWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } } return -1; } public IEnumerable SetBits(int startIndex, int endIndex) { while (true) { int next = NextSetBit(startIndex, endIndex); if (next == -1) break; yield return next; startIndex = next + 1; } } public void ReplaceWith(BitSet incoming) { Debug.Assert(words.Length == incoming.words.Length); Array.Copy(incoming.words, 0, words, 0, words.Length); } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append('{'); for (int i = 0; i < words.Length * BitsPerWord; i++) { if (this[i]) { if (b.Length > 1) b.Append(", "); if (b.Length > 500) { b.Append("..."); break; } b.Append(i); } } b.Append('}'); return b.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/BusyManager.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Util { /// /// This class is used to prevent stack overflows by representing a 'busy' flag /// that prevents reentrance when another call is running. /// However, using a simple 'bool busy' is not thread-safe, so we use a /// thread-static BusyManager. /// public static class BusyManager { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Should always be used with 'var'")] public struct BusyLock : IDisposable { public static readonly BusyLock Failed = new BusyLock(null); readonly List? objectList; internal BusyLock(List? objectList) { this.objectList = objectList; } public bool Success { get { return objectList != null; } } public void Dispose() { if (objectList != null) { objectList.RemoveAt(objectList.Count - 1); } } } [ThreadStatic] static List? _activeObjects; public static BusyLock Enter(object? obj) { List? activeObjects = _activeObjects; if (activeObjects == null) activeObjects = _activeObjects = new List(); for (int i = 0; i < activeObjects.Count; i++) { if (activeObjects[i] == obj) return BusyLock.Failed; } activeObjects.Add(obj); return new BusyLock(activeObjects); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { /// /// Static helper method for converting between primitive types. /// public static class CSharpPrimitiveCast { /// /// Performs a conversion between primitive types. /// Unfortunately we cannot use Convert.ChangeType because it has different semantics /// (e.g. rounding behavior for floats, overflow, etc.), so we write down every possible primitive C# cast /// and let the compiler figure out the exact semantics. /// And we have to do everything twice, once in a checked-block, once in an unchecked-block. /// /// Overflow checking is enabled and an overflow occurred. /// The cast is invalid, e.g. casting a boolean to an integer. public static object Cast(TypeCode targetType, object input, bool checkForOverflow) { if (input == null) return null; if (checkForOverflow) return CSharpPrimitiveCastChecked(targetType, input); else return CSharpPrimitiveCastUnchecked(targetType, input); } static object CSharpPrimitiveCastChecked(TypeCode targetType, object input) { checked { TypeCode sourceType = Type.GetTypeCode(input.GetType()); if (sourceType == targetType) return input; switch (targetType) { case TypeCode.Char: switch (sourceType) { case TypeCode.SByte: return (char)(sbyte)input; case TypeCode.Byte: return (char)(byte)input; case TypeCode.Int16: return (char)(short)input; case TypeCode.UInt16: return (char)(ushort)input; case TypeCode.Int32: return (char)(int)input; case TypeCode.UInt32: return (char)(uint)input; case TypeCode.Int64: return (char)(long)input; case TypeCode.UInt64: return (char)(ulong)input; case TypeCode.Single: return (char)(float)input; case TypeCode.Double: return (char)(double)input; case TypeCode.Decimal: return (char)(decimal)input; case TypeCode.Boolean: return (char)((bool)input ? 1 : 0); } break; case TypeCode.SByte: switch (sourceType) { case TypeCode.Char: return (sbyte)(char)input; case TypeCode.Byte: return (sbyte)(byte)input; case TypeCode.Int16: return (sbyte)(short)input; case TypeCode.UInt16: return (sbyte)(ushort)input; case TypeCode.Int32: return (sbyte)(int)input; case TypeCode.UInt32: return (sbyte)(uint)input; case TypeCode.Int64: return (sbyte)(long)input; case TypeCode.UInt64: return (sbyte)(ulong)input; case TypeCode.Single: return (sbyte)(float)input; case TypeCode.Double: return (sbyte)(double)input; case TypeCode.Decimal: return (sbyte)(decimal)input; case TypeCode.Boolean: return (sbyte)((bool)input ? 1 : 0); } break; case TypeCode.Byte: switch (sourceType) { case TypeCode.Char: return (byte)(char)input; case TypeCode.SByte: return (byte)(sbyte)input; case TypeCode.Int16: return (byte)(short)input; case TypeCode.UInt16: return (byte)(ushort)input; case TypeCode.Int32: return (byte)(int)input; case TypeCode.UInt32: return (byte)(uint)input; case TypeCode.Int64: return (byte)(long)input; case TypeCode.UInt64: return (byte)(ulong)input; case TypeCode.Single: return (byte)(float)input; case TypeCode.Double: return (byte)(double)input; case TypeCode.Decimal: return (byte)(decimal)input; case TypeCode.Boolean: return (byte)((bool)input ? 1 : 0); } break; case TypeCode.Int16: switch (sourceType) { case TypeCode.Char: return (short)(char)input; case TypeCode.SByte: return (short)(sbyte)input; case TypeCode.Byte: return (short)(byte)input; case TypeCode.UInt16: return (short)(ushort)input; case TypeCode.Int32: return (short)(int)input; case TypeCode.UInt32: return (short)(uint)input; case TypeCode.Int64: return (short)(long)input; case TypeCode.UInt64: return (short)(ulong)input; case TypeCode.Single: return (short)(float)input; case TypeCode.Double: return (short)(double)input; case TypeCode.Decimal: return (short)(decimal)input; case TypeCode.Boolean: return (short)((bool)input ? 1 : 0); } break; case TypeCode.UInt16: switch (sourceType) { case TypeCode.Char: return (ushort)(char)input; case TypeCode.SByte: return (ushort)(sbyte)input; case TypeCode.Byte: return (ushort)(byte)input; case TypeCode.Int16: return (ushort)(short)input; case TypeCode.Int32: return (ushort)(int)input; case TypeCode.UInt32: return (ushort)(uint)input; case TypeCode.Int64: return (ushort)(long)input; case TypeCode.UInt64: return (ushort)(ulong)input; case TypeCode.Single: return (ushort)(float)input; case TypeCode.Double: return (ushort)(double)input; case TypeCode.Decimal: return (ushort)(decimal)input; case TypeCode.Boolean: return (ushort)((bool)input ? 1 : 0); } break; case TypeCode.Int32: switch (sourceType) { case TypeCode.Char: return (int)(char)input; case TypeCode.SByte: return (int)(sbyte)input; case TypeCode.Byte: return (int)(byte)input; case TypeCode.Int16: return (int)(short)input; case TypeCode.UInt16: return (int)(ushort)input; case TypeCode.UInt32: return (int)(uint)input; case TypeCode.Int64: return (int)(long)input; case TypeCode.UInt64: return (int)(ulong)input; case TypeCode.Single: return (int)(float)input; case TypeCode.Double: return (int)(double)input; case TypeCode.Decimal: return (int)(decimal)input; case TypeCode.Boolean: return (int)((bool)input ? 1 : 0); } break; case TypeCode.UInt32: switch (sourceType) { case TypeCode.Char: return (uint)(char)input; case TypeCode.SByte: return (uint)(sbyte)input; case TypeCode.Byte: return (uint)(byte)input; case TypeCode.Int16: return (uint)(short)input; case TypeCode.UInt16: return (uint)(ushort)input; case TypeCode.Int32: return (uint)(int)input; case TypeCode.Int64: return (uint)(long)input; case TypeCode.UInt64: return (uint)(ulong)input; case TypeCode.Single: return (uint)(float)input; case TypeCode.Double: return (uint)(double)input; case TypeCode.Decimal: return (uint)(decimal)input; case TypeCode.Boolean: return (uint)((bool)input ? 1 : 0); } break; case TypeCode.Int64: switch (sourceType) { case TypeCode.Char: return (long)(char)input; case TypeCode.SByte: return (long)(sbyte)input; case TypeCode.Byte: return (long)(byte)input; case TypeCode.Int16: return (long)(short)input; case TypeCode.UInt16: return (long)(ushort)input; case TypeCode.Int32: return (long)(int)input; case TypeCode.UInt32: return (long)(uint)input; case TypeCode.UInt64: return (long)(ulong)input; case TypeCode.Single: return (long)(float)input; case TypeCode.Double: return (long)(double)input; case TypeCode.Decimal: return (long)(decimal)input; case TypeCode.Boolean: return (long)((bool)input ? 1 : 0); } break; case TypeCode.UInt64: switch (sourceType) { case TypeCode.Char: return (ulong)(char)input; case TypeCode.SByte: return (ulong)(sbyte)input; case TypeCode.Byte: return (ulong)(byte)input; case TypeCode.Int16: return (ulong)(short)input; case TypeCode.UInt16: return (ulong)(ushort)input; case TypeCode.Int32: return (ulong)(int)input; case TypeCode.UInt32: return (ulong)(uint)input; case TypeCode.Int64: return (ulong)(long)input; case TypeCode.Single: return (ulong)(float)input; case TypeCode.Double: return (ulong)(double)input; case TypeCode.Decimal: return (ulong)(decimal)input; case TypeCode.Boolean: return (ulong)((bool)input ? 1 : 0); } break; case TypeCode.Single: switch (sourceType) { case TypeCode.Char: return (float)(char)input; case TypeCode.SByte: return (float)(sbyte)input; case TypeCode.Byte: return (float)(byte)input; case TypeCode.Int16: return (float)(short)input; case TypeCode.UInt16: return (float)(ushort)input; case TypeCode.Int32: return (float)(int)input; case TypeCode.UInt32: return (float)(uint)input; case TypeCode.Int64: return (float)(long)input; case TypeCode.UInt64: return (float)(ulong)input; case TypeCode.Double: return (float)(double)input; case TypeCode.Decimal: return (float)(decimal)input; case TypeCode.Boolean: return (float)((bool)input ? 1 : 0); } break; case TypeCode.Double: switch (sourceType) { case TypeCode.Char: return (double)(char)input; case TypeCode.SByte: return (double)(sbyte)input; case TypeCode.Byte: return (double)(byte)input; case TypeCode.Int16: return (double)(short)input; case TypeCode.UInt16: return (double)(ushort)input; case TypeCode.Int32: return (double)(int)input; case TypeCode.UInt32: return (double)(uint)input; case TypeCode.Int64: return (double)(long)input; case TypeCode.UInt64: return (double)(ulong)input; case TypeCode.Single: return (double)(float)input; case TypeCode.Decimal: return (double)(decimal)input; case TypeCode.Boolean: return (double)((bool)input ? 1 : 0); } break; case TypeCode.Decimal: switch (sourceType) { case TypeCode.Char: return (decimal)(char)input; case TypeCode.SByte: return (decimal)(sbyte)input; case TypeCode.Byte: return (decimal)(byte)input; case TypeCode.Int16: return (decimal)(short)input; case TypeCode.UInt16: return (decimal)(ushort)input; case TypeCode.Int32: return (decimal)(int)input; case TypeCode.UInt32: return (decimal)(uint)input; case TypeCode.Int64: return (decimal)(long)input; case TypeCode.UInt64: return (decimal)(ulong)input; case TypeCode.Single: return (decimal)(float)input; case TypeCode.Double: return (decimal)(double)input; case TypeCode.Boolean: return (decimal)((bool)input ? 1 : 0); } break; } throw new InvalidCastException("Cast from " + sourceType + " to " + targetType + "not supported."); } } static object CSharpPrimitiveCastUnchecked(TypeCode targetType, object input) { unchecked { TypeCode sourceType = Type.GetTypeCode(input.GetType()); if (sourceType == targetType) return input; switch (targetType) { case TypeCode.Char: switch (sourceType) { case TypeCode.SByte: return (char)(sbyte)input; case TypeCode.Byte: return (char)(byte)input; case TypeCode.Int16: return (char)(short)input; case TypeCode.UInt16: return (char)(ushort)input; case TypeCode.Int32: return (char)(int)input; case TypeCode.UInt32: return (char)(uint)input; case TypeCode.Int64: return (char)(long)input; case TypeCode.UInt64: return (char)(ulong)input; case TypeCode.Single: return (char)(float)input; case TypeCode.Double: return (char)(double)input; case TypeCode.Decimal: return (char)(decimal)input; case TypeCode.Boolean: return (char)((bool)input ? 1 : 0); } break; case TypeCode.SByte: switch (sourceType) { case TypeCode.Char: return (sbyte)(char)input; case TypeCode.Byte: return (sbyte)(byte)input; case TypeCode.Int16: return (sbyte)(short)input; case TypeCode.UInt16: return (sbyte)(ushort)input; case TypeCode.Int32: return (sbyte)(int)input; case TypeCode.UInt32: return (sbyte)(uint)input; case TypeCode.Int64: return (sbyte)(long)input; case TypeCode.UInt64: return (sbyte)(ulong)input; case TypeCode.Single: return (sbyte)(float)input; case TypeCode.Double: return (sbyte)(double)input; case TypeCode.Decimal: return (sbyte)(decimal)input; case TypeCode.Boolean: return (sbyte)((bool)input ? 1 : 0); } break; case TypeCode.Byte: switch (sourceType) { case TypeCode.Char: return (byte)(char)input; case TypeCode.SByte: return (byte)(sbyte)input; case TypeCode.Int16: return (byte)(short)input; case TypeCode.UInt16: return (byte)(ushort)input; case TypeCode.Int32: return (byte)(int)input; case TypeCode.UInt32: return (byte)(uint)input; case TypeCode.Int64: return (byte)(long)input; case TypeCode.UInt64: return (byte)(ulong)input; case TypeCode.Single: return (byte)(float)input; case TypeCode.Double: return (byte)(double)input; case TypeCode.Decimal: return (byte)(decimal)input; case TypeCode.Boolean: return (byte)((bool)input ? 1 : 0); } break; case TypeCode.Int16: switch (sourceType) { case TypeCode.Char: return (short)(char)input; case TypeCode.SByte: return (short)(sbyte)input; case TypeCode.Byte: return (short)(byte)input; case TypeCode.UInt16: return (short)(ushort)input; case TypeCode.Int32: return (short)(int)input; case TypeCode.UInt32: return (short)(uint)input; case TypeCode.Int64: return (short)(long)input; case TypeCode.UInt64: return (short)(ulong)input; case TypeCode.Single: return (short)(float)input; case TypeCode.Double: return (short)(double)input; case TypeCode.Decimal: return (short)(decimal)input; case TypeCode.Boolean: return (short)((bool)input ? 1 : 0); } break; case TypeCode.UInt16: switch (sourceType) { case TypeCode.Char: return (ushort)(char)input; case TypeCode.SByte: return (ushort)(sbyte)input; case TypeCode.Byte: return (ushort)(byte)input; case TypeCode.Int16: return (ushort)(short)input; case TypeCode.Int32: return (ushort)(int)input; case TypeCode.UInt32: return (ushort)(uint)input; case TypeCode.Int64: return (ushort)(long)input; case TypeCode.UInt64: return (ushort)(ulong)input; case TypeCode.Single: return (ushort)(float)input; case TypeCode.Double: return (ushort)(double)input; case TypeCode.Decimal: return (ushort)(decimal)input; case TypeCode.Boolean: return (ushort)((bool)input ? 1 : 0); } break; case TypeCode.Int32: switch (sourceType) { case TypeCode.Char: return (int)(char)input; case TypeCode.SByte: return (int)(sbyte)input; case TypeCode.Byte: return (int)(byte)input; case TypeCode.Int16: return (int)(short)input; case TypeCode.UInt16: return (int)(ushort)input; case TypeCode.UInt32: return (int)(uint)input; case TypeCode.Int64: return (int)(long)input; case TypeCode.UInt64: return (int)(ulong)input; case TypeCode.Single: return (int)(float)input; case TypeCode.Double: return (int)(double)input; case TypeCode.Decimal: return (int)(decimal)input; case TypeCode.Boolean: return (int)((bool)input ? 1 : 0); } break; case TypeCode.UInt32: switch (sourceType) { case TypeCode.Char: return (uint)(char)input; case TypeCode.SByte: return (uint)(sbyte)input; case TypeCode.Byte: return (uint)(byte)input; case TypeCode.Int16: return (uint)(short)input; case TypeCode.UInt16: return (uint)(ushort)input; case TypeCode.Int32: return (uint)(int)input; case TypeCode.Int64: return (uint)(long)input; case TypeCode.UInt64: return (uint)(ulong)input; case TypeCode.Single: return (uint)(float)input; case TypeCode.Double: return (uint)(double)input; case TypeCode.Decimal: return (uint)(decimal)input; case TypeCode.Boolean: return (uint)((bool)input ? 1 : 0); } break; case TypeCode.Int64: switch (sourceType) { case TypeCode.Char: return (long)(char)input; case TypeCode.SByte: return (long)(sbyte)input; case TypeCode.Byte: return (long)(byte)input; case TypeCode.Int16: return (long)(short)input; case TypeCode.UInt16: return (long)(ushort)input; case TypeCode.Int32: return (long)(int)input; case TypeCode.UInt32: return (long)(uint)input; case TypeCode.UInt64: return (long)(ulong)input; case TypeCode.Single: return (long)(float)input; case TypeCode.Double: return (long)(double)input; case TypeCode.Decimal: return (long)(decimal)input; case TypeCode.Boolean: return (long)((bool)input ? 1 : 0); } break; case TypeCode.UInt64: switch (sourceType) { case TypeCode.Char: return (ulong)(char)input; case TypeCode.SByte: return (ulong)(sbyte)input; case TypeCode.Byte: return (ulong)(byte)input; case TypeCode.Int16: return (ulong)(short)input; case TypeCode.UInt16: return (ulong)(ushort)input; case TypeCode.Int32: return (ulong)(int)input; case TypeCode.UInt32: return (ulong)(uint)input; case TypeCode.Int64: return (ulong)(long)input; case TypeCode.Single: return (ulong)(float)input; case TypeCode.Double: return (ulong)(double)input; case TypeCode.Decimal: return (ulong)(decimal)input; case TypeCode.Boolean: return (ulong)((bool)input ? 1 : 0); } break; case TypeCode.Single: switch (sourceType) { case TypeCode.Char: return (float)(char)input; case TypeCode.SByte: return (float)(sbyte)input; case TypeCode.Byte: return (float)(byte)input; case TypeCode.Int16: return (float)(short)input; case TypeCode.UInt16: return (float)(ushort)input; case TypeCode.Int32: return (float)(int)input; case TypeCode.UInt32: return (float)(uint)input; case TypeCode.Int64: return (float)(long)input; case TypeCode.UInt64: return (float)(ulong)input; case TypeCode.Double: return (float)(double)input; case TypeCode.Decimal: return (float)(decimal)input; case TypeCode.Boolean: return (float)((bool)input ? 1 : 0); } break; case TypeCode.Double: switch (sourceType) { case TypeCode.Char: return (double)(char)input; case TypeCode.SByte: return (double)(sbyte)input; case TypeCode.Byte: return (double)(byte)input; case TypeCode.Int16: return (double)(short)input; case TypeCode.UInt16: return (double)(ushort)input; case TypeCode.Int32: return (double)(int)input; case TypeCode.UInt32: return (double)(uint)input; case TypeCode.Int64: return (double)(long)input; case TypeCode.UInt64: return (double)(ulong)input; case TypeCode.Single: return (double)(float)input; case TypeCode.Decimal: return (double)(decimal)input; case TypeCode.Boolean: return (double)((bool)input ? 1 : 0); } break; case TypeCode.Decimal: switch (sourceType) { case TypeCode.Char: return (decimal)(char)input; case TypeCode.SByte: return (decimal)(sbyte)input; case TypeCode.Byte: return (decimal)(byte)input; case TypeCode.Int16: return (decimal)(short)input; case TypeCode.UInt16: return (decimal)(ushort)input; case TypeCode.Int32: return (decimal)(int)input; case TypeCode.UInt32: return (decimal)(uint)input; case TypeCode.Int64: return (decimal)(long)input; case TypeCode.UInt64: return (decimal)(ulong)input; case TypeCode.Single: return (decimal)(float)input; case TypeCode.Double: return (decimal)(double)input; case TypeCode.Boolean: return (decimal)((bool)input ? 1 : 0); } break; } throw new InvalidCastException("Cast from " + sourceType + " to " + targetType + " not supported."); } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/CacheManager.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections.Concurrent; namespace ICSharpCode.Decompiler.Util { /// /// Allows caching values for a specific compilation. /// A CacheManager consists of a for shared instances (shared among all threads working with that resolve context). /// /// This class is thread-safe public sealed class CacheManager { readonly ConcurrentDictionary sharedDict = new ConcurrentDictionary(ReferenceComparer.Instance); // There used to be a thread-local dictionary here, but I removed it as it was causing memory // leaks in some use cases. public object? GetShared(object key) { object? value; sharedDict.TryGetValue(key, out value); return value; } public object GetOrAddShared(object key, Func valueFactory) { return sharedDict.GetOrAdd(key, valueFactory); } public object GetOrAddShared(object key, object value) { return sharedDict.GetOrAdd(key, value); } public void SetShared(object key, object value) { sharedDict[key] = value; } } } ================================================ FILE: ICSharpCode.Decompiler/Util/CallbackOnDispose.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Threading; namespace ICSharpCode.Decompiler.Util { /// /// Invokes an action when it is disposed. /// /// /// This class ensures the callback is invoked at most once, /// even when Dispose is called on multiple threads. /// public sealed class CallbackOnDispose : IDisposable { Action? action; public CallbackOnDispose(Action action) { if (action == null) throw new ArgumentNullException(nameof(action)); this.action = action; } public void Dispose() { Action? a = Interlocked.Exchange(ref action, null); if (a != null) { a(); } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/CollectionExtensions.cs ================================================ #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; namespace ICSharpCode.Decompiler.Util { static class CollectionExtensions { public static void Deconstruct(this KeyValuePair pair, out K key, out V value) { key = pair.Key; value = pair.Value; } #if !NET8_0_OR_GREATER public static IEnumerable<(A, B)> Zip(this IEnumerable input1, IEnumerable input2) { return input1.Zip(input2, (a, b) => (a, b)); } #endif public static IEnumerable<(int, A, B)> ZipWithIndex(this IEnumerable input1, IEnumerable input2) { int index = 0; return input1.Zip(input2, (a, b) => (index++, a, b)); } public static IEnumerable<(A?, B?)> ZipLongest(this IEnumerable input1, IEnumerable input2) { using (var it1 = input1.GetEnumerator()) { using (var it2 = input2.GetEnumerator()) { bool hasElements1 = true; bool hasElements2 = true; while (true) { if (hasElements1) hasElements1 = it1.MoveNext(); if (hasElements2) hasElements2 = it2.MoveNext(); if (!(hasElements1 || hasElements2)) break; yield return ((hasElements1 ? it1.Current : default), (hasElements2 ? it2.Current : default)); } } } } public static IEnumerable Slice(this IReadOnlyList input, int offset, int length) { for (int i = offset; i < offset + length; i++) { yield return input[i]; } } public static IEnumerable Slice(this IReadOnlyList input, int offset) { int length = input.Count; for (int i = offset; i < length; i++) { yield return input[i]; } } #if !NET8_0_OR_GREATER public static HashSet ToHashSet(this IEnumerable input) { return new HashSet(input); } #endif public static IEnumerable SkipLast(this IReadOnlyCollection input, int count) { return input.Take(input.Count - count); } public static IEnumerable TakeLast(this IReadOnlyCollection input, int count) { return input.Skip(input.Count - count); } public static T? PopOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); return stack.Pop(); } public static T? PeekOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); return stack.Peek(); } public static int MaxOrDefault(this IEnumerable input, Func selector, int defaultValue = 0) { int max = defaultValue; foreach (var element in input) { int value = selector(element); if (value > max) max = value; } return max; } public static int IndexOf(this IReadOnlyList collection, T value) { var comparer = EqualityComparer.Default; int index = 0; foreach (T item in collection) { if (comparer.Equals(item, value)) { return index; } index++; } return -1; } public static void AddRange(this ICollection collection, IEnumerable input) { foreach (T item in input) collection.Add(item); } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this ICollection collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToImmutableArray(), but more efficient as it makes /// use of the input collection's known size. /// public static ImmutableArray SelectImmutableArray(this IReadOnlyCollection collection, Func func) { var builder = ImmutableArray.CreateBuilder(collection.Count); foreach (var element in collection) { builder.Add(func(element)); } return builder.MoveToImmutable(); } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectReadOnlyArray(this IReadOnlyCollection collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this List collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this T[] collection, Func func) { U[] result = new U[collection.Length]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToList(), but more efficient as it makes /// use of the input collection's known size. /// public static List SelectList(this ICollection collection, Func func) { List result = new List(collection.Count); foreach (var element in collection) { result.Add(func(element)); } return result; } public static IEnumerable SelectWithIndex(this IEnumerable source, Func func) { int index = 0; foreach (var element in source) yield return func(index++, element); } public static IEnumerable<(int, T)> WithIndex(this IEnumerable source) { int index = 0; foreach (var item in source) { yield return (index, item); index++; } } /// /// The merge step of merge sort. /// public static IEnumerable Merge(this IEnumerable input1, IEnumerable input2, Comparison comparison) { using (var enumA = input1.GetEnumerator()) using (var enumB = input2.GetEnumerator()) { bool moreA = enumA.MoveNext(); bool moreB = enumB.MoveNext(); while (moreA && moreB) { if (comparison(enumA.Current, enumB.Current) <= 0) { yield return enumA.Current; moreA = enumA.MoveNext(); } else { yield return enumB.Current; moreB = enumB.MoveNext(); } } while (moreA) { yield return enumA.Current; moreA = enumA.MoveNext(); } while (moreB) { yield return enumB.Current; moreB = enumB.MoveNext(); } } } /// /// Returns the minimum element. /// /// The input sequence is empty public static T MinBy(this IEnumerable source, Func keySelector) where K : IComparable { return source.MinBy(keySelector, Comparer.Default); } /// /// Returns the minimum element. /// /// The input sequence is empty public static T MinBy(this IEnumerable source, Func keySelector, IComparer? keyComparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) keyComparer = Comparer.Default; using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements"); T minElement = enumerator.Current; K minKey = keySelector(minElement); while (enumerator.MoveNext()) { T element = enumerator.Current; K key = keySelector(element); if (keyComparer.Compare(key, minKey) < 0) { minElement = element; minKey = key; } } return minElement; } } #if !NET8_0_OR_GREATER /// /// Returns the maximum element. /// /// The input sequence is empty public static T MaxBy(this IEnumerable source, Func keySelector) where K : IComparable { return source.MaxBy(keySelector, Comparer.Default); } #endif /// /// Returns the maximum element. /// /// The input sequence is empty public static T MaxBy(this IEnumerable source, Func keySelector, IComparer? keyComparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) keyComparer = Comparer.Default; using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements"); T maxElement = enumerator.Current; K maxKey = keySelector(maxElement); while (enumerator.MoveNext()) { T element = enumerator.Current; K key = keySelector(element); if (keyComparer.Compare(key, maxKey) > 0) { maxElement = element; maxKey = key; } } return maxElement; } } public static void RemoveLast(this IList list) { if (list == null) throw new ArgumentNullException(nameof(list)); list.RemoveAt(list.Count - 1); } public static T? OnlyOrDefault(this IEnumerable source, Func predicate) => OnlyOrDefault(source.Where(predicate)); public static T? OnlyOrDefault(this IEnumerable source) { bool any = false; T? first = default; foreach (var t in source) { if (any) return default(T); first = t; any = true; } return first; } #if !NET8_0_OR_GREATER public static int EnsureCapacity(this List list, int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); if (list.Capacity < capacity) { const int DefaultCapacity = 4; const int MaxLength = 0X7FFFFFC7; int newcapacity = list.Capacity == 0 ? DefaultCapacity : 2 * list.Capacity; if ((uint)newcapacity > MaxLength) newcapacity = MaxLength; if (newcapacity < capacity) newcapacity = capacity; list.Capacity = newcapacity; } return list.Capacity; } #endif #region Aliases/shortcuts for Enumerable extension methods public static bool Any(this ICollection list) => list.Count > 0; public static bool Any(this T[] array, Predicate match) => Array.Exists(array, match); public static bool Any(this List list, Predicate match) => list.Exists(match); public static bool All(this T[] array, Predicate match) => Array.TrueForAll(array, match); public static bool All(this List list, Predicate match) => list.TrueForAll(match); public static T? FirstOrDefault(this T[] array, Predicate predicate) => Array.Find(array, predicate); public static T? FirstOrDefault(this List list, Predicate predicate) => list.Find(predicate); public static T Last(this IList list) => list[list.Count - 1]; #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Util/DelegateComparer.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { public class DelegateComparer : IComparer { private readonly Func func; public DelegateComparer(Func func) { this.func = func ?? throw new ArgumentNullException(nameof(func)); } public int Compare(T? x, T? y) { return func(x, y); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/EmptyList.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Util { [Serializable] public sealed class EmptyList : IList, IEnumerator, IReadOnlyList { public static readonly EmptyList Instance = new EmptyList(); private EmptyList() { } public T this[int index] { get { throw new ArgumentOutOfRangeException(nameof(index)); } set { throw new ArgumentOutOfRangeException(nameof(index)); } } public int Count { get { return 0; } } bool ICollection.IsReadOnly { get { return true; } } int IList.IndexOf(T item) { return -1; } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { } bool ICollection.Contains(T item) { return false; } void ICollection.CopyTo(T[] array, int arrayIndex) { } bool ICollection.Remove(T item) { return false; } IEnumerator IEnumerable.GetEnumerator() { return this; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this; } T IEnumerator.Current { get { throw new NotSupportedException(); } } object IEnumerator.Current { get { throw new NotSupportedException(); } } void IDisposable.Dispose() { } bool IEnumerator.MoveNext() { return false; } void IEnumerator.Reset() { } } public static class Empty { public static readonly T[] Array = System.Array.Empty(); } public struct Unit { } } ================================================ FILE: ICSharpCode.Decompiler/Util/ExtensionMethods.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { /// /// Contains extension methods for internal use within the decompiler. /// static class ExtensionMethods { public static Predicate? And(this Predicate? filter1, Predicate? filter2) { if (filter1 == null) return filter2; if (filter2 == null) return filter1; return m => filter1(m) && filter2(m); } public static void Swap(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } } } ================================================ FILE: ICSharpCode.Decompiler/Util/FileUtility.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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. #nullable enable using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; namespace ICSharpCode.Decompiler.Util { static class FileUtility { /// /// Gets the normalized version of fileName. /// Slashes are replaced with backslashes, backreferences "." and ".." are 'evaluated'. /// [return: NotNullIfNotNull("fileName")] public static string? NormalizePath(string? fileName) { if (fileName == null || fileName.Length == 0) return fileName; int i; bool isWeb = false; for (i = 0; i < fileName.Length; i++) { if (fileName[i] == '/' || fileName[i] == '\\') break; if (fileName[i] == ':') { if (i > 1) isWeb = true; break; } } char outputSeparator = '/'; bool isRelative; bool isAbsoluteUnixPath = false; StringBuilder result = new StringBuilder(); if (isWeb == false && IsUNCPath(fileName)) { // UNC path i = 2; outputSeparator = '\\'; result.Append(outputSeparator); isRelative = false; } else { i = 0; isAbsoluteUnixPath = fileName[0] == '/'; isRelative = !isWeb && !isAbsoluteUnixPath && (fileName.Length < 2 || fileName[1] != ':'); if (fileName.Length >= 2 && fileName[1] == ':') { outputSeparator = '\\'; } } int levelsBack = 0; int segmentStartPos = i; for (; i <= fileName.Length; i++) { if (i == fileName.Length || fileName[i] == '/' || fileName[i] == '\\') { int segmentLength = i - segmentStartPos; switch (segmentLength) { case 0: // ignore empty segment (if not in web mode) if (isWeb) { result.Append(outputSeparator); } break; case 1: // ignore /./ segment, but append other one-letter segments if (fileName[segmentStartPos] != '.') { if (result.Length > 0) result.Append(outputSeparator); result.Append(fileName[segmentStartPos]); } break; case 2: if (fileName[segmentStartPos] == '.' && fileName[segmentStartPos + 1] == '.') { // remove previous segment int j; for (j = result.Length - 1; j >= 0 && result[j] != outputSeparator; j--) { } if (j > 0) { result.Length = j; } else if (isAbsoluteUnixPath) { result.Length = 0; } else if (isRelative) { if (result.Length == 0) levelsBack++; else result.Length = 0; } break; } else { // append normal segment goto default; } default: if (result.Length > 0) result.Append(outputSeparator); result.Append(fileName, segmentStartPos, segmentLength); break; } segmentStartPos = i + 1; // remember start position for next segment } } if (isWeb == false) { if (isRelative) { for (int j = 0; j < levelsBack; j++) { result.Insert(0, ".." + outputSeparator); } } if (result.Length > 0 && result[result.Length - 1] == outputSeparator) { result.Length -= 1; } if (isAbsoluteUnixPath) { result.Insert(0, '/'); } if (result.Length == 2 && result[1] == ':') { result.Append(outputSeparator); } if (result.Length == 0) return "."; } return result.ToString(); } static bool IsUNCPath(string fileName) { return fileName.Length > 2 && (fileName[0] == '\\' || fileName[0] == '/') && (fileName[1] == '\\' || fileName[1] == '/'); } public static bool IsEqualFileName(string? fileName1, string? fileName2) { return string.Equals(NormalizePath(fileName1), NormalizePath(fileName2), StringComparison.OrdinalIgnoreCase); } public static bool IsBaseDirectory(string? baseDirectory, string? testDirectory) { if (baseDirectory == null || testDirectory == null) return false; baseDirectory = NormalizePath(baseDirectory); if (baseDirectory == "." || baseDirectory == "") return !Path.IsPathRooted(testDirectory); baseDirectory = AddTrailingSeparator(baseDirectory); testDirectory = AddTrailingSeparator(NormalizePath(testDirectory)); return testDirectory.StartsWith(baseDirectory, StringComparison.OrdinalIgnoreCase); } [return: NotNullIfNotNull("input")] static string? AddTrailingSeparator(string? input) { if (input == null || input.Length == 0) return input; if (input[input.Length - 1] == Path.DirectorySeparatorChar || input[input.Length - 1] == Path.AltDirectorySeparatorChar) return input; else return input + GetSeparatorForPath(input).ToString(); } static char GetSeparatorForPath(string input) { if (input.Length > 2 && input[1] == ':' || IsUNCPath(input)) return '\\'; return '/'; } readonly static char[] separators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; /// /// Converts a given absolute path and a given base path to a path that leads /// from the base path to the absoulte path. (as a relative path) /// public static string GetRelativePath(string? baseDirectoryPath, string absPath) { if (baseDirectoryPath == null || baseDirectoryPath.Length == 0) { return absPath; } baseDirectoryPath = NormalizePath(baseDirectoryPath); absPath = NormalizePath(absPath); string[] bPath = baseDirectoryPath != "." ? baseDirectoryPath.TrimEnd(separators).Split(separators) : new string[0]; string[] aPath = absPath != "." ? absPath.TrimEnd(separators).Split(separators) : new string[0]; int indx = 0; for (; indx < Math.Min(bPath.Length, aPath.Length); ++indx) { if (!bPath[indx].Equals(aPath[indx], StringComparison.OrdinalIgnoreCase)) break; } if (indx == 0 && (Path.IsPathRooted(baseDirectoryPath) || Path.IsPathRooted(absPath))) { return absPath; } if (indx == bPath.Length && indx == aPath.Length) { return "."; } StringBuilder erg = new StringBuilder(); for (int i = indx; i < bPath.Length; ++i) { erg.Append(".."); erg.Append(Path.DirectorySeparatorChar); } erg.Append(String.Join(Path.DirectorySeparatorChar.ToString(), aPath, indx, aPath.Length - indx)); if (erg[erg.Length - 1] == Path.DirectorySeparatorChar) erg.Length -= 1; return erg.ToString(); } [return: NotNullIfNotNull("path")] public static string? TrimPath(string? path, int max_chars) { const char ellipsis = '\u2026'; // HORIZONTAL ELLIPSIS const int ellipsisLength = 2; if (path == null || path.Length <= max_chars) return path; char sep = Path.DirectorySeparatorChar; if (path.IndexOf(Path.AltDirectorySeparatorChar) >= 0 && path.IndexOf(Path.DirectorySeparatorChar) < 0) { sep = Path.AltDirectorySeparatorChar; } string[] parts = path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); int len = ellipsisLength; // For initial ellipsis int index = parts.Length; // From the end of the path, fit as many parts as possible: while (index > 1 && len + parts[index - 1].Length < max_chars) { len += parts[index - 1].Length + 1; index--; } StringBuilder result = new StringBuilder(); result.Append(ellipsis); // If there's 5 chars left, partially fit another part: if (index > 1 && len + 5 <= max_chars) { if (index == 2 && parts[0].Length <= ellipsisLength) { // If the partial part is part #1, // and part #0 is as short as the ellipsis // (e.g. just a drive letter), use part #0 // instead of the ellipsis. result.Clear(); result.Append(parts[0]); } result.Append(sep); result.Append(parts[index - 1], 0, max_chars - len - 3); result.Append(ellipsis); } while (index < parts.Length) { result.Append(sep); result.Append(parts[index]); index++; } return result.ToString(); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/GraphTraversal.cs ================================================ // Copyright (c) 2023 Daniel Grunwald // // 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. #nullable enable using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Util; static class GraphTraversal { /// /// Depth-first-search of an graph data structure. /// The two callbacks (successorFunc + postorderAction) will be called exactly once for each node reachable from startNodes. /// /// The start nodes. /// Called multiple times per node. The first call should return true, subsequent calls must return false. /// The first calls to this function occur in pre-order. /// If null, normal Equals/GetHashCode will be used to compare nodes. /// The function that gets the successors of an element. Called in pre-order. /// Called in post-order. /// /// With reverse_successors=True, the start_nodes and each list of successors will be handled in reverse order. /// This is useful if the post-order will be reversed later (e.g. for a topological sort) /// so that blocks which could be output in either order (e.g. then-block and else-block of an if) /// will maintain the order of the edges (then-block before else-block). /// public static void DepthFirstSearch(IEnumerable startNodes, Func? visitedFunc, Func?> successorFunc, Action? postorderAction = null, bool reverseSuccessors = false) { /* Pseudocode: def dfs_walk(start_nodes, successor_func, visited, postorder_func, reverse_successors): if reverse_successors: start_nodes = reversed(start_nodes) for node in start_nodes: if node in visited: continue visited.insert(node) children = successor_func(node) dfs_walk(children, successor_func, visited, postorder_action, reverse_successors) postorder_action(node) The actual implementation here is equivalent but does not use recursion, so that we don't blow the stack on large graphs. A single stack holds the "continuations" of work that needs to be done. These can be either "visit continuations" (=execute the body of the pseudocode loop for the given node) or "postorder continuations" (=execute postorder_action) */ // Use a List as stack (but allowing for the Reverse() usage) var worklist = new List<(T node, bool isPostOrderContinuation)>(); visitedFunc ??= new HashSet().Add; foreach (T node in startNodes) { worklist.Add((node, false)); } if (!reverseSuccessors) { // Our use of a stack will reverse the order of the nodes. // If that's not desired, restore original order by reversing twice. worklist.Reverse(); } // Process outstanding continuations: while (worklist.Count > 0) { var (node, isPostOrderContinuation) = worklist.Last(); if (isPostOrderContinuation) { // Execute postorder_action postorderAction?.Invoke(node); worklist.RemoveAt(worklist.Count - 1); continue; } // Execute body of loop if (!visitedFunc(node)) { // Already visited worklist.RemoveAt(worklist.Count - 1); continue; } // foreach-loop-iteration will end with postorder_func call, // so switch the type of continuation for this node int oldWorkListSize = worklist.Count; worklist[oldWorkListSize - 1] = (node, true); // Create "visit continuations" for all successor nodes: IEnumerable? children = successorFunc(node); if (children != null) { foreach (T child in children) { worklist.Add((child, false)); } } // Our use of a stack will reverse the order of the nodes. // If that's not desired, restore original order by reversing twice. if (!reverseSuccessors) { worklist.Reverse(oldWorkListSize, worklist.Count - oldWorkListSize); } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/Index.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace System { /// Represent a type can be used to index a collection either from the start or the end. /// /// Index is used by the C# compiler to support the new index syntax /// /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; /// int lastElement = someArray[^1]; // lastElement = 5 /// /// #if SYSTEM_PRIVATE_CORELIB public #else internal #endif readonly struct Index : IEquatable { private readonly int _value; /// Construct an Index using a value and indicating if the index is from the start or from the end. /// The index value. it has to be zero or positive number. /// Indicating if the index is from the start or from the end. /// /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Index(int value, bool fromEnd = false) { if (value < 0) { ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } if (fromEnd) _value = ~value; else _value = value; } // The following private constructors mainly created for perf reason to avoid the checks private Index(int value) { _value = value; } /// Create an Index pointing at first element. public static Index Start => new Index(0); /// Create an Index pointing at beyond last element. public static Index End => new Index(~0); /// Create an Index from the start at the position indicated by the value. /// The index value from the start. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Index FromStart(int value) { if (value < 0) { ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } return new Index(value); } /// Create an Index from the end at the position indicated by the value. /// The index value from the end. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Index FromEnd(int value) { if (value < 0) { ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } return new Index(~value); } /// Returns the index value. public int Value { get { if (_value < 0) return ~_value; else return _value; } } /// Indicates whether the index is from the start or the end. public bool IsFromEnd => _value < 0; /// Calculate the offset from the start using the giving collection length. /// The length of the collection that the Index will be used with. length has to be a positive value /// /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. /// we don't validate either the returned offset is greater than the input length. /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and /// then used to index a collection will get out of range exception which will be same affect as the validation. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetOffset(int length) { int offset = _value; if (IsFromEnd) { // offset = length - (~value) // offset = length + (~(~value) + 1) // offset = length + value + 1 offset += length + 1; } return offset; } /// Indicates whether the current Index object is equal to another object of the same type. /// An object to compare with this object public override bool Equals([NotNullWhen(true)] object? value) => value is Index && _value == ((Index)value)._value; /// Indicates whether the current Index object is equal to another Index object. /// An object to compare with this object public bool Equals(Index other) => _value == other._value; /// Returns the hash code for this instance. public override int GetHashCode() => _value; /// Converts integer number to an Index. public static implicit operator Index(int value) => FromStart(value); /// Converts the value of the current Index object to its equivalent string representation. public override string ToString() { if (IsFromEnd) return ToStringFromEnd(); return ((uint)Value).ToString(); } private static void ThrowValueArgumentOutOfRange_NeedNonNegNumException() { #if SYSTEM_PRIVATE_CORELIB throw new ArgumentOutOfRangeException("value", SR.ArgumentOutOfRange_NeedNonNegNum); #else throw new ArgumentOutOfRangeException("value", "value must be non-negative"); #endif } private string ToStringFromEnd() { #if (!NETSTANDARD2_0 && !NETFRAMEWORK) Span span = stackalloc char[11]; // 1 for ^ and 10 for longest possible uint value bool formatted = ((uint)Value).TryFormat(span.Slice(1), out int charsWritten); Debug.Assert(formatted); span[0] = '^'; return new string(span.Slice(0, charsWritten + 1)); #else return '^' + Value.ToString(); #endif } } } ================================================ FILE: ICSharpCode.Decompiler/Util/Interval.cs ================================================ #nullable enable // Copyright (c) 2016 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Util { /// /// Represents a half-closed interval. /// The start position is inclusive; but the end position is exclusive. /// /// /// Start <= unchecked(End - 1): normal interval /// Start == End: empty interval /// Special case: Start == End == int.MinValue: interval containing all integers, not an empty interval! /// public struct Interval : IEquatable { /// /// Gets the inclusive start of the interval. /// public readonly int Start; /// /// Gets the exclusive end of the interval. /// /// /// Note that an End of int.MinValue is a special case, and stands /// for an actual End of int.MaxValue+1. /// If possible, prefer using InclusiveEnd for comparisons, as that does not have an overflow problem. /// public readonly int End; /// /// Creates a new interval. /// /// Start position (inclusive) /// End position (exclusive). /// Note that it is possible to create an interval that includes int.MaxValue /// by using end==int.MaxValue+1==int.MinValue. public Interval(int start, int end) { if (!(start <= unchecked(end - 1) || start == end)) throw new ArgumentException("The end must be after the start", nameof(end)); this.Start = start; this.End = end; } /// /// Gets the inclusive end of the interval. (End - 1) /// For empty intervals, this returns Start - 1. /// /// /// Because there is no empty interval at int.MinValue, /// (Start==End==int.MinValue is a special case referring to [int.MinValue..int.MaxValue]), /// integer overflow is not a problem here. /// public int InclusiveEnd { get { return unchecked(End - 1); } } public bool IsEmpty { get { return Start > InclusiveEnd; } } public bool Contains(int val) { // Use 'val <= InclusiveEnd' instead of 'val < End' to allow intervals to include int.MaxValue. return Start <= val && val <= InclusiveEnd; } /// /// Calculates the intersection between this interval and the other interval. /// public Interval Intersect(Interval other) { int start = Math.Max(this.Start, other.Start); int inclusiveEnd = Math.Min(this.InclusiveEnd, other.InclusiveEnd); if (start <= inclusiveEnd) return new Interval(start, unchecked(inclusiveEnd + 1)); else return default(Interval); } public override string ToString() { if (End == int.MinValue) return string.Format("[{0}..int.MaxValue]", Start); else return string.Format("[{0}..{1})", Start, End); } #region Equals and GetHashCode implementation public override bool Equals(object? obj) { return (obj is Interval) && Equals((Interval)obj); } public bool Equals(Interval other) { return this.Start == other.Start && this.End == other.End; } public override int GetHashCode() { return Start ^ End ^ (End << 7); } public static bool operator ==(Interval lhs, Interval rhs) { return lhs.Equals(rhs); } public static bool operator !=(Interval lhs, Interval rhs) { return !(lhs == rhs); } #endregion } /// /// Represents a half-closed interval. /// The start position is inclusive; but the end position is exclusive. /// /// /// Start <= unchecked(End - 1): normal interval /// Start == End: empty interval /// Special case: Start == End == long.MinValue: interval containing all integers, not an empty interval! /// public struct LongInterval : IEquatable { /// /// Gets the inclusive start of the interval. /// public readonly long Start; /// /// Gets the exclusive end of the interval. /// /// /// Note that an End of long.MinValue is a special case, and stands /// for an actual End of long.MaxValue+1. /// If possible, prefer using InclusiveEnd for comparisons, as that does not have an overflow problem. /// public readonly long End; /// /// Creates a new interval. /// /// Start position (inclusive) /// End position (exclusive). /// Note that it is possible to create an interval that includes long.MaxValue /// by using end==long.MaxValue+1==long.MinValue. /// /// This method can be used to create an empty interval by specifying start==end, /// however this is error-prone due to the special case of /// start==end==long.MinValue being interpreted as the full interval [long.MinValue,long.MaxValue]. /// public LongInterval(long start, long end) { if (!(start <= unchecked(end - 1) || start == end)) throw new ArgumentException("The end must be after the start", nameof(end)); this.Start = start; this.End = end; } /// /// Creates a new interval from start to end. /// Unlike the constructor where the end position is exclusive, /// this method interprets the end position as inclusive. /// /// This method cannot be used to construct an empty interval. /// public static LongInterval Inclusive(long start, long inclusiveEnd) { if (!(start <= inclusiveEnd)) throw new ArgumentException(); return new LongInterval(start, unchecked(inclusiveEnd + 1)); } /// /// Gets the inclusive end of the interval. (End - 1) /// For empty intervals, this returns Start - 1. /// /// /// Because there is no empty interval at int.MinValue, /// (Start==End==int.MinValue is a special case referring to [int.MinValue..int.MaxValue]), /// integer overflow is not a problem here. /// public long InclusiveEnd { get { return unchecked(End - 1); } } public bool IsEmpty { get { return Start > InclusiveEnd; } } public bool Contains(long val) { // Use 'val <= InclusiveEnd' instead of 'val < End' to allow intervals to include long.MaxValue. return Start <= val && val <= InclusiveEnd; } /// /// Calculates the intersection between this interval and the other interval. /// public LongInterval Intersect(LongInterval other) { long start = Math.Max(this.Start, other.Start); long inclusiveEnd = Math.Min(this.InclusiveEnd, other.InclusiveEnd); if (start <= inclusiveEnd) return new LongInterval(start, unchecked(inclusiveEnd + 1)); else return default(LongInterval); } /// /// Returns an enumerator over all values in this interval. /// public IEnumerable Range() { if (End == long.MinValue) { long i = Start; while (true) { yield return i; if (i == long.MaxValue) break; i++; } } else { for (long i = Start; i < End; i++) yield return i; } } public override string ToString() { if (End == long.MinValue) { if (Start == long.MinValue) return "[long.MinValue..long.MaxValue]"; else return $"[{Start}..long.MaxValue]"; } else if (Start == long.MinValue) { return $"[long.MinValue..{End})"; } else { return $"[{Start}..{End})"; } } #region Equals and GetHashCode implementation public override bool Equals(object? obj) { return (obj is LongInterval) && Equals((LongInterval)obj); } public bool Equals(LongInterval other) { return this.Start == other.Start && this.End == other.End; } public override int GetHashCode() { return (Start ^ End ^ (End << 7)).GetHashCode(); } public static bool operator ==(LongInterval lhs, LongInterval rhs) { return lhs.Equals(rhs); } public static bool operator !=(LongInterval lhs, LongInterval rhs) { return !(lhs == rhs); } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Util/KeyComparer.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { public static class KeyComparer { public static KeyComparer Create(Func keySelector) { return new KeyComparer(keySelector, Comparer.Default, EqualityComparer.Default); } public static KeyComparer Create(Func keySelector, IComparer comparer, IEqualityComparer equalityComparer) { return new KeyComparer(keySelector, comparer, equalityComparer); } public static IComparer Create(Func keySelector, IComparer comparer) { return new KeyComparer(keySelector, comparer, EqualityComparer.Default); } public static IEqualityComparer Create(Func keySelector, IEqualityComparer equalityComparer) { return new KeyComparer(keySelector, Comparer.Default, equalityComparer); } public static void SortBy(this List list, Func keySelector) { list.Sort(Create(keySelector)); } } public class KeyComparer : IComparer, IEqualityComparer { readonly Func keySelector; readonly IComparer keyComparer; readonly IEqualityComparer keyEqualityComparer; public KeyComparer(Func keySelector, IComparer keyComparer, IEqualityComparer keyEqualityComparer) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) throw new ArgumentNullException(nameof(keyComparer)); if (keyEqualityComparer == null) throw new ArgumentNullException(nameof(keyEqualityComparer)); this.keySelector = keySelector; this.keyComparer = keyComparer; this.keyEqualityComparer = keyEqualityComparer; } public int Compare(TElement? x, TElement? y) { return keyComparer.Compare(keySelector(x!), keySelector(y!)); } public bool Equals(TElement? x, TElement? y) { return keyEqualityComparer.Equals(keySelector(x!), keySelector(y!)); } public int GetHashCode(TElement obj) { var key = keySelector(obj)!; return keyEqualityComparer.GetHashCode(key); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/LazyInit.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Diagnostics.CodeAnalysis; using System.Threading; namespace ICSharpCode.Decompiler.Util { public static class LazyInit { public static T VolatileRead(ref T location) where T : class? { return Volatile.Read(ref location); } /// /// Atomically performs the following operation: /// - If target is null: stores newValue in target and returns newValue. /// - If target is not null: returns target. /// [return: NotNullIfNotNull("newValue")] public static T? GetOrSet(ref T? target, T? newValue) where T : class { T? oldValue = Interlocked.CompareExchange(ref target, newValue, null); return oldValue ?? newValue; } } } ================================================ FILE: ICSharpCode.Decompiler/Util/LongDict.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Util { static class LongDict { public static LongDict Create(IEnumerable<(LongSet, T)> entries) { return new LongDict(entries); } internal static readonly KeyComparer StartComparer = KeyComparer.Create((LongInterval i) => i.Start); } /// /// An immutable mapping from keys of type long to values of type T. /// struct LongDict : IEnumerable> { readonly LongInterval[] keys; readonly T[] values; /// /// Creates a new LongDict from the given entries. /// If there are multiple entries for the same long key, /// the resulting LongDict will store the value from the first entry. /// public LongDict(IEnumerable<(LongSet, T)> entries) { LongSet available = LongSet.Universe; var keys = new List(); var values = new List(); foreach (var (key, val) in entries) { foreach (var interval in key.IntersectWith(available).Intervals) { keys.Add(interval); values.Add(val); } available = available.ExceptWith(key); } this.keys = keys.ToArray(); this.values = values.ToArray(); Array.Sort(this.keys, this.values, LongDict.StartComparer); } public bool TryGetValue(long key, out T value) { int pos = Array.BinarySearch(this.keys, new LongInterval(key, key), LongDict.StartComparer); // If the element isn't found, BinarySearch returns the complement of "insertion position". // We use this to find the previous element (if there wasn't any exact match). if (pos < 0) pos = ~pos - 1; if (pos >= 0 && this.keys[pos].Contains(key)) { value = this.values[pos]; return true; } value = default(T); return false; } public T GetOrDefault(long key) { TryGetValue(key, out T val); return val; } public IEnumerator> GetEnumerator() { for (int i = 0; i < this.keys.Length; ++i) { yield return new KeyValuePair(this.keys[i], this.values[i]); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/LongSet.cs ================================================ #nullable enable // Copyright (c) 2016 Daniel Grunwald // // 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.Collections.Immutable; using System.Diagnostics; using System.Linq; namespace ICSharpCode.Decompiler.Util { /// /// An immutable set of longs, that is implemented as a list of intervals. /// public struct LongSet : IEquatable { /// /// The intervals in this set of longs. /// /// /// Invariant: the intervals in this array are non-empty, non-overlapping, non-touching, and sorted. /// /// This invariant ensures every LongSet is always in a normalized representation. /// public readonly ImmutableArray Intervals; private LongSet(ImmutableArray intervals) { this.Intervals = intervals; #if DEBUG // Check invariant long minValue = long.MinValue; for (int i = 0; i < intervals.Length; i++) { Debug.Assert(!intervals[i].IsEmpty); Debug.Assert(minValue <= intervals[i].Start); if (intervals[i].InclusiveEnd == long.MaxValue - 1 || intervals[i].InclusiveEnd == long.MaxValue) { // An inclusive end of long.MaxValue-1 or long.MaxValue means (after the gap of 1 element), // there isn't any room for more non-empty intervals. Debug.Assert(i == intervals.Length - 1); } else { minValue = checked(intervals[i].End + 1); // enforce least 1 gap between intervals } } #endif } /// /// Create a new LongSet that contains a single value. /// public LongSet(long value) : this(ImmutableArray.Create(LongInterval.Inclusive(value, value))) { } /// /// Create a new LongSet that contains the values from the interval. /// public LongSet(LongInterval interval) : this(interval.IsEmpty ? Empty.Intervals : ImmutableArray.Create(interval)) { } /// /// Creates a new LongSet the contains the values from the specified intervals. /// public LongSet(IEnumerable intervals) : this(MergeOverlapping(intervals.Where(i => !i.IsEmpty).OrderBy(i => i.Start)).ToImmutableArray()) { } /// /// The empty LongSet. /// public static readonly LongSet Empty = new LongSet(ImmutableArray.Create()); /// /// The LongSet that contains all possible long values. /// public static readonly LongSet Universe = new LongSet(LongInterval.Inclusive(long.MinValue, long.MaxValue)); public bool IsEmpty { get { return Intervals.IsEmpty; } } /// /// Gets the number of values in this LongSet. /// Note: for LongSet.Universe, the number of values does not fit into ulong. /// Instead, this property returns the off-by-one value ulong.MaxValue to avoid overflow. /// public ulong Count() { unchecked { ulong count = 0; foreach (var interval in Intervals) { count += (ulong)(interval.End - interval.Start); } if (count == 0 && !Intervals.IsEmpty) return ulong.MaxValue; else return count; } } IEnumerable DoIntersectWith(LongSet other) { var enumA = this.Intervals.GetEnumerator(); var enumB = other.Intervals.GetEnumerator(); bool moreA = enumA.MoveNext(); bool moreB = enumB.MoveNext(); while (moreA && moreB) { LongInterval a = enumA.Current; LongInterval b = enumB.Current; LongInterval intersection = a.Intersect(b); if (!intersection.IsEmpty) { yield return intersection; } if (a.InclusiveEnd < b.InclusiveEnd) { moreA = enumA.MoveNext(); } else { moreB = enumB.MoveNext(); } } } public bool Overlaps(LongSet other) { return DoIntersectWith(other).Any(); } public LongSet IntersectWith(LongSet other) { return new LongSet(DoIntersectWith(other).ToImmutableArray()); } /// /// Given an enumerable of non-empty intervals sorted by the starting position, /// merges overlapping or touching intervals to create a valid interval array for LongSet. /// static IEnumerable MergeOverlapping(IEnumerable input) { long start = long.MinValue; long end = long.MinValue; bool empty = true; foreach (var element in input) { Debug.Assert(start <= element.Start); Debug.Assert(!element.IsEmpty); if (!empty && element.Start <= end) { // element overlaps or touches [start, end), so combine the intervals: if (element.End == long.MinValue) { // special case: element goes all the way up to long.MaxValue inclusive end = long.MinValue; } else { end = Math.Max(end, element.End); } } else { // flush existing interval: if (!empty) { yield return new LongInterval(start, end); } else { empty = false; } start = element.Start; end = element.End; } if (end == long.MinValue) { // special case: element goes all the way up to long.MaxValue inclusive // all further intervals in the input must be contained in [start, end), // so ignore them (and avoid trouble due to the overflow in `end`). break; } } if (!empty) { yield return new LongInterval(start, end); } } public LongSet UnionWith(LongSet other) { var mergedIntervals = this.Intervals.Merge(other.Intervals, (a, b) => a.Start.CompareTo(b.Start)); return new LongSet(MergeOverlapping(mergedIntervals).ToImmutableArray()); } /// /// Creates a new LongSet where val is added to each element of this LongSet. /// public LongSet AddOffset(long val) { if (val == 0) { return this; } var newIntervals = new List(Intervals.Length + 1); foreach (var element in Intervals) { long newStart = unchecked(element.Start + val); long newInclusiveEnd = unchecked(element.InclusiveEnd + val); if (newStart <= newInclusiveEnd) { newIntervals.Add(LongInterval.Inclusive(newStart, newInclusiveEnd)); } else { // interval got split by integer overflow newIntervals.Add(LongInterval.Inclusive(newStart, long.MaxValue)); newIntervals.Add(LongInterval.Inclusive(long.MinValue, newInclusiveEnd)); } } newIntervals.Sort((a, b) => a.Start.CompareTo(b.Start)); return new LongSet(MergeOverlapping(newIntervals).ToImmutableArray()); } /// /// Creates a new set that contains all values that are in this, but not in other. /// public LongSet ExceptWith(LongSet other) { return IntersectWith(other.Invert()); } /// /// Creates a new LongSet that contains all elements not contained in this LongSet. /// public LongSet Invert() { // The loop below assumes a non-empty LongSet, so handle the empty case specially. if (IsEmpty) { return Universe; } List newIntervals = new List(Intervals.Length + 1); long prevEnd = long.MinValue; // previous exclusive end foreach (var interval in Intervals) { if (interval.Start > prevEnd) { newIntervals.Add(new LongInterval(prevEnd, interval.Start)); } prevEnd = interval.End; } // create a final interval up to long.MaxValue inclusive if (prevEnd != long.MinValue) { newIntervals.Add(new LongInterval(prevEnd, long.MinValue)); } return new LongSet(newIntervals.ToImmutableArray()); } /// /// Gets whether this set is a subset of other, or equal. /// public bool IsSubsetOf(LongSet other) { // TODO: optimize IsSubsetOf -- there's no need to build a temporary set return this.UnionWith(other).SetEquals(other); } /// /// Gets whether this set is a superset of other, or equal. /// public bool IsSupersetOf(LongSet other) { return other.IsSubsetOf(this); } public bool IsProperSubsetOf(LongSet other) { return IsSubsetOf(other) && !SetEquals(other); } public bool IsProperSupersetOf(LongSet other) { return IsSupersetOf(other) && !SetEquals(other); } public bool Contains(long val) { int index = upper_bound(val); return index > 0 && Intervals[index - 1].Contains(val); } internal int upper_bound(long val) { int min = 0, max = Intervals.Length - 1; while (max >= min) { int m = min + (max - min) / 2; LongInterval i = Intervals[m]; if (val < i.Start) { max = m - 1; continue; } if (val > i.End) { min = m + 1; continue; } return m + 1; } return min; } public IEnumerable Values { get { return Intervals.SelectMany(i => i.Range()); } } public LongInterval ContainingInterval() { if (IsEmpty) return default; return new LongInterval(Intervals[0].Start, Intervals[Intervals.Length - 1].End); } public override string ToString() { return string.Join(",", Intervals); } #region Equals and GetHashCode implementation public override bool Equals(object? obj) { return obj is LongSet && SetEquals((LongSet)obj); } public override int GetHashCode() { throw new NotImplementedException(); } [Obsolete("Explicitly call SetEquals() instead.")] public bool Equals(LongSet other) { return SetEquals(other); } public bool SetEquals(LongSet other) { if (Intervals.Length != other.Intervals.Length) return false; for (int i = 0; i < Intervals.Length; i++) { if (Intervals[i] != other.Intervals[i]) return false; } return true; } #endregion } } ================================================ FILE: ICSharpCode.Decompiler/Util/MultiDictionary.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Util { /// /// A dictionary that allows multiple pairs with the same key. /// public class MultiDictionary : ILookup where TKey : notnull { readonly Dictionary> dict; public MultiDictionary() { dict = new Dictionary>(); } public MultiDictionary(IEqualityComparer? comparer) { dict = new Dictionary>(comparer); } public void Add(TKey key, TValue value) { if (!dict.TryGetValue(key, out List? valueList)) { valueList = new List(); dict.Add(key, valueList); } valueList.Add(value); } public bool Remove(TKey key, TValue value) { if (dict.TryGetValue(key, out List? valueList)) { if (valueList.Remove(value)) { if (valueList.Count == 0) dict.Remove(key); return true; } } return false; } /// /// Removes all entries with the specified key. /// /// Returns true if at least one entry was removed. public bool RemoveAll(TKey key) { return dict.Remove(key); } public void Clear() { dict.Clear(); } public IReadOnlyList this[TKey key] { get { if (dict.TryGetValue(key, out var list)) return list; else return EmptyList.Instance; } } public bool TryGetValues(TKey key, out IReadOnlyList values) { values = EmptyList.Instance; if (dict.TryGetValue(key, out var list)) { values = list; return true; } return false; } /// /// Returns the number of different keys. /// public int Count { get { return dict.Count; } } public ICollection Keys { get { return dict.Keys; } } public IEnumerable Values { get { return dict.Values.SelectMany(list => list); } } IEnumerable ILookup.this[TKey key] { get { return this[key]; } } public bool Contains(TKey key) { return dict.ContainsKey(key); } public IEnumerator> GetEnumerator() { foreach (var pair in dict) yield return new Grouping(pair.Key, pair.Value); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } sealed class Grouping : IGrouping { readonly TKey key; readonly List values; public Grouping(TKey key, List values) { this.key = key; this.values = values; } public TKey Key { get { return key; } } public IEnumerator GetEnumerator() { return values.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return values.GetEnumerator(); } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/Platform.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { /// /// Platform-specific code. /// public static class Platform { public static StringComparer FileNameComparer { get { switch (Environment.OSVersion.Platform) { case PlatformID.Unix: case PlatformID.MacOSX: return StringComparer.Ordinal; default: return StringComparer.OrdinalIgnoreCase; } } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/ProjectedList.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { public sealed class ProjectedList : IReadOnlyList where TOutput : class { readonly IList input; readonly Func projection; readonly TOutput?[] items; public ProjectedList(IList input, Func projection) { if (input == null) throw new ArgumentNullException(nameof(input)); if (projection == null) throw new ArgumentNullException(nameof(projection)); this.input = input; this.projection = projection; this.items = new TOutput?[input.Count]; } public TOutput this[int index] { get { TOutput? output = LazyInit.VolatileRead(ref items[index]); if (output != null) { return output; } return LazyInit.GetOrSet(ref items[index], projection(input[index])); } } public int Count { get { return items.Length; } } public IEnumerator GetEnumerator() { for (int i = 0; i < this.Count; i++) { yield return this[i]; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } public sealed class ProjectedList : IReadOnlyList where TOutput : class { readonly IList input; readonly TContext context; readonly Func projection; readonly TOutput?[] items; public ProjectedList(TContext context, IList input, Func projection) { if (input == null) throw new ArgumentNullException(nameof(input)); if (projection == null) throw new ArgumentNullException(nameof(projection)); this.input = input; this.context = context; this.projection = projection; this.items = new TOutput?[input.Count]; } public TOutput this[int index] { get { TOutput? output = LazyInit.VolatileRead(ref items[index]); if (output != null) { return output; } return LazyInit.GetOrSet(ref items[index], projection(context, input[index])); } } public int Count { get { return items.Length; } } public IEnumerator GetEnumerator() { for (int i = 0; i < this.Count; i++) { yield return this[i]; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/ReferenceComparer.cs ================================================ // Copyright (c) 2010-2013 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 // 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. #nullable enable using System.Collections.Generic; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Util { public sealed class ReferenceComparer : IEqualityComparer { public readonly static ReferenceComparer Instance = new ReferenceComparer(); public new bool Equals(object? x, object? y) { return x == y; } public int GetHashCode(object? obj) { return RuntimeHelpers.GetHashCode(obj); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/ResXResourceWriter.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // This file is based on the Mono implementation of ResXResourceWriter. // It is modified to add support for "ResourceSerializedObject" values. // // 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. // // Copyright (c) 2004-2005 Novell, Inc. // // Authors: // Duncan Mak duncan@ximian.com // Gonzalo Paniagua Javier gonzalo@ximian.com // Peter Bartok pbartok@novell.com // Gary Barnett gary.barnett.mono@gmail.com // includes code by Mike Krüger and Lluis Sanchez using System; using System.Globalization; using System.IO; using System.Text; using System.Xml; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Util { #if INSIDE_SYSTEM_WEB internal #else public #endif class ResXResourceWriter : IDisposable { private string filename; private Stream stream; private TextWriter textwriter; private XmlTextWriter writer; private bool written; private string base_path; public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64"; public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64"; public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType; public static readonly string ResMimeType = "text/microsoft-resx"; public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64"; public static readonly string Version = "2.0"; public ResXResourceWriter(Stream stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (!stream.CanWrite) throw new ArgumentException("stream is not writable.", nameof(stream)); this.stream = stream; } public ResXResourceWriter(TextWriter textWriter) { if (textWriter == null) throw new ArgumentNullException(nameof(textWriter)); this.textwriter = textWriter; } public ResXResourceWriter(string fileName) { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); this.filename = fileName; } ~ResXResourceWriter() { Dispose(false); } const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string MSCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string ResXNullRefTypeName = "System.Resources.ResXNullRef" + WinFormsAssemblyName; void InitWriter() { if (filename != null) stream = File.Open(filename, FileMode.Create); if (textwriter == null) textwriter = new StreamWriter(stream, Encoding.UTF8); writer = new XmlTextWriter(textwriter); writer.Formatting = Formatting.Indented; writer.WriteStartDocument(); writer.WriteStartElement("root"); writer.WriteRaw(schema); WriteHeader("resmimetype", "text/microsoft-resx"); WriteHeader("version", "1.3"); WriteHeader("reader", "System.Resources.ResXResourceReader" + WinFormsAssemblyName); WriteHeader("writer", "System.Resources.ResXResourceWriter" + WinFormsAssemblyName); } void WriteHeader(string name, string value) { writer.WriteStartElement("resheader"); writer.WriteAttributeString("name", name); writer.WriteStartElement("value"); writer.WriteString(value); writer.WriteEndElement(); writer.WriteEndElement(); } void WriteNiceBase64(byte[] value, int offset, int length) { string base64 = Convert.ToBase64String( value, offset, length, Base64FormattingOptions.InsertLineBreaks); writer.WriteString(base64); } void WriteBytes(string name, string type, byte[] value, int offset, int length, string comment) { writer.WriteStartElement("data"); writer.WriteAttributeString("name", name); if (type != null) { writer.WriteAttributeString("type", type); // byte[] should never get a mimetype, otherwise MS.NET won't be able // to parse the data. if (type != "System.Byte[]" + MSCorLibAssemblyName) writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType); writer.WriteStartElement("value"); WriteNiceBase64(value, offset, length); } else { writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType); writer.WriteStartElement("value"); WriteNiceBase64(value, offset, length); } writer.WriteEndElement(); if (!string.IsNullOrEmpty(comment)) { writer.WriteStartElement("comment"); writer.WriteString(comment); writer.WriteEndElement(); } writer.WriteEndElement(); } void WriteString(string name, string value, string type, string comment) { writer.WriteStartElement("data"); writer.WriteAttributeString("name", name); if (type != null) { writer.WriteAttributeString("type", type); } else { writer.WriteAttributeString("xml:space", "preserve"); } writer.WriteStartElement("value"); writer.WriteString(value); writer.WriteEndElement(); if (!string.IsNullOrEmpty(comment)) { writer.WriteStartElement("comment"); writer.WriteString(comment); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteWhitespace("\n "); } public void AddResource(string name, byte[] value) { AddResource(name, value, string.Empty); } public void AddResource(string name, object value) { AddResource(name, value, string.Empty); } private void AddResource(string name, object value, string comment) { if (name == null) throw new ArgumentNullException(nameof(name)); if (written) throw new InvalidOperationException("The resource is already generated."); if (writer == null) InitWriter(); switch (value) { case null: // nulls written as ResXNullRef WriteString(name, "", ResXNullRefTypeName, comment); break; case string s: WriteString(name, s, null, comment); break; case bool bo: WriteString(name, bo.ToString(CultureInfo.InvariantCulture), "System.Boolean" + MSCorLibAssemblyName, comment); break; case char ch: WriteString(name, ch.ToString(CultureInfo.InvariantCulture), "System.Char" + MSCorLibAssemblyName, comment); break; case sbyte sb: WriteString(name, sb.ToString(CultureInfo.InvariantCulture), "System.SByte" + MSCorLibAssemblyName, comment); break; case byte b: WriteString(name, b.ToString(CultureInfo.InvariantCulture), "System.Byte" + MSCorLibAssemblyName, comment); break; case short sh: WriteString(name, sh.ToString(CultureInfo.InvariantCulture), "System.Int16" + MSCorLibAssemblyName, comment); break; case ushort ush: WriteString(name, ush.ToString(CultureInfo.InvariantCulture), "System.UInt16" + MSCorLibAssemblyName, comment); break; case int i: WriteString(name, i.ToString(CultureInfo.InvariantCulture), "System.Int32" + MSCorLibAssemblyName, comment); break; case uint u: WriteString(name, u.ToString(CultureInfo.InvariantCulture), "System.UInt32" + MSCorLibAssemblyName, comment); break; case long l: WriteString(name, l.ToString(CultureInfo.InvariantCulture), "System.Int64" + MSCorLibAssemblyName, comment); break; case ulong ul: WriteString(name, ul.ToString(CultureInfo.InvariantCulture), "System.UInt64" + MSCorLibAssemblyName, comment); break; case float f: WriteString(name, f.ToString(CultureInfo.InvariantCulture), "System.Single" + MSCorLibAssemblyName, comment); break; case double d: WriteString(name, d.ToString(CultureInfo.InvariantCulture), "System.Double" + MSCorLibAssemblyName, comment); break; case decimal m: WriteString(name, m.ToString(CultureInfo.InvariantCulture), "System.Decimal" + MSCorLibAssemblyName, comment); break; case DateTime dt: WriteString(name, dt.ToString(CultureInfo.InvariantCulture), "System.DateTime" + MSCorLibAssemblyName, comment); break; case TimeSpan sp: WriteString(name, sp.ToString(), "System.TimeSpan" + MSCorLibAssemblyName, comment); break; case byte[] array: WriteBytes(name, "System.Byte[]" + MSCorLibAssemblyName, array, 0, array.Length, comment); break; case MemoryStream memoryStream: var arr = memoryStream.ToArray(); WriteBytes(name, null, arr, 0, arr.Length, comment); break; case ResourceSerializedObject rso: var bytes = rso.GetBytes(); WriteBytes(name, rso.TypeName, bytes, 0, bytes.Length, comment); break; default: throw new NotSupportedException($"Value '{value}' of type {value.GetType().FullName} is not supported by this version of ResXResourceWriter. Use byte arrays or streams instead."); } } public void AddResource(string name, string value) { AddResource(name, value, string.Empty); } public void Close() { if (writer != null) { if (!written) { Generate(); } writer.Close(); stream = null; filename = null; textwriter = null; } } public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Generate() { if (writer == null) InitWriter(); if (written) throw new InvalidOperationException("The resource is already generated."); written = true; writer.WriteEndElement(); writer.Flush(); } protected virtual void Dispose(bool disposing) { if (disposing) Close(); } static readonly string schema = @" ".Replace("'", "\"").Replace("\t", " "); public string BasePath { get { return base_path; } set { base_path = value; } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/ResourcesFile.cs ================================================ #nullable enable // Copyright (c) 2018 Daniel Grunwald // Based on the .NET Core ResourceReader; make available under the MIT license // by the .NET Foundation. // // 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; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; namespace ICSharpCode.Decompiler.Util { /// /// .resources file. /// public class ResourcesFile : IEnumerable>, IDisposable { sealed class MyBinaryReader : BinaryReader { public MyBinaryReader(Stream input, bool leaveOpen) : base(input, Encoding.UTF8, leaveOpen) { } // upgrade from protected to public visibility public new int Read7BitEncodedInt() { return base.Read7BitEncodedInt(); } public void Seek(long pos, SeekOrigin origin) { BaseStream.Seek(pos, origin); } } enum ResourceTypeCode { Null = 0, String = 1, Boolean = 2, Char = 3, Byte = 4, SByte = 5, Int16 = 6, UInt16 = 7, Int32 = 8, UInt32 = 9, Int64 = 10, UInt64 = 11, Single = 12, Double = 13, Decimal = 14, DateTime = 0xF, TimeSpan = 0x10, LastPrimitive = 0x10, ByteArray = 0x20, Stream = 33, StartOfUserTypes = 0x40 } enum SerializationFormat { BinaryFormatter = 1, TypeConverterByteArray = 2, TypeConverterString = 3, ActivatorStream = 4 } /// Holds the number used to identify resource files. public const int MagicNumber = unchecked((int)0xBEEFCACE); const int ResourceSetVersion = 2; readonly MyBinaryReader reader; readonly int version; readonly bool usesSerializationFormat; readonly int numResources; readonly string[] typeTable; readonly int[] namePositions; readonly long fileStartPosition; readonly long nameSectionPosition; readonly long dataSectionPosition; long[]? startPositions; /// /// Creates a new ResourcesFile. /// /// Input stream. /// Whether the stream should be held open when the ResourcesFile is disposed. /// /// The stream is must be held open while the ResourcesFile is in use. /// The stream must be seekable; any operation using the ResourcesFile will end up seeking the stream. /// public ResourcesFile(Stream stream, bool leaveOpen = true) { fileStartPosition = stream.Position; reader = new MyBinaryReader(stream, leaveOpen); const string ResourcesHeaderCorrupted = "Resources header corrupted."; // Read ResourceManager header // Check for magic number int magicNum = reader.ReadInt32(); if (magicNum != MagicNumber) throw new BadImageFormatException("Not a .resources file - invalid magic number"); // Assuming this is ResourceManager header V1 or greater, hopefully // after the version number there is a number of bytes to skip // to bypass the rest of the ResMgr header. For V2 or greater, we // use this to skip to the end of the header int resMgrHeaderVersion = reader.ReadInt32(); int numBytesToSkip = reader.ReadInt32(); if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } if (resMgrHeaderVersion > 1) { reader.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current); } else { // We don't care about numBytesToSkip; read the rest of the header // readerType: string readerType = reader.ReadString(); usesSerializationFormat = readerType == "System.Resources.Extensions.DeserializingResourceReader, System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"; // resourceSetType: reader.ReadString(); } // Read RuntimeResourceSet header // Do file version check version = reader.ReadInt32(); if (version != ResourceSetVersion && version != 1) throw new BadImageFormatException($"Unsupported resource set version: {version}"); numResources = reader.ReadInt32(); if (numResources < 0) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } // Read type positions into type positions array. // But delay initialize the type table. int numTypes = reader.ReadInt32(); if (numTypes < 0) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } typeTable = new string[numTypes]; for (int i = 0; i < numTypes; i++) { typeTable[i] = reader.ReadString(); } // Prepare to read in the array of name hashes // Note that the name hashes array is aligned to 8 bytes so // we can use pointers into it on 64 bit machines. (4 bytes // may be sufficient, but let's plan for the future) // Skip over alignment stuff. All public .resources files // should be aligned No need to verify the byte values. long pos = reader.BaseStream.Position - fileStartPosition; int alignBytes = unchecked((int)pos) & 7; if (alignBytes != 0) { for (int i = 0; i < 8 - alignBytes; i++) { reader.ReadByte(); } } // Skip over the array of name hashes try { reader.Seek(checked(4 * numResources), SeekOrigin.Current); } catch (OverflowException) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } // Read in the array of relative positions for all the names. namePositions = new int[numResources]; for (int i = 0; i < numResources; i++) { int namePosition = reader.ReadInt32(); if (namePosition < 0) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } namePositions[i] = namePosition; } // Read location of data section. int dataSectionOffset = reader.ReadInt32(); if (dataSectionOffset < 0) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } // Store current location as start of name section nameSectionPosition = reader.BaseStream.Position; dataSectionPosition = fileStartPosition + dataSectionOffset; // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt if (dataSectionPosition < nameSectionPosition) { throw new BadImageFormatException(ResourcesHeaderCorrupted); } } public void Dispose() { reader.Dispose(); } public int ResourceCount => numResources; public string GetResourceName(int index) { return GetResourceName(index, out _); } int GetResourceDataOffset(int index) { GetResourceName(index, out int dataOffset); return dataOffset; } string GetResourceName(int index, out int dataOffset) { long pos = nameSectionPosition + namePositions[index]; byte[] bytes; lock (reader) { reader.Seek(pos, SeekOrigin.Begin); // Can't use reader.ReadString, since it's using UTF-8! int byteLen = reader.Read7BitEncodedInt(); if (byteLen < 0) { throw new BadImageFormatException("Resource name has negative length"); } bytes = new byte[byteLen]; // We must read byteLen bytes, or we have a corrupted file. // Use a blocking read in case the stream doesn't give us back // everything immediately. int count = byteLen; while (count > 0) { int n = reader.Read(bytes, byteLen - count, count); if (n == 0) throw new BadImageFormatException("End of stream within a resource name"); count -= n; } dataOffset = reader.ReadInt32(); if (dataOffset < 0) { throw new BadImageFormatException("Negative data offset"); } } return Encoding.Unicode.GetString(bytes); } internal bool AllEntriesAreStreams() { if (version != 2) return false; lock (reader) { for (int i = 0; i < numResources; i++) { int dataOffset = GetResourceDataOffset(i); reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin); var typeCode = (ResourceTypeCode)reader.Read7BitEncodedInt(); if (typeCode != ResourceTypeCode.Stream) return false; } } return true; } object? LoadObject(int dataOffset) { try { lock (reader) { if (version == 1) { return LoadObjectV1(dataOffset); } else { return LoadObjectV2(dataOffset); } } } catch (EndOfStreamException e) { throw new BadImageFormatException("Invalid resource file", e); } } string FindType(int typeIndex) { if (typeIndex < 0 || typeIndex >= typeTable.Length) throw new BadImageFormatException("Type index out of bounds"); return typeTable[typeIndex]; } // This takes a virtual offset into the data section and reads an Object // from that location. // Anyone who calls LoadObject should make sure they take a lock so // no one can cause us to do a seek in here. private object? LoadObjectV1(int dataOffset) { Debug.Assert(System.Threading.Monitor.IsEntered(reader)); reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin); int typeIndex = reader.Read7BitEncodedInt(); if (typeIndex == -1) return null; string typeName = FindType(typeIndex); int comma = typeName.IndexOf(','); if (comma > 0) { // strip assembly name typeName = typeName.Substring(0, comma); } switch (typeName) { case "System.String": return reader.ReadString(); case "System.Byte": return reader.ReadByte(); case "System.SByte": return reader.ReadSByte(); case "System.Int16": return reader.ReadInt16(); case "System.UInt16": return reader.ReadUInt16(); case "System.Int32": return reader.ReadInt32(); case "System.UInt32": return reader.ReadUInt32(); case "System.Int64": return reader.ReadInt64(); case "System.UInt64": return reader.ReadUInt64(); case "System.Single": return reader.ReadSingle(); case "System.Double": return reader.ReadDouble(); case "System.DateTime": // Ideally we should use DateTime's ToBinary & FromBinary, // but we can't for compatibility reasons. return new DateTime(reader.ReadInt64()); case "System.TimeSpan": return new TimeSpan(reader.ReadInt64()); case "System.Decimal": int[] bits = new int[4]; for (int i = 0; i < bits.Length; i++) bits[i] = reader.ReadInt32(); return new decimal(bits); default: return new ResourceSerializedObject(FindType(typeIndex), this, reader.BaseStream.Position, usesSerializationFormat); } } private object? LoadObjectV2(int dataOffset) { Debug.Assert(System.Threading.Monitor.IsEntered(reader)); reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin); var typeCode = (ResourceTypeCode)reader.Read7BitEncodedInt(); switch (typeCode) { case ResourceTypeCode.Null: return null; case ResourceTypeCode.String: return reader.ReadString(); case ResourceTypeCode.Boolean: return reader.ReadBoolean(); case ResourceTypeCode.Char: return (char)reader.ReadUInt16(); case ResourceTypeCode.Byte: return reader.ReadByte(); case ResourceTypeCode.SByte: return reader.ReadSByte(); case ResourceTypeCode.Int16: return reader.ReadInt16(); case ResourceTypeCode.UInt16: return reader.ReadUInt16(); case ResourceTypeCode.Int32: return reader.ReadInt32(); case ResourceTypeCode.UInt32: return reader.ReadUInt32(); case ResourceTypeCode.Int64: return reader.ReadInt64(); case ResourceTypeCode.UInt64: return reader.ReadUInt64(); case ResourceTypeCode.Single: return reader.ReadSingle(); case ResourceTypeCode.Double: return reader.ReadDouble(); case ResourceTypeCode.Decimal: return reader.ReadDecimal(); case ResourceTypeCode.DateTime: // Use DateTime's ToBinary & FromBinary. long data = reader.ReadInt64(); return DateTime.FromBinary(data); case ResourceTypeCode.TimeSpan: long ticks = reader.ReadInt64(); return new TimeSpan(ticks); // Special types case ResourceTypeCode.ByteArray: { int len = reader.ReadInt32(); if (len < 0) { throw new BadImageFormatException("Resource with negative length"); } return reader.ReadBytes(len); } case ResourceTypeCode.Stream: { int len = reader.ReadInt32(); if (len < 0) { throw new BadImageFormatException("Resource with negative length"); } byte[] bytes = reader.ReadBytes(len); return new MemoryStream(bytes, writable: false); } default: if (typeCode < ResourceTypeCode.StartOfUserTypes) { throw new BadImageFormatException("Invalid typeCode"); } return new ResourceSerializedObject(FindType(typeCode - ResourceTypeCode.StartOfUserTypes), this, reader.BaseStream.Position, usesSerializationFormat); } } public object? GetResourceValue(int index) { GetResourceName(index, out int dataOffset); return LoadObject(dataOffset); } public IEnumerator> GetEnumerator() { for (int i = 0; i < numResources; i++) { string name = GetResourceName(i, out int dataOffset); object? val = LoadObject(dataOffset); yield return new KeyValuePair(name, val); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } long[] GetStartPositions() { long[]? positions = LazyInit.VolatileRead(ref startPositions); if (positions != null) return positions; lock (reader) { // double-checked locking positions = LazyInit.VolatileRead(ref startPositions); if (positions != null) return positions; positions = new long[numResources * 2]; int outPos = 0; for (int i = 0; i < numResources; i++) { positions[outPos++] = nameSectionPosition + namePositions[i]; positions[outPos++] = dataSectionPosition + GetResourceDataOffset(i); } Array.Sort(positions); return LazyInit.GetOrSet(ref startPositions, positions); } } internal byte[] GetBytesForSerializedObject(long pos, bool usesSerializationFormat) { long[] positions = GetStartPositions(); int i = Array.BinarySearch(positions, pos); if (i < 0) { // 'pos' the the start position of the serialized object data // This is the position after the type code, so it should not appear in the 'positions' array. // Set i to the index of the next position after 'pos'. i = ~i; // Note: if 'pos' does exist in the array, that means the stream has length 0, // so we keep the i that we found. } lock (reader) { long endPos; if (i == positions.Length) { endPos = reader.BaseStream.Length; } else { endPos = positions[i]; } int len = (int)(endPos - pos); reader.Seek(pos, SeekOrigin.Begin); if (usesSerializationFormat) { int kind = reader.Read7BitEncodedInt(); Debug.Assert(Enum.IsDefined(typeof(SerializationFormat), kind)); len = reader.Read7BitEncodedInt(); } return reader.ReadBytes(len); } } } public class ResourceSerializedObject { public string? TypeName { get; } readonly ResourcesFile file; readonly long position; readonly bool usesSerializationFormat; internal ResourceSerializedObject(string? typeName, ResourcesFile file, long position, bool usesSerializationFormat) { this.TypeName = usesSerializationFormat ? typeName : null; this.file = file; this.position = position; this.usesSerializationFormat = usesSerializationFormat; } /// /// Gets a stream that starts with the serialized object data. /// public Stream GetStream() { return new MemoryStream(file.GetBytesForSerializedObject(position, usesSerializationFormat), writable: false); } /// /// Gets the serialized object data. /// public byte[] GetBytes() { return file.GetBytesForSerializedObject(position, usesSerializationFormat); } } } ================================================ FILE: ICSharpCode.Decompiler/Util/TreeTraversal.cs ================================================ #nullable enable // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Util { /// /// Static helper methods for traversing trees. /// public static class TreeTraversal { /// /// Converts a tree data structure into a flat list by traversing it in pre-order. /// /// The root element of the tree. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in pre-order. public static IEnumerable PreOrder(T root, Func?> recursion) { return PreOrder(new T[] { root }, recursion); } /// /// Converts a tree data structure into a flat list by traversing it in pre-order. /// /// The root elements of the forest. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in pre-order. public static IEnumerable PreOrder(IEnumerable input, Func?> recursion) { Stack> stack = new Stack>(); try { stack.Push(input.GetEnumerator()); while (stack.Count > 0) { while (stack.Peek().MoveNext()) { T element = stack.Peek().Current; yield return element; IEnumerable? children = recursion(element); if (children != null) { stack.Push(children.GetEnumerator()); } } stack.Pop().Dispose(); } } finally { while (stack.Count > 0) { stack.Pop().Dispose(); } } } /// /// Converts a tree data structure into a flat list by traversing it in post-order. /// /// The root element of the tree. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in post-order. public static IEnumerable PostOrder(T root, Func?> recursion) { return PostOrder(new T[] { root }, recursion); } /// /// Converts a tree data structure into a flat list by traversing it in post-order. /// /// The root elements of the forest. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in post-order. public static IEnumerable PostOrder(IEnumerable input, Func?> recursion) { Stack> stack = new Stack>(); try { stack.Push(input.GetEnumerator()); while (stack.Count > 0) { while (stack.Peek().MoveNext()) { T element = stack.Peek().Current; IEnumerable? children = recursion(element); if (children != null) { stack.Push(children.GetEnumerator()); } else { yield return element; } } stack.Pop().Dispose(); if (stack.Count > 0) yield return stack.Peek().Current; } } finally { while (stack.Count > 0) { stack.Pop().Dispose(); } } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/UnicodeNewline.cs ================================================ #nullable enable // // UnicodeNewline.cs // // Author: // Mike Krüger // // Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) // // 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; namespace ICSharpCode.Decompiler.Util { public enum UnicodeNewline { Unknown, /// /// Line Feed, U+000A /// LF = 0x0A, CRLF = 0x0D0A, /// /// Carriage Return, U+000D /// CR = 0x0D, /// /// Next Line, U+0085 /// NEL = 0x85, /// /// Vertical Tab, U+000B /// VT = 0x0B, /// /// Form Feed, U+000C /// FF = 0x0C, /// /// Line Separator, U+2028 /// LS = 0x2028, /// /// Paragraph Separator, U+2029 /// PS = 0x2029 } /// /// Defines unicode new lines according to Unicode Technical Report #13 /// http://www.unicode.org/standard/reports/tr13/tr13-5.html /// public static class NewLine { /// /// Carriage Return, U+000D /// public const char CR = (char)0x0D; /// /// Line Feed, U+000A /// public const char LF = (char)0x0A; /// /// Next Line, U+0085 /// public const char NEL = (char)0x85; /// /// Vertical Tab, U+000B /// public const char VT = (char)0x0B; /// /// Form Feed, U+000C /// public const char FF = (char)0x0C; /// /// Line Separator, U+2028 /// public const char LS = (char)0x2028; /// /// Paragraph Separator, U+2029 /// public const char PS = (char)0x2029; /// /// Determines if a char is a new line delimiter. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// A callback getting the next character (may be null). public static int GetDelimiterLength(char curChar, Func? nextChar = null) { if (curChar == CR) { if (nextChar != null && nextChar() == LF) return 2; return 1; } if (curChar == LF || curChar == NEL || curChar == VT || curChar == FF || curChar == LS || curChar == PS) return 1; return 0; } /// /// Determines if a char is a new line delimiter. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// The next character (if != LF then length will always be 0 or 1). public static int GetDelimiterLength(char curChar, char nextChar) { if (curChar == CR) { if (nextChar == LF) return 2; return 1; } if (curChar == LF || curChar == NEL || curChar == VT || curChar == FF || curChar == LS || curChar == PS) return 1; return 0; } /// /// Determines if a char is a new line delimiter. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// The length of the delimiter /// The type of the delimiter /// A callback getting the next character (may be null). public static bool TryGetDelimiterLengthAndType(char curChar, out int length, out UnicodeNewline type, Func? nextChar = null) { if (curChar == CR) { if (nextChar != null && nextChar() == LF) { length = 2; type = UnicodeNewline.CRLF; } else { length = 1; type = UnicodeNewline.CR; } return true; } switch (curChar) { case LF: type = UnicodeNewline.LF; length = 1; return true; case NEL: type = UnicodeNewline.NEL; length = 1; return true; case VT: type = UnicodeNewline.VT; length = 1; return true; case FF: type = UnicodeNewline.FF; length = 1; return true; case LS: type = UnicodeNewline.LS; length = 1; return true; case PS: type = UnicodeNewline.PS; length = 1; return true; } length = -1; type = UnicodeNewline.Unknown; return false; } /// /// Determines if a char is a new line delimiter. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// The length of the delimiter /// The type of the delimiter /// The next character (if != LF then length will always be 0 or 1). public static bool TryGetDelimiterLengthAndType(char curChar, out int length, out UnicodeNewline type, char nextChar) { if (curChar == CR) { if (nextChar == LF) { length = 2; type = UnicodeNewline.CRLF; } else { length = 1; type = UnicodeNewline.CR; } return true; } switch (curChar) { case LF: type = UnicodeNewline.LF; length = 1; return true; case NEL: type = UnicodeNewline.NEL; length = 1; return true; case VT: type = UnicodeNewline.VT; length = 1; return true; case FF: type = UnicodeNewline.FF; length = 1; return true; case LS: type = UnicodeNewline.LS; length = 1; return true; case PS: type = UnicodeNewline.PS; length = 1; return true; } length = -1; type = UnicodeNewline.Unknown; return false; } /// /// Gets the new line type of a given char/next char. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// A callback getting the next character (may be null). public static UnicodeNewline GetDelimiterType(char curChar, Func? nextChar = null) { switch (curChar) { case CR: if (nextChar != null && nextChar() == LF) return UnicodeNewline.CRLF; return UnicodeNewline.CR; case LF: return UnicodeNewline.LF; case NEL: return UnicodeNewline.NEL; case VT: return UnicodeNewline.VT; case FF: return UnicodeNewline.FF; case LS: return UnicodeNewline.LS; case PS: return UnicodeNewline.PS; } return UnicodeNewline.Unknown; } /// /// Gets the new line type of a given char/next char. /// /// 0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter. /// The current character. /// The next character (if != LF then length will always be 0 or 1). public static UnicodeNewline GetDelimiterType(char curChar, char nextChar) { switch (curChar) { case CR: if (nextChar == LF) return UnicodeNewline.CRLF; return UnicodeNewline.CR; case LF: return UnicodeNewline.LF; case NEL: return UnicodeNewline.NEL; case VT: return UnicodeNewline.VT; case FF: return UnicodeNewline.FF; case LS: return UnicodeNewline.LS; case PS: return UnicodeNewline.PS; } return UnicodeNewline.Unknown; } /// /// Determines if a char is a new line delimiter. /// /// Note that the only 2 char wide new line is CR LF and both chars are new line /// chars on their own. For most cases GetDelimiterLength is the better choice. /// public static bool IsNewLine(char ch) { return ch == NewLine.CR || ch == NewLine.LF || ch == NewLine.NEL || ch == NewLine.VT || ch == NewLine.FF || ch == NewLine.LS || ch == NewLine.PS; } /// /// Gets the new line as a string. /// public static string GetString(UnicodeNewline newLine) { switch (newLine) { case UnicodeNewline.Unknown: return ""; case UnicodeNewline.LF: return "\n"; case UnicodeNewline.CRLF: return "\r\n"; case UnicodeNewline.CR: return "\r"; case UnicodeNewline.NEL: return "\u0085"; case UnicodeNewline.VT: return "\u000B"; case UnicodeNewline.FF: return "\u000C"; case UnicodeNewline.LS: return "\u2028"; case UnicodeNewline.PS: return "\u2029"; default: throw new ArgumentOutOfRangeException(); } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/UnionFind.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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. #nullable enable using System.Collections.Generic; namespace ICSharpCode.Decompiler.Util { /// /// Union-Find data structure. /// public class UnionFind where T : notnull { Dictionary mapping; class Node { public int rank; public Node parent; public T value; internal Node(T value) { this.value = value; this.parent = this; } } public UnionFind() { mapping = new Dictionary(); } Node GetNode(T element) { if (!mapping.TryGetValue(element, out Node? node)) { node = new Node(element); node.parent = node; mapping.Add(element, node); } return node; } public T Find(T element) { return FindRoot(GetNode(element)).value; } Node FindRoot(Node node) { if (node.parent != node) node.parent = FindRoot(node.parent); return node.parent; } public void Merge(T a, T b) { var rootA = FindRoot(GetNode(a)); var rootB = FindRoot(GetNode(b)); if (rootA == rootB) return; if (rootA.rank < rootB.rank) rootA.parent = rootB; else if (rootA.rank > rootB.rank) rootB.parent = rootA; else { rootB.parent = rootA; rootA.rank++; } } } } ================================================ FILE: ICSharpCode.Decompiler/Util/Win32Resources.cs ================================================ #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection.PortableExecutable; namespace ICSharpCode.Decompiler.Util { /// /// Represents win32 resources /// public static class Win32Resources { /// /// Reads win32 resource root directory /// /// /// public static unsafe Win32ResourceDirectory? ReadWin32Resources(this PEReader pe) { if (pe == null) { throw new ArgumentNullException(nameof(pe)); } int rva = pe.PEHeaders.PEHeader?.ResourceTableDirectory.RelativeVirtualAddress ?? 0; if (rva == 0) return null; byte* pRoot = pe.GetSectionData(rva).Pointer; return new Win32ResourceDirectory(pe, pRoot, 0, new Win32ResourceName("Root")); } public static Win32ResourceDirectory? Find(this Win32ResourceDirectory root, Win32ResourceName type) { if (root is null) throw new ArgumentNullException(nameof(root)); if (!root.Name.HasName || root.Name.Name != "Root") throw new ArgumentOutOfRangeException(nameof(root)); if (type is null) throw new ArgumentNullException(nameof(type)); return root.FindDirectory(type); } public static Win32ResourceDirectory? Find(this Win32ResourceDirectory root, Win32ResourceName type, Win32ResourceName name) { if (root is null) throw new ArgumentNullException(nameof(root)); if (!root.Name.HasName || root.Name.Name != "Root") throw new ArgumentOutOfRangeException(nameof(root)); if (type is null) throw new ArgumentNullException(nameof(type)); if (name is null) throw new ArgumentNullException(nameof(name)); return root.FindDirectory(type)?.FindDirectory(name); } public static Win32ResourceData? Find(this Win32ResourceDirectory root, Win32ResourceName type, Win32ResourceName name, Win32ResourceName langId) { if (root is null) throw new ArgumentNullException(nameof(root)); if (!root.Name.HasName || root.Name.Name != "Root") throw new ArgumentOutOfRangeException(nameof(root)); if (type is null) throw new ArgumentNullException(nameof(type)); if (name is null) throw new ArgumentNullException(nameof(name)); if (langId is null) throw new ArgumentNullException(nameof(langId)); return root.FindDirectory(type)?.FindDirectory(name)?.FindData(langId); } } [DebuggerDisplay("Directory: {Name}")] public sealed class Win32ResourceDirectory { #region Structure public uint Characteristics { get; } public uint TimeDateStamp { get; } public ushort MajorVersion { get; } public ushort MinorVersion { get; } public ushort NumberOfNamedEntries { get; } public ushort NumberOfIdEntries { get; } #endregion public Win32ResourceName Name { get; } public IList Directories { get; } public IList Datas { get; } internal unsafe Win32ResourceDirectory(PEReader pe, byte* pRoot, int offset, Win32ResourceName name) { var p = (IMAGE_RESOURCE_DIRECTORY*)(pRoot + offset); Characteristics = p->Characteristics; TimeDateStamp = p->TimeDateStamp; MajorVersion = p->MajorVersion; MinorVersion = p->MinorVersion; NumberOfNamedEntries = p->NumberOfNamedEntries; NumberOfIdEntries = p->NumberOfIdEntries; Name = name; Directories = new List(); Datas = new List(); var pEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(p + 1); int total = NumberOfNamedEntries + NumberOfIdEntries; for (int i = 0; i < total; i++) { var pEntry = pEntries + i; name = new Win32ResourceName(pRoot, pEntry); if ((pEntry->OffsetToData & 0x80000000) == 0) Datas.Add(new Win32ResourceData(pe, pRoot, (int)pEntry->OffsetToData, name)); else Directories.Add(new Win32ResourceDirectory(pe, pRoot, (int)(pEntry->OffsetToData & 0x7FFFFFFF), name)); } } static unsafe string ReadString(byte* pRoot, int offset) { var pString = (IMAGE_RESOURCE_DIRECTORY_STRING*)(pRoot + offset); return new string(pString->NameString, 0, pString->Length); } public Win32ResourceDirectory? FindDirectory(Win32ResourceName name) { foreach (var directory in Directories) { if (directory.Name == name) return directory; } return null; } public Win32ResourceData? FindData(Win32ResourceName name) { foreach (var data in Datas) { if (data.Name == name) return data; } return null; } public Win32ResourceDirectory? FirstDirectory() { return Directories.Count != 0 ? Directories[0] : null; } public Win32ResourceData? FirstData() { return Datas.Count != 0 ? Datas[0] : null; } } [DebuggerDisplay("Data: {Name}")] public sealed unsafe class Win32ResourceData { #region Structure public uint OffsetToData { get; } public uint Size { get; } public uint CodePage { get; } public uint Reserved { get; } #endregion private readonly void* _pointer; public Win32ResourceName Name { get; } public byte[] Data { get { byte[] data = new byte[Size]; fixed (void* pData = data) Buffer.MemoryCopy(_pointer, pData, Size, Size); return data; } } internal Win32ResourceData(PEReader pe, byte* pRoot, int offset, Win32ResourceName name) { var p = (IMAGE_RESOURCE_DATA_ENTRY*)(pRoot + offset); OffsetToData = p->OffsetToData; Size = p->Size; CodePage = p->CodePage; Reserved = p->Reserved; _pointer = pe.GetSectionData((int)OffsetToData).Pointer; Name = name; } } public sealed class Win32ResourceName { private readonly object _name; public bool HasName => _name is string; public bool HasId => _name is ushort; public string Name => (string)_name; public ushort Id => (ushort)_name; public Win32ResourceName(string name) { _name = name ?? throw new ArgumentNullException(nameof(name)); } public Win32ResourceName(int id) : this(checked((ushort)id)) { } public Win32ResourceName(ushort id) { _name = id; } internal unsafe Win32ResourceName(byte* pRoot, IMAGE_RESOURCE_DIRECTORY_ENTRY* pEntry) { _name = (pEntry->Name & 0x80000000) == 0 ? (object)(ushort)pEntry->Name : ReadString(pRoot, (int)(pEntry->Name & 0x7FFFFFFF)); static string ReadString(byte* pRoot, int offset) { var pString = (IMAGE_RESOURCE_DIRECTORY_STRING*)(pRoot + offset); return new string(pString->NameString, 0, pString->Length); } } public static bool operator ==(Win32ResourceName x, Win32ResourceName y) { if (x.HasName) { return y.HasName ? string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase) == 0 : false; } else { return y.HasId ? x.Id == y.Id : false; } } public static bool operator !=(Win32ResourceName x, Win32ResourceName y) { return !(x == y); } public override int GetHashCode() { return _name.GetHashCode(); } public override bool Equals(object? obj) { if (!(obj is Win32ResourceName name)) return false; return this == name; } public override string ToString() { return HasName ? $"Name: {Name}" : $"Id: {Id}"; } } internal struct IMAGE_RESOURCE_DIRECTORY { public uint Characteristics; public uint TimeDateStamp; public ushort MajorVersion; public ushort MinorVersion; public ushort NumberOfNamedEntries; public ushort NumberOfIdEntries; } internal struct IMAGE_RESOURCE_DIRECTORY_ENTRY { public uint Name; public uint OffsetToData; } internal unsafe struct IMAGE_RESOURCE_DIRECTORY_STRING { public ushort Length; public fixed char NameString[1]; } internal struct IMAGE_RESOURCE_DATA_ENTRY { public uint OffsetToData; public uint Size; public uint CodePage; public uint Reserved; } } ================================================ FILE: ICSharpCode.Decompiler/packages.lock.json ================================================ { "version": 1, "dependencies": { ".NETStandard,Version=v2.0": { "Microsoft.CodeAnalysis.NetAnalyzers": { "type": "Direct", "requested": "[10.0.201, )", "resolved": "10.0.201", "contentHash": "MTE+F0fj0N8dtfkEGIcX3/rEycMbOf54DXq/n6n8cE6ZfDIPEElY8lc4X5BpZ+c7DBoQDGLG0iBY/W0YZ3nhtw==" }, "Microsoft.Sbom.Targets": { "type": "Direct", "requested": "[4.1.5, )", "resolved": "4.1.5", "contentHash": "i5z+cNu/cOcdO0AgFB8aXk8w6In2H+haaDfSgd9ImvQIK+rSHavHZIogVoAZLL8jLwYx4bAcs5b7EyuMMG4mQQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[10.0.201, )", "resolved": "10.0.201", "contentHash": "qxYAmO4ktzd9L+HMdnqWucxpu7bI9undPyACXOMqPyhaiMtbpbYL/n0ACyWIJlbyEJrXFwxiOaBOSasLtDvsCg==", "dependencies": { "Microsoft.Build.Tasks.Git": "10.0.201", "Microsoft.SourceLink.Common": "10.0.201", "System.IO.Hashing": "10.0.5" } }, "NETStandard.Library": { "type": "Direct", "requested": "[2.0.3, )", "resolved": "2.0.3", "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } }, "System.Collections.Immutable": { "type": "Direct", "requested": "[9.0.0, )", "resolved": "9.0.0", "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Reflection.Metadata": { "type": "Direct", "requested": "[9.0.0, )", "resolved": "9.0.0", "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==", "dependencies": { "System.Collections.Immutable": "9.0.0", "System.Memory": "4.5.5" } }, "TunnelVisionLabs.ReferenceAssemblyAnnotator": { "type": "Direct", "requested": "[1.0.0-alpha.160, )", "resolved": "1.0.0-alpha.160", "contentHash": "ktxB8PGoPpIaYKjLk/+P94Fi2Qw2E1Dw7atBQRrKnHA57sk8WwmkI4RJmg6s5ph4k1RIaaAZMus05ah/AikEkA==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "10.0.201", "contentHash": "DMYBnrFZvLnBKn14VavEuuIr31CY6YY2i2L9P8DorS/Qp6ifRR8ZPLdJCFLFfjikNq8DykbYyLd/RP6lSqHcWw==", "dependencies": { "System.IO.Hashing": "10.0.5" } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "10.0.201", "contentHash": "QbBYhkjgL6rCnBfDbzsAJLlsad13TlBHqYCFDIw56OO2g6ix+9RsmY8uxiQGdWwFKbZXaXyAA6jDCzFYVGCZDw==" }, "System.Buffers": { "type": "Transitive", "resolved": "4.6.1", "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.IO.Hashing": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "8IBJWcCT9+e4Bmevm4T7+fQEiAh133KGiz4oiVTgJckd3Q76OFdR1falgn9lpz7+C4HJvogCDJeAa2QmvbeVtg==", "dependencies": { "System.Buffers": "4.6.1", "System.Memory": "4.6.3" } }, "System.Memory": { "type": "Transitive", "resolved": "4.6.3", "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { "System.Buffers": "4.6.1", "System.Numerics.Vectors": "4.6.1", "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.6.1", "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.1.2", "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" } } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/Demo.ps1 ================================================ $basePath = $PSScriptRoot if ([string]::IsNullOrEmpty($basePath)) { $basePath = Split-Path -parent $psISE.CurrentFile.Fullpath } $modulePath = $basePath + '\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.Powershell.dll' Import-Module $modulePath $version = Get-DecompilerVersion Write-Output $version # different test assemblies - it makes a difference wrt .deps.json so there are two netstandard tests here $asm_netstdWithDepsJson = $basePath + '\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.Powershell.dll' $asm_netstd = $basePath + '\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.dll' $decompiler = Get-Decompiler $asm_netstdWithDepsJson $classes = Get-DecompiledTypes $decompiler -Types class $classes.Count foreach ($c in $classes) { Write-Output $c.FullName } Get-DecompiledSource $decompiler -TypeName ICSharpCode.Decompiler.PowerShell.GetDecompilerCmdlet Get-DecompiledProject $decompiler -OutputPath .\decomptest ================================================ FILE: ICSharpCode.Decompiler.PowerShell/ErrorIds.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace ICSharpCode.Decompiler.PowerShell { public static class ErrorIds { public static readonly string AssemblyLoadFailed = "1"; public static readonly string DecompilationFailed = "2"; } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs ================================================ using System; using System.IO; using System.Management.Automation; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "DecompiledProject")] [OutputType(typeof(string))] public class GetDecompiledProjectCmdlet : PSCmdlet, IProgress { [Parameter(Position = 0, Mandatory = true)] public CSharpDecompiler Decompiler { get; set; } [Parameter(Position = 1, Mandatory = true)] [Alias("PSPath", "OutputPath")] [ValidateNotNullOrEmpty] public string LiteralPath { get; set; } readonly object syncObject = new object(); int completed; string fileName; ProgressRecord progress; public void Report(DecompilationProgress value) { lock (syncObject) { completed++; progress = new ProgressRecord(1, "Decompiling " + fileName, $"Completed {completed} of {value.TotalUnits}: {value.Status}") { PercentComplete = (int)(completed * 100.0 / value.TotalUnits) }; } } protected override void ProcessRecord() { string path = GetUnresolvedProviderPathFromPSPath(LiteralPath); if (!Directory.Exists(path)) { WriteObject("Destination directory must exist prior to decompilation"); return; } try { var task = Task.Run(() => DoDecompile(path)); int timeout = 100; // Give the decompiler some time to spin up all threads Thread.Sleep(timeout); while (!task.IsCompleted) { ProgressRecord progress; lock (syncObject) { progress = this.progress; this.progress = null; } if (progress != null) { timeout = 100; WriteProgress(progress); } else { Thread.Sleep(timeout); timeout = Math.Min(1000, timeout * 2); } } task.Wait(); WriteProgress(new ProgressRecord(1, "Decompiling " + fileName, "Decompilation finished") { RecordType = ProgressRecordType.Completed }); } catch (Exception e) { WriteVerbose(e.ToString()); WriteError(new ErrorRecord(e, ErrorIds.DecompilationFailed, ErrorCategory.OperationStopped, null)); } } private void DoDecompile(string path) { MetadataFile module = Decompiler.TypeSystem.MainModule.MetadataFile; var assemblyResolver = new UniversalAssemblyResolver(module.FileName, false, module.Metadata.DetectTargetFrameworkId()); WholeProjectDecompiler decompiler = new WholeProjectDecompiler(assemblyResolver); decompiler.ProgressIndicator = this; fileName = module.FileName; completed = 0; decompiler.DecompileProject(module, path); } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetDecompiledSourceCmdlet.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Text; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "DecompiledSource")] [OutputType(typeof(string))] public class GetDecompiledSourceCmdlet : PSCmdlet { [Parameter(Position = 0, Mandatory = true)] public CSharpDecompiler Decompiler { get; set; } [Parameter] public string TypeName { get; set; } = string.Empty; protected override void ProcessRecord() { try { StringWriter output = new StringWriter(); if (TypeName == null) { output.Write(Decompiler.DecompileWholeModuleAsString()); } else { var name = new FullTypeName(TypeName); output.Write(Decompiler.DecompileTypeAsString(name)); } WriteObject(output.ToString()); } catch (Exception e) { WriteVerbose(e.ToString()); WriteError(new ErrorRecord(e, ErrorIds.DecompilationFailed, ErrorCategory.OperationStopped, null)); } } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetDecompiledTypesCmdlet.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation; using System.Text; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "DecompiledTypes")] [OutputType(typeof(ITypeDefinition[]))] public class GetDecompiledTypesCmdlet : PSCmdlet { [Parameter(Position = 0, Mandatory = true)] public CSharpDecompiler Decompiler { get; set; } [Parameter(Mandatory = true)] public string[] Types { get; set; } protected override void ProcessRecord() { HashSet kinds = TypesParser.ParseSelection(Types); try { List output = new List(); foreach (var type in Decompiler.TypeSystem.MainModule.TypeDefinitions) { if (!kinds.Contains(type.Kind)) continue; output.Add(type); } WriteObject(output.ToArray()); } catch (Exception e) { WriteVerbose(e.ToString()); WriteError(new ErrorRecord(e, ErrorIds.DecompilationFailed, ErrorCategory.OperationStopped, null)); } } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Reflection.PortableExecutable; using System.Text; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX.PdbProvider; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "Decompiler")] [OutputType(typeof(CSharpDecompiler))] public class GetDecompilerCmdlet : PSCmdlet { [Parameter(Position = 0, Mandatory = true, HelpMessage = "Path to the assembly you want to decompile")] [Alias("PSPath")] [ValidateNotNullOrEmpty] public string LiteralPath { get; set; } [Parameter(HelpMessage = "C# Language version to be used by the decompiler")] public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Latest; [Parameter(HelpMessage = "Remove dead stores")] public bool RemoveDeadStores { get; set; } [Parameter(HelpMessage = "Remove dead code")] public bool RemoveDeadCode { get; set; } [Parameter(HelpMessage = "Use PDB")] public string PDBFilePath { get; set; } protected override void ProcessRecord() { string path = GetUnresolvedProviderPathFromPSPath(LiteralPath); try { var module = new PEFile(LiteralPath, new FileStream(LiteralPath, FileMode.Open, FileAccess.Read), PEStreamOptions.Default); var debugInfo = DebugInfoUtils.FromFile(module, PDBFilePath); var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) { ThrowOnAssemblyResolveErrors = false, RemoveDeadCode = RemoveDeadCode, RemoveDeadStores = RemoveDeadStores, UseDebugSymbols = debugInfo != null, ShowDebugInfo = debugInfo != null, }); decompiler.DebugInfoProvider = debugInfo; WriteObject(decompiler); } catch (Exception e) { WriteVerbose(e.ToString()); WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null)); } } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetDecompilerVersion.cs ================================================ using System; using System.Management.Automation; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "DecompilerVersion")] [OutputType(typeof(string))] public class GetDecompilerVersion : PSCmdlet { protected override void ProcessRecord() { WriteObject(typeof(FullTypeName).Assembly.GetName().Version.ToString()); } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/GetTargetFramework.cs ================================================ using System.Management.Automation; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.PowerShell { [Cmdlet(VerbsCommon.Get, "TargetFramework")] [OutputType(typeof(string))] public class GetTargetFramework : PSCmdlet { [Parameter(Position = 0, Mandatory = true)] public CSharpDecompiler Decompiler { get; set; } protected override void ProcessRecord() { MetadataFile module = Decompiler.TypeSystem.MainModule.MetadataFile; WriteObject(module.Metadata.DetectTargetFrameworkId()); } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj ================================================ netstandard2.0 true true ICSharpCode.Decompiler.PowerShell 8.0 ================================================ FILE: ICSharpCode.Decompiler.PowerShell/NullAttributes.cs ================================================ #if !NETCORE #nullable enable namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)] internal sealed class NotNullIfNotNullAttribute : Attribute { public string ParameterName { get; } public NotNullIfNotNullAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] internal sealed class NotNullWhenAttribute : Attribute { public NotNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } public bool ReturnValue { get; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] internal sealed class DoesNotReturnIfAttribute : Attribute { public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; public bool ParameterValue { get; } } } #endif ================================================ FILE: ICSharpCode.Decompiler.PowerShell/README.md ================================================ # ILSpy PowerShell Module Built using https://github.com/PowerShell/PowerShell/blob/master/docs/cmdlet-example/command-line-simple-example.md as guideline Sample usage: Demo.ps1 Tested with: PowerShell 5.1 on Windows, PowerShell 7+ on Windows and Mac ## Missing Publishing to https://www.powershellgallery.com/ * https://learn.microsoft.com/en-us/powershell/gallery/how-to/publishing-packages/publishing-a-package * https://learn.microsoft.com/en-us/powershell/gallery/concepts/publishing-guidelines ## Links for developing PS cmdlets * https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/how-to-write-a-simple-cmdlet * https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands * https://github.com/mmaitre314/PowerShellGet-Test-Binary-Module * https://www.red-gate.com/simple-talk/dotnet/net-development/using-c-to-create-powershell-cmdlets-beyond-the-basics/ * https://www.google.com/search?q=write+a+module+for+powershell+core ================================================ FILE: ICSharpCode.Decompiler.PowerShell/TypesParser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.PowerShell { public static class TypesParser { public static HashSet ParseSelection(string[] values) { var possibleValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["class"] = TypeKind.Class, ["struct"] = TypeKind.Struct, ["interface"] = TypeKind.Interface, ["enum"] = TypeKind.Enum, ["delegate"] = TypeKind.Delegate }; HashSet kinds = new HashSet(); if (values.Length == 1 && !possibleValues.Keys.Any(v => values[0].StartsWith(v, StringComparison.OrdinalIgnoreCase))) { foreach (char ch in values[0]) { switch (ch) { case 'c': kinds.Add(TypeKind.Class); break; case 'i': kinds.Add(TypeKind.Interface); break; case 's': kinds.Add(TypeKind.Struct); break; case 'd': kinds.Add(TypeKind.Delegate); break; case 'e': kinds.Add(TypeKind.Enum); break; } } } else { foreach (var value in values) { string v = value; while (v.Length > 0 && !possibleValues.ContainsKey(v)) v = v.Remove(v.Length - 1); if (possibleValues.TryGetValue(v, out var kind)) kinds.Add(kind); } } return kinds; } } } ================================================ FILE: ICSharpCode.Decompiler.PowerShell/manifest.psd1 ================================================ @{ # Script module or binary module file associated with this manifest. RootModule = 'ICSharpCode.Decompiler.PowerShell.dll' # Version number of this module. ModuleVersion = '8.0.0.0' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module GUID = '198b4312-cbe7-417e-81a7-1aaff467ef06' # Author of this module Author = 'ILSpy Contributors' # Company or vendor of this module CompanyName = 'ic#code' # Copyright statement for this module Copyright = 'Copyright 2011-2023 AlphaSierraPapa' # Description of the functionality provided by this module Description = 'PowerShell front-end for ILSpy' # Minimum version of the PowerShell engine required by this module # PowerShellVersion = '' # Name of the PowerShell host required by this module # PowerShellHostName = '' # Minimum version of the PowerShell host required by this module # PowerShellHostVersion = '' # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # DotNetFrameworkVersion = '' # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # ClrVersion = '' # Processor architecture (None, X86, Amd64) required by this module # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module # RequiredModules = @() # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() # Script files (.ps1) that are run in the caller's environment prior to importing this module. # ScriptsToProcess = @() # Type files (.ps1xml) to be loaded when importing this module # TypesToProcess = @() # Format files (.ps1xml) to be loaded when importing this module # FormatsToProcess = @() # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @() # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @( 'Get-DecompiledProject', 'Get-DecompiledSource', 'Get-DecompiledTypes', 'Get-Decompiler', 'Get-DecompilerVersion', 'Get-TargetFramework' ) # Variables to export from this module VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. AliasesToExport = @() # DSC resources to export from this module # DscResourcesToExport = @() # List of all modules packaged with this module # ModuleList = @() # List of all files packaged with this module # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. # Tags = @() # A URL to the license for this module. # LicenseUri = '' # A URL to the main website for this project. ProjectUri = 'https://github.com/icsharpcode/ILSpy' # A URL to an icon representing this module. # IconUri = '' # ReleaseNotes of this module # ReleaseNotes = '' # Prerelease string of this module # Prerelease = '' # Flag to indicate whether the module requires explicit user acceptance for install/update/save # RequireLicenseAcceptance = $false # External dependent modules of this module # ExternalModuleDependencies = @() } # End of PSData hashtable } # End of PrivateData hashtable # HelpInfo URI of this module # HelpInfoURI = '' # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # DefaultCommandPrefix = '' } ================================================ FILE: ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj ================================================ Exe net10.0 enable ================================================ FILE: ICSharpCode.Decompiler.TestRunner/Program.cs ================================================ // Copyright (c) 2022 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.Reflection; using System.Runtime.Loader; namespace ICSharpCode.Decompiler.TestRunner; public static class Program { static int Main(string[] args) { AssemblyLoadContext context = new("TestRunner", isCollectible: true); context.Resolving += ContextResolving; try { var mainAssembly = context.LoadFromAssemblyPath(args[0]); int paramCount = mainAssembly.EntryPoint!.GetParameters().Length; object? result = mainAssembly.EntryPoint!.Invoke(null, paramCount == 0 ? new object[0] : new object[1] { new string[0] }); return result is int i ? i : 0; } catch (Exception ex) { Console.Error.WriteLine("TestRunner crashed:"); Console.Error.WriteLine(ex.ToString()); return -1; } finally { context.Unload(); context.Resolving -= ContextResolving; } } private static Assembly? ContextResolving(AssemblyLoadContext context, AssemblyName name) { return null; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/.editorconfig ================================================ # We disable some of the rules that don't make sense in our test [*.cs] dotnet_diagnostic.CA1060.severity = none dotnet_diagnostic.CA1063.severity = none dotnet_diagnostic.CA1065.severity = none dotnet_diagnostic.CA1401.severity = none dotnet_diagnostic.CA1821.severity = none dotnet_diagnostic.CA2002.severity = none dotnet_diagnostic.CA2101.severity = none dotnet_diagnostic.CA2241.severity = none dotnet_diagnostic.CA2263.severity = none # disable because it's choking on our test code in UndocumentedExpressions.cs ================================================ FILE: ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class CorrectnessTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/Correctness"; [Test] public void AllFilesHaveTests() { var testNames = typeof(CorrectnessTestRunner).GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension == ".txt" || file.Extension == ".exe" || file.Extension == ".config") continue; var testName = Path.GetFileNameWithoutExtension(file.Name); Assert.That(testNames, Has.Member(testName)); } } static readonly CompilerOptions[] noMonoOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] net40OnlyOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40 }; static readonly CompilerOptions[] defaultOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, CompilerOptions.UseMcs2_6_4, CompilerOptions.Optimize | CompilerOptions.UseMcs2_6_4, CompilerOptions.UseMcs5_23, CompilerOptions.Optimize | CompilerOptions.UseMcs5_23 }; static readonly CompilerOptions[] roslynOnlyOptions = { CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn2OrNewerOptions = { CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslynLatestOnlyOptions = { CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; [Test] public async Task Comparisons([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Conversions([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task FloatingPointArithmetic([ValueSource(nameof(noMonoOptions))] CompilerOptions options, [Values(32, 64)] int bits) { // The behavior of the #1794 incorrect `(float)(double)val` cast only causes test failures // for some runtime+compiler combinations. if (bits == 32) options |= CompilerOptions.Force32Bit; // Mono is excluded because we never use it for the second pass, so the test ends up failing // due to some Mono vs. Roslyn compiler differences. await RunCS(options: options); } [Test] public async Task HelloWorld([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task ControlFlow([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task CompoundAssignment([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Switch([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Using([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Loops([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task NullableTests([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task ValueTypeCall([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task DecimalFields([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task UndocumentedExpressions([ValueSource(nameof(noMonoOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Uninit([ValueSource(nameof(noMonoOptions))] CompilerOptions options) { await RunVB(options: options); } [Test] public async Task MemberLookup([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task OverloadResolution([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task ExpressionTrees([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task DeconstructionTests([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task BitNot([Values(false, true)] bool force32Bit) { CompilerOptions compiler = CompilerOptions.UseDebug; AssemblerOptions asm = AssemblerOptions.None; if (force32Bit) { compiler |= CompilerOptions.Force32Bit; asm |= AssemblerOptions.Force32Bit; } await RunIL("BitNot.il", compiler, asm); } [Test] public async Task Jmp() { await RunIL("Jmp.il"); } [Test] public async Task NonGenericConstrainedCallVirt() { await RunIL("NonGenericConstrainedCallVirt.il", CompilerOptions.UseRoslynLatest); } [Test] public async Task StackTests() { // IL contains .corflags = 32BITREQUIRED await RunIL("StackTests.il", CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit); } [Test] public async Task StackTypes([Values(false, true)] bool force32Bit) { CompilerOptions compiler = CompilerOptions.UseRoslynLatest | CompilerOptions.UseDebug; AssemblerOptions asm = AssemblerOptions.None; if (force32Bit) { compiler |= CompilerOptions.Force32Bit; asm |= AssemblerOptions.Force32Bit; } await RunIL("StackTypes.il", compiler, asm); } [Test] public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task ConditionalAttr([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task TrickyTypes([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task Capturing([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions options) { if ((options & CompilerOptions.UseMcsMask) != 0) { Assert.Ignore("Decompiler bug with mono!"); } await RunCS(options: options); } [Test] public async Task Async([ValueSource(nameof(noMonoOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task LINQRaytracer([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task StringConcat([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task DynamicTests([ValueSource(nameof(noMonoOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task MiniJSON([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await RunCS(options: options); } [Test] public async Task ComInterop([ValueSource(nameof(net40OnlyOptions))] CompilerOptions options) { await RunCS(options: options); } async Task RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug) { if ((options & CompilerOptions.UseRoslynMask) != 0 && (options & CompilerOptions.TargetNet40) == 0) options |= CompilerOptions.UseTestRunner; string testFileName = testName + ".cs"; string testOutputFileName = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(options) + ".exe"); Helpers.CompilerResults outputFile = null, decompiledOutputFile = null; try { outputFile = await Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options, outputFileName: testOutputFileName).ConfigureAwait(false); string decompiledCodeFile = await Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options)).ConfigureAwait(false); if ((options & CompilerOptions.UseMcsMask) != 0) { // For second pass, use roslyn instead of mcs. // mcs has some compiler bugs that cause it to not accept ILSpy-generated code, // for example when there's unreachable code due to other compiler bugs in the first mcs run. options &= ~CompilerOptions.UseMcsMask; options |= CompilerOptions.UseRoslynLatest; // Also, add an .exe.config so that we consistently use the .NET 4.x runtime. File.WriteAllText(outputFile.PathToAssembly + ".config", @" "); options |= CompilerOptions.TargetNet40; } decompiledOutputFile = await Tester.CompileCSharp(decompiledCodeFile, options).ConfigureAwait(false); await Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile, (options & CompilerOptions.UseTestRunner) != 0, (options & CompilerOptions.Force32Bit) != 0); Tester.RepeatOnIOError(() => File.Delete(decompiledCodeFile)); } finally { if (outputFile != null) outputFile.DeleteTempFiles(); if (decompiledOutputFile != null) decompiledOutputFile.DeleteTempFiles(); } } async Task RunVB([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug) { options |= CompilerOptions.ReferenceVisualBasic; if ((options & CompilerOptions.UseRoslynMask) != 0) options |= CompilerOptions.UseTestRunner; string testFileName = testName + ".vb"; string testOutputFileName = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(options) + ".exe"); Helpers.CompilerResults outputFile = null, decompiledOutputFile = null; try { outputFile = await Tester.CompileVB(Path.Combine(TestCasePath, testFileName), options, outputFileName: testOutputFileName).ConfigureAwait(false); string decompiledCodeFile = await Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options)).ConfigureAwait(false); decompiledOutputFile = await Tester.CompileCSharp(decompiledCodeFile, options).ConfigureAwait(false); await Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile, (options & CompilerOptions.UseTestRunner) != 0, (options & CompilerOptions.Force32Bit) != 0); Tester.RepeatOnIOError(() => File.Delete(decompiledCodeFile)); } finally { if (outputFile != null) outputFile.DeleteTempFiles(); if (decompiledOutputFile != null) decompiledOutputFile.DeleteTempFiles(); } } async Task RunIL(string testFileName, CompilerOptions options = CompilerOptions.UseDebug, AssemblerOptions asmOptions = AssemblerOptions.None) { string outputFile = null; Helpers.CompilerResults decompiledOutputFile = null; bool optionsForce32Bit = options.HasFlag(CompilerOptions.Force32Bit); bool asmOptionsForce32Bit = asmOptions.HasFlag(AssemblerOptions.Force32Bit); Assert.That(asmOptionsForce32Bit, Is.EqualTo(optionsForce32Bit), "Inconsistent architecture."); try { options |= CompilerOptions.UseTestRunner; outputFile = await Tester.AssembleIL(Path.Combine(TestCasePath, testFileName), asmOptions).ConfigureAwait(false); string decompiledCodeFile = await Tester.DecompileCSharp(outputFile, Tester.GetSettings(options)).ConfigureAwait(false); decompiledOutputFile = await Tester.CompileCSharp(decompiledCodeFile, options).ConfigureAwait(false); await Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile, (options & CompilerOptions.UseTestRunner) != 0, (options & CompilerOptions.Force32Bit) != 0).ConfigureAwait(false); Tester.RepeatOnIOError(() => File.Delete(decompiledCodeFile)); } finally { if (decompiledOutputFile != null) decompiledOutputFile.DeleteTempFiles(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/DataFlowTest.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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; using System.Text; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture] class DataFlowTest { class RDTest : ReachingDefinitionsVisitor { ILVariable v; public RDTest(ILFunction f, ILVariable v) : base(f, _ => true, CancellationToken.None) { this.v = v; } protected internal override void VisitTryFinally(TryFinally inst) { Assert.That(IsPotentiallyUninitialized(state, v)); base.VisitTryFinally(inst); Assert.That(state.IsReachable); Assert.That(GetStores(state, v).Count(), Is.EqualTo(1)); Assert.That(!IsPotentiallyUninitialized(state, v)); } } [Test] public void TryFinallyWithAssignmentInFinally() { ILVariable v = new ILVariable(VariableKind.Local, SpecialType.UnknownType, 0); ILFunction f = new ILFunction( returnType: SpecialType.UnknownType, parameters: new IParameter[0], genericContext: new GenericContext(), body: new TryFinally( new Nop(), new StLoc(v, new LdcI4(0)) )); f.AddRef(); f.Variables.Add(v); f.Body.AcceptVisitor(new RDTest(f, v)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class DisassemblerPrettyTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/Disassembler/Pretty"; [Test] public void AllFilesHaveTests() { var testNames = typeof(DisassemblerPrettyTestRunner).GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase)) { var testName = file.Name.Split('.')[0]; Assert.That(testNames, Has.Member(testName)); } } } [Test] public async Task GenericConstraints() { await Run(); } [Test] public async Task SecurityDeclarations() { await Run(); } [Test] public async Task SortMembers() { await Run(ilExpectedFile: Path.Combine(TestCasePath, "SortMembers.expected.il"), asmOptions: AssemblerOptions.SortedOutput); } [Test] public async Task InterfaceImplAttributes() { await Run(); } async Task Run([CallerMemberName] string testName = null, string ilExpectedFile = null, AssemblerOptions asmOptions = AssemblerOptions.None) { var ilInputFile = Path.Combine(TestCasePath, testName + ".il"); ilExpectedFile ??= ilInputFile; var ilResultFile = Path.Combine(TestCasePath, testName + ".result.il"); var executable = await Tester.AssembleIL(ilInputFile, AssemblerOptions.Library).ConfigureAwait(false); var disassembled = await Tester.Disassemble(executable, ilResultFile, AssemblerOptions.UseOwnDisassembler | asmOptions).ConfigureAwait(false); CodeAssert.FilesAreEqual(ilExpectedFile, disassembled); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/CodeAssert.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using DiffLib; using DiffLib.Alignment; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Helpers { public static class CodeAssert { public static void FilesAreEqual(string fileName1, string fileName2, string[] definedSymbols = null) { AreEqual(File.ReadAllText(fileName1), File.ReadAllText(fileName2), definedSymbols); } public static void AreEqual(string input1, string input2, string[] definedSymbols = null) { var diff = new StringWriter(); if (!CodeComparer.Compare(input1, input2, diff, CodeComparer.NormalizeLine, definedSymbols)) { Assert.Fail(diff.ToString()); } } } public static class CodeComparer { public static bool Compare(string input1, string input2, StringWriter diff, Func normalizeLine, string[] definedSymbols = null) { var collection1 = NormalizeAndSplitCode(input1, definedSymbols ?? new string[0]); var collection2 = NormalizeAndSplitCode(input2, definedSymbols ?? new string[0]); var diffSections = DiffLib.Diff.CalculateSections( collection1, collection2, new CodeLineEqualityComparer(normalizeLine) ); var alignedDiff = Diff.AlignElements(collection1, collection2, diffSections, new StringSimilarityDiffElementAligner()); bool result = true; int line1 = 0, line2 = 0; const int contextSize = 10; int consecutiveMatches = contextSize; var hiddenMatches = new List(); foreach (var change in alignedDiff) { switch (change.Operation) { case DiffOperation.Match: AppendMatch($"{++line1,4} {++line2,4} ", change.ElementFromCollection1.Value); break; case DiffOperation.Insert: string pos = $" {++line2,4} "; if (ShouldIgnoreChange(change.ElementFromCollection2.Value)) { AppendMatch(pos, change.ElementFromCollection2.Value); } else { AppendDelta(pos, " + ", change.ElementFromCollection2.Value); result = false; } break; case DiffOperation.Delete: pos = $"{++line1,4} "; if (ShouldIgnoreChange(change.ElementFromCollection1.Value)) { AppendMatch(pos, change.ElementFromCollection1.Value); } else { AppendDelta(pos, " - ", change.ElementFromCollection1.Value); result = false; } break; case DiffOperation.Modify: case DiffOperation.Replace: AppendDelta($"{++line1,4} ", "(-)", change.ElementFromCollection1.Value); AppendDelta($" {++line2,4} ", "(+)", change.ElementFromCollection2.Value); result = false; break; } } if (hiddenMatches.Count > 0) { diff.WriteLine(" ..."); } return result; void AppendMatch(string pos, string code) { consecutiveMatches++; if (consecutiveMatches > contextSize) { // hide this match hiddenMatches.Add(pos + " " + code); } else { diff.WriteLine(pos + " " + code); } } void AppendDelta(string pos, string changeType, string code) { consecutiveMatches = 0; if (hiddenMatches.Count > contextSize) { diff.WriteLine(" ..."); } for (int i = Math.Max(0, hiddenMatches.Count - contextSize); i < hiddenMatches.Count; i++) { diff.WriteLine(hiddenMatches[i]); } hiddenMatches.Clear(); diff.WriteLine(pos + changeType + " " + code); } } class CodeLineEqualityComparer : IEqualityComparer { private IEqualityComparer baseComparer = EqualityComparer.Default; private Func normalizeLine; public CodeLineEqualityComparer(Func normalizeLine) { this.normalizeLine = normalizeLine; } public bool Equals(string x, string y) { return baseComparer.Equals( normalizeLine(x), normalizeLine(y) ); } public int GetHashCode(string obj) { return baseComparer.GetHashCode(normalizeLine(obj)); } } public static string NormalizeLine(string line) { line = line.Trim(); var index = line.IndexOf("//", StringComparison.Ordinal); if (index >= 0) { return line.Substring(0, index); } else if (line.StartsWith("#", StringComparison.Ordinal)) { return string.Empty; } else { return line; } } private static bool ShouldIgnoreChange(string line) { // for the result, we should ignore blank lines and added comments return NormalizeLine(line) == string.Empty; } class DeleteDisabledTextRewriter : CSharpSyntaxRewriter { public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia) { if (trivia.IsKind(SyntaxKind.DisabledTextTrivia)) { return default(SyntaxTrivia); // delete } if (trivia.IsKind(SyntaxKind.PragmaWarningDirectiveTrivia)) { return default(SyntaxTrivia); // delete } return base.VisitTrivia(trivia); } } private static IList NormalizeAndSplitCode(string input, IEnumerable definedSymbols) { var syntaxTree = CSharpSyntaxTree.ParseText(input, new CSharpParseOptions(preprocessorSymbols: definedSymbols)); var result = new DeleteDisabledTextRewriter().Visit(syntaxTree.GetRoot()); input = result.ToFullString(); return input.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs ================================================ using System.Collections.Generic; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Tests.Helpers { class RemoveCompilerAttribute : DepthFirstAstVisitor, IAstTransform { public override void VisitAttribute(CSharp.Syntax.Attribute attribute) { var section = (AttributeSection)attribute.Parent; SimpleType type = attribute.Type as SimpleType; if (section.AttributeTarget == "assembly" && (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework")) { attribute.Remove(); if (section.Attributes.Count == 0) section.Remove(); } if (section.AttributeTarget == "module" && type.Identifier is "UnverifiableCode" or "RefSafetyRules") { attribute.Remove(); if (section.Attributes.Count == 0) section.Remove(); } } public void Run(AstNode rootNode, TransformContext context) { rootNode.AcceptVisitor(this); } } public class RemoveNamespaceMy : DepthFirstAstVisitor, IAstTransform { public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { if (namespaceDeclaration.Name == "My") { namespaceDeclaration.Remove(); } else { base.VisitNamespaceDeclaration(namespaceDeclaration); } } public void Run(AstNode rootNode, TransformContext context) { rootNode.AcceptVisitor(this); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/RoslynToolset.cs ================================================ // Copyright (c) 2020 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.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; using NuGet.Common; using NuGet.Packaging; using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Versioning; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Helpers { [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Derived types are intended to be used as static singletons, each living until the process terminates.")] abstract class AbstractToolset { readonly SourceCacheContext cache; readonly SourceRepository repository; readonly FindPackageByIdResource resource; protected readonly string baseDir; public AbstractToolset(string baseDir) { this.cache = new SourceCacheContext(); this.repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); this.resource = repository.GetResource(); this.baseDir = baseDir; } protected async Task FetchPackage(string packageName, string version, string sourcePath, string outputPath) { if (!Directory.Exists(Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "nuget"))) Assert.Fail("No nuget cache found!"); ILogger logger = NullLogger.Instance; CancellationToken cancellationToken = CancellationToken.None; string pathToPackage = Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "nuget", $"{packageName}-{version}.nupkg"); Stream packageStream; if (File.Exists(pathToPackage)) { packageStream = File.OpenRead(pathToPackage); } else { packageStream = new MemoryStream(); await resource.CopyNupkgToStreamAsync( packageName, NuGetVersion.Parse(version), packageStream, cache, logger, cancellationToken).ConfigureAwait(false); packageStream.Position = 0; } using (packageStream) { using PackageArchiveReader packageReader = new PackageArchiveReader(packageStream); NuspecReader nuspecReader = await packageReader.GetNuspecReaderAsync(cancellationToken).ConfigureAwait(false); var files = (await packageReader.GetFilesAsync(cancellationToken).ConfigureAwait(false)).ToArray(); files = files.Where(f => f.StartsWith(sourcePath, StringComparison.OrdinalIgnoreCase)).ToArray(); await packageReader.CopyFilesAsync(outputPath, files, (sourceFile, targetPath, fileStream) => { fileStream.CopyToFile(targetPath); return targetPath; }, logger, cancellationToken).ConfigureAwait(false); } } } class RoslynToolset : AbstractToolset { readonly Dictionary installedCompilers = new Dictionary { { "legacy", Environment.ExpandEnvironmentVariables(@"%WINDIR%\Microsoft.NET\Framework\v4.0.30319") } }; public RoslynToolset() : base(Path.Combine(AppContext.BaseDirectory, "roslyn")) { } public async Task Fetch(string version, string packageName = "Microsoft.Net.Compilers.Toolset", string sourcePath = "tasks/net472") { string path = Path.Combine(baseDir, version, sourcePath); if (!Directory.Exists(path)) { await FetchPackage(packageName, version, sourcePath, Path.Combine(baseDir, version)).ConfigureAwait(false); } installedCompilers.Add(SanitizeVersion(version), path); } public string GetCSharpCompiler(string version) { return GetCompiler("csc.exe", version); } public string GetVBCompiler(string version) { return GetCompiler("vbc.exe", version); } string GetCompiler(string compiler, string version) { if (installedCompilers.TryGetValue(SanitizeVersion(version), out var path)) return Path.Combine(path, compiler); throw new NotSupportedException($"Cannot find {compiler} {version}, please add it to the initialization."); } internal static string SanitizeVersion(string version) { int index = version.IndexOf("-"); if (index > 0) return version.Remove(index); return version; } } class VsWhereToolset : AbstractToolset { string vswherePath; public VsWhereToolset() : base(Path.Combine(AppContext.BaseDirectory, "vswhere")) { } public async Task Fetch() { string path = Path.Combine(baseDir, "tools"); if (!Directory.Exists(path)) { await FetchPackage("vswhere", "2.8.4", "tools", baseDir).ConfigureAwait(false); } vswherePath = Path.Combine(path, "vswhere.exe"); } public string GetVsWhere() => vswherePath; } class RefAssembliesToolset : AbstractToolset { readonly Dictionary installedFrameworks = new Dictionary { { "legacy", Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "dotnet", "legacy") }, { "2.2.0", Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "dotnet", "netcore-2.2") }, }; public RefAssembliesToolset() : base(Path.Combine(AppContext.BaseDirectory, "netfx")) { } public async Task Fetch(string version, string packageName = "Microsoft.NETCore.App.Ref", string sourcePath = "ref/net5.0") { string path = Path.Combine(baseDir, version, sourcePath); if (!Directory.Exists(path)) { await FetchPackage(packageName, version, sourcePath, Path.Combine(baseDir, version)).ConfigureAwait(false); } installedFrameworks.Add(RoslynToolset.SanitizeVersion(version), path); } internal string GetPath(string targetFramework) { var (id, version) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework); string path; if (id == TargetFrameworkIdentifier.NETFramework) { path = installedFrameworks["legacy"]; } else { path = installedFrameworks[version.ToString(3)]; } Debug.Assert(Path.Exists(path)); return path; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/SdkUtility.cs ================================================ // Copyright (c) 2014 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 // 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.IO; using Microsoft.Win32; namespace ICSharpCode.Decompiler.Tests.Helpers { public static class SdkUtility { static string GetPathFromRegistry(string key, string valueName) { using (RegistryKey installRootKey = Registry.LocalMachine.OpenSubKey(key)) { if (installRootKey != null) { object o = installRootKey.GetValue(valueName); if (o != null) { string r = o.ToString(); if (!string.IsNullOrEmpty(r)) return r; } } } return null; } static string GetPathFromRegistryX86(string key, string valueName) { using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) { using (RegistryKey installRootKey = baseKey.OpenSubKey(key)) { if (installRootKey != null) { object o = installRootKey.GetValue(valueName); if (o != null) { string r = o.ToString(); if (!string.IsNullOrEmpty(r)) return r; } } } } return null; } #region InstallRoot Properties static string netFrameworkInstallRoot = null; /// /// Gets the installation root of the .NET Framework (@"C:\Windows\Microsoft.NET\Framework\") /// public static string NetFrameworkInstallRoot { get { if (netFrameworkInstallRoot == null) { netFrameworkInstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "InstallRoot") ?? string.Empty; } return netFrameworkInstallRoot; } } static string netSdk20InstallRoot = null; /// /// Location of the .NET 2.0 SDK install root. /// public static string NetSdk20InstallRoot { get { if (netSdk20InstallRoot == null) { netSdk20InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\.NETFramework", "sdkInstallRootv2.0") ?? string.Empty; } return netSdk20InstallRoot; } } static string windowsSdk60InstallRoot = null; /// /// Location of the .NET 3.0 SDK (Windows SDK 6.0) install root. /// public static string WindowsSdk60InstallRoot { get { if (windowsSdk60InstallRoot == null) { windowsSdk60InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0", "InstallationFolder") ?? string.Empty; } return windowsSdk60InstallRoot; } } static string windowsSdk60aInstallRoot = null; /// /// Location of the Windows SDK Components in Visual Studio 2008 (.NET 3.5; Windows SDK 6.0a). /// public static string WindowsSdk60aInstallRoot { get { if (windowsSdk60aInstallRoot == null) { windowsSdk60aInstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0a", "InstallationFolder") ?? string.Empty; } return windowsSdk60aInstallRoot; } } static string windowsSdk61InstallRoot = null; /// /// Location of the .NET 3.5 SDK (Windows SDK 6.1) install root. /// public static string WindowsSdk61InstallRoot { get { if (windowsSdk61InstallRoot == null) { windowsSdk61InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1", "InstallationFolder") ?? string.Empty; } return windowsSdk61InstallRoot; } } static string windowsSdk70InstallRoot = null; /// /// Location of the .NET 3.5 SP1 SDK (Windows SDK 7.0) install root. /// public static string WindowsSdk70InstallRoot { get { if (windowsSdk70InstallRoot == null) { windowsSdk70InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0", "InstallationFolder") ?? string.Empty; } return windowsSdk70InstallRoot; } } static string windowsSdk71InstallRoot = null; /// /// Location of the .NET 4.0 SDK (Windows SDK 7.1) install root. /// public static string WindowsSdk71InstallRoot { get { if (windowsSdk71InstallRoot == null) { windowsSdk71InstallRoot = GetPathFromRegistry(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1", "InstallationFolder") ?? string.Empty; } return windowsSdk71InstallRoot; } } static string windowsSdk80InstallRoot = null; /// /// Location of the .NET 4.5 SDK (Windows SDK 8.0) install root. /// public static string WindowsSdk80NetFxTools { get { if (windowsSdk80InstallRoot == null) { windowsSdk80InstallRoot = GetPathFromRegistryX86(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A\WinSDK-NetFx40Tools", "InstallationFolder") ?? string.Empty; } return windowsSdk80InstallRoot; } } static string WindowsSdk461InstallRoot = null; /// /// Location of the .NET 4.6.1 SDK install root. /// public static string WindowsSdk461NetFxTools { get { if (WindowsSdk461InstallRoot == null) { WindowsSdk461InstallRoot = GetPathFromRegistryX86(@"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.1\WinSDK-NetFx40Tools", "InstallationFolder") ?? string.Empty; } return WindowsSdk461InstallRoot; } } #endregion /// /// Searches all the .net sdk bin folders and return the path of the /// exe from the latest sdk. /// /// The EXE to search for. /// The path of the executable, or null if the exe is not found. public static string GetSdkPath(string exeName) { string execPath; if (!string.IsNullOrEmpty(WindowsSdk461NetFxTools)) { execPath = Path.Combine(WindowsSdk461NetFxTools, exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk80NetFxTools)) { execPath = Path.Combine(WindowsSdk80NetFxTools, exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk71InstallRoot)) { execPath = Path.Combine(WindowsSdk71InstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk70InstallRoot)) { execPath = Path.Combine(WindowsSdk70InstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk61InstallRoot)) { execPath = Path.Combine(WindowsSdk61InstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk60aInstallRoot)) { execPath = Path.Combine(WindowsSdk60aInstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(WindowsSdk60InstallRoot)) { execPath = Path.Combine(WindowsSdk60InstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } if (!string.IsNullOrEmpty(NetSdk20InstallRoot)) { execPath = Path.Combine(NetSdk20InstallRoot, "bin\\" + exeName); if (File.Exists(execPath)) { return execPath; } } return null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs ================================================ // Copyright (c) 2015 Daniel Grunwald // // 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.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using CliWrap; using CliWrap.Buffered; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Helpers { partial class Tester { public static async Task CompileVB(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug, string outputFileName = null) { List sourceFileNames = new List { sourceFileName }; foreach (Match match in Regex.Matches(File.ReadAllText(sourceFileName), @"#include ""([\w\d./]+)""")) { sourceFileNames.Add(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(sourceFileName), match.Groups[1].Value))); } var preprocessorSymbols = GetPreprocessorSymbols(flags).Select(symbol => new KeyValuePair(symbol, 1)).ToList(); if ((flags & CompilerOptions.UseMcsMask) == 0) { CompilerResults results = new CompilerResults(); results.PathToAssembly = outputFileName; bool targetNet40 = (flags & CompilerOptions.TargetNet40) != 0; var (roslynVersion, languageVersion, targetFramework) = (flags & CompilerOptions.UseRoslynMask) switch { 0 => ("legacy", "11", null), CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "14", null), CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v2.2"), CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v5.0"), _ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest", targetNet40 ? null : ".NETCoreApp,Version=v10.0") }; var vbcPath = roslynToolset.GetVBCompiler(roslynVersion); IEnumerable references; string libPath; if ((flags & CompilerOptions.UseRoslynMask) != 0 && targetFramework != null) { var coreRefAsmPath = RefAssembliesToolset.GetPath(targetFramework); references = coreDefaultReferences.Select(r => "-r:\"" + r + "\""); libPath = coreRefAsmPath; } else { references = defaultReferences.Select(r => "-r:\"" + r + "\""); libPath = RefAssembliesToolset.GetPath("legacy"); } if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { references = references.Concat(new[] { "-r:\"Microsoft.VisualBasic.dll\"" }); } string otherOptions = $"-nologo -noconfig " + "-optioninfer+ -optionexplicit+ " + $"-langversion:{languageVersion} " + $"/optimize{(flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- ")}"; // note: the /shared switch is undocumented. It allows us to use the VBCSCompiler.exe compiler // server to speed up testing if (roslynVersion != "legacy") { otherOptions += "/shared "; } if (flags.HasFlag(CompilerOptions.Library)) { otherOptions += "-t:library "; } else { otherOptions += "-t:exe "; } if (flags.HasFlag(CompilerOptions.GeneratePdb)) { otherOptions += "-debug:full "; } else { otherOptions += "-debug- "; } if (flags.HasFlag(CompilerOptions.Force32Bit)) { otherOptions += "-platform:x86 "; } else { otherOptions += "-platform:anycpu "; } if (preprocessorSymbols.Count > 0) { otherOptions += " \"-d:" + string.Join(",", preprocessorSymbols.Select(kv => kv.Key + "=" + kv.Value)) + "\" "; } var command = Cli.Wrap(vbcPath) .WithArguments($"{otherOptions}-libpath:\"{libPath}\" {string.Join(" ", references)} -out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}") .WithValidation(CommandResultValidation.None); //Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } Assert.That(result.ExitCode, Is.EqualTo(0), "vbc failed"); return results; } else { throw new NotSupportedException("Cannot use mcs for VB"); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/Tester.cs ================================================ // Copyright (c) 2015 Daniel Grunwald // // 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.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.XPath; using CliWrap; using CliWrap.Buffered; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.PdbProvider; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Helpers { [Flags] public enum CompilerOptions { None, Optimize = 0x1, UseDebug = 0x2, Force32Bit = 0x4, Library = 0x8, UseRoslyn1_3_2 = 0x10, UseMcs2_6_4 = 0x20, ReferenceVisualBasic = 0x40, TargetNet40 = 0x80, GeneratePdb = 0x100, Preview = 0x200, UseRoslyn2_10_0 = 0x400, UseRoslyn3_11_0 = 0x800, UseRoslynLatest = 0x1000, UseMcs5_23 = 0x2000, UseTestRunner = 0x4000, NullableEnable = 0x8000, ReferenceUnsafe = 0x10000, CheckForOverflowUnderflow = 0x20000, ProcessXmlDoc = 0x40000, UseMcsMask = UseMcs2_6_4 | UseMcs5_23, UseRoslynMask = UseRoslyn1_3_2 | UseRoslyn2_10_0 | UseRoslyn3_11_0 | UseRoslynLatest } [Flags] public enum AssemblerOptions { None, UseDebug = 0x1, Force32Bit = 0x2, Library = 0x4, /// Testing our own disassembler, or working around a bug in ildasm. UseOwnDisassembler = 0x8, /// Work around bug in .NET 5 ilasm (https://github.com/dotnet/runtime/issues/32400) UseLegacyAssembler = 0x10, /// UseSortByNameFilter, implies UseOwnDisassembler SortedOutput = 0x20, } public static partial class Tester { public static readonly string TesterPath; public static readonly string TestCasePath; static readonly string testRunnerBasePath; static readonly string packagesPropsFile; static readonly string roslynLatestVersion; static readonly RoslynToolset roslynToolset; static readonly VsWhereToolset vswhereToolset; internal static readonly RefAssembliesToolset RefAssembliesToolset; static Tester() { TesterPath = Path.GetDirectoryName(typeof(Tester).Assembly.Location); TestCasePath = Path.Combine(TesterPath, "../../../../TestCases"); #if DEBUG testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Debug/net10.0"); #else testRunnerBasePath = Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/bin/Release/net10.0"); #endif // To parse: packagesPropsFile = Path.Combine(TesterPath, "../../../../../Directory.Packages.props"); roslynLatestVersion = ((IEnumerable)(XDocument .Load(packagesPropsFile) .XPathEvaluate("//Project//ItemGroup//PackageVersion[@Include='Microsoft.CodeAnalysis.CSharp']/@Version"))) .OfType() .Single() .Value; roslynToolset = new RoslynToolset(); vswhereToolset = new VsWhereToolset(); RefAssembliesToolset = new RefAssembliesToolset(); } internal static async Task Initialize() { await roslynToolset.Fetch("1.3.2", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false); await roslynToolset.Fetch("2.10.0", "Microsoft.Net.Compilers", "tools").ConfigureAwait(false); await roslynToolset.Fetch("3.11.0").ConfigureAwait(false); await roslynToolset.Fetch(roslynLatestVersion).ConfigureAwait(false); await vswhereToolset.Fetch().ConfigureAwait(false); await RefAssembliesToolset.Fetch("5.0.0", sourcePath: "ref/net5.0").ConfigureAwait(false); await RefAssembliesToolset.Fetch("10.0.0-rc.2.25502.107", sourcePath: "ref/net10.0").ConfigureAwait(false); #if DEBUG await BuildTestRunner("win-x86", "Debug").ConfigureAwait(false); await BuildTestRunner("win-x64", "Debug").ConfigureAwait(false); #else await BuildTestRunner("win-x86", "Release").ConfigureAwait(false); await BuildTestRunner("win-x64", "Release").ConfigureAwait(false); #endif } static async Task BuildTestRunner(string runtime, string config) { await Cli.Wrap("dotnet.exe") .WithArguments(new[] { "build", Path.Combine(TesterPath, "../../../../../ICSharpCode.Decompiler.TestRunner/ICSharpCode.Decompiler.TestRunner.csproj"), "-r", runtime, "-c", config, "--self-contained" }) .ExecuteAsync(); } public static async Task AssembleIL(string sourceFileName, AssemblerOptions options = AssemblerOptions.UseDebug) { string ilasmPath; if (options.HasFlag(AssemblerOptions.UseLegacyAssembler)) { ilasmPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), @"Microsoft.NET\Framework\v4.0.30319\ilasm.exe"); } else { ilasmPath = Path.Combine( Path.GetDirectoryName(typeof(Tester).Assembly.Location), "ilasm.exe"); } string outputFile = Path.Combine(Path.GetDirectoryName(sourceFileName), Path.GetFileNameWithoutExtension(sourceFileName)); string otherOptions = " "; if (options.HasFlag(AssemblerOptions.Force32Bit)) { outputFile += ".32"; otherOptions += "/32BitPreferred "; } if (options.HasFlag(AssemblerOptions.Library)) { outputFile += ".dll"; otherOptions += "/dll "; } else { outputFile += ".exe"; otherOptions += "/exe "; } if (options.HasFlag(AssemblerOptions.UseDebug)) { otherOptions += "/debug "; } var command = Cli.Wrap(ilasmPath) .WithArguments($"/quiet {otherOptions}/output=\"{outputFile}\" \"{sourceFileName}\"") .WithValidation(CommandResultValidation.None); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } Assert.That(result.ExitCode, Is.EqualTo(0), "ilasm failed"); return outputFile; } public static async Task Disassemble(string sourceFileName, string outputFile, AssemblerOptions asmOptions) { if (asmOptions.HasFlag(AssemblerOptions.UseOwnDisassembler) || asmOptions.HasFlag(AssemblerOptions.SortedOutput)) { using (var peFileStream = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read)) using (var peFile = new PEFile(sourceFileName, peFileStream)) using (var writer = new StringWriter()) { var metadata = peFile.Metadata; var output = new PlainTextOutput(writer); ReflectionDisassembler rd = new ReflectionDisassembler(output, CancellationToken.None); if (asmOptions.HasFlag(AssemblerOptions.SortedOutput)) { rd.EntityProcessor = new SortByNameProcessor(); } rd.AssemblyResolver = new UniversalAssemblyResolver(sourceFileName, throwOnError: true, null); rd.DetectControlStructure = false; rd.WriteAssemblyReferences(metadata); if (metadata.IsAssembly) rd.WriteAssemblyHeader(peFile); output.WriteLine(); rd.WriteModuleHeader(peFile, skipMVID: true); output.WriteLine(); rd.WriteModuleContents(peFile); File.WriteAllText(outputFile, ReplacePrivImplDetails(writer.ToString())); } return outputFile; } string ildasmPath = Path.Combine( Path.GetDirectoryName(typeof(Tester).Assembly.Location), "ildasm.exe"); var command = Cli.Wrap(ildasmPath) .WithArguments($"/utf8 /out=\"{outputFile}\" \"{sourceFileName}\"") .WithValidation(CommandResultValidation.None); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } Assert.That(result.ExitCode, Is.EqualTo(0), "ildasm failed"); // Unlike the .imagebase directive (which is a fixed value when compiling with /deterministic), // the image base comment still varies... ildasm putting a random number here? string il = File.ReadAllText(outputFile); il = Regex.Replace(il, @"^// Image base: 0x[0-9A-F]+\r?\n", "", RegexOptions.Multiline); // and while we're at it, also remove the MVID il = Regex.Replace(il, @"^// MVID: \{[0-9A-F-]+\}\r?\n", "", RegexOptions.Multiline); // and the ildasm version info (varies from system to system) il = Regex.Replace(il, @"^// +Microsoft .* Disassembler\. +Version.*\r?\n", "", RegexOptions.Multiline); // copyright header "All rights reserved" is dependent on system language il = Regex.Replace(il, @"^// +Copyright .* Microsoft.*\r?\n", "", RegexOptions.Multiline); // filename may contain full path il = Regex.Replace(il, @"^// WARNING: Created Win32 resource file.*\r?\n", "", RegexOptions.Multiline); il = ReplacePrivImplDetails(il); File.WriteAllText(outputFile, il); return outputFile; } private static string ReplacePrivImplDetails(string il) { return Regex.Replace(il, @"'\{[0-9A-F-]+\}'", "''"); } static readonly string[] defaultReferences = new[] { "System.dll", "System.Core.dll", "System.Runtime.dll", "System.Xml.dll", "Microsoft.CSharp.dll" }; static readonly string[] core220DefaultReferences = new[] { "netstandard.dll", "mscorlib.dll", "System.dll", "System.Collections.dll", "System.Console.dll", "System.Core.dll", "System.Linq.dll", "System.Linq.Expressions.dll", "System.Linq.Queryable.dll", "System.IO.FileSystem.Watcher.dll", "System.Memory.dll", "System.Private.CoreLib.dll", "System.Private.Xml.dll", "System.Threading.dll", "System.Threading.Thread.dll", "System.Runtime.dll", "System.Runtime.Extensions.dll", "System.Runtime.InteropServices.dll", "System.Xml.dll", "System.Xml.ReaderWriter.dll", "System.ValueTuple.dll", "Microsoft.CSharp.dll", "Microsoft.VisualBasic.dll", }; static readonly string[] coreDefaultReferences = new[] { "netstandard.dll", "mscorlib.dll", "System.dll", "System.Collections.dll", "System.Console.dll", "System.Core.dll", "System.Linq.dll", "System.Linq.Expressions.dll", "System.Linq.Queryable.dll", "System.IO.FileSystem.Watcher.dll", "System.Memory.dll", "System.Threading.dll", "System.Threading.Thread.dll", "System.Runtime.dll", "System.Runtime.InteropServices.dll", "System.Xml.dll", "System.Xml.ReaderWriter.dll", "System.ValueTuple.dll", "Microsoft.CSharp.dll", "Microsoft.VisualBasic.dll", }; static readonly Dictionary> targetFrameworkAttributeSnippetFiles = new() { { ".NETCoreApp,Version=v10.0", new Lazy(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v10.0")) }, { ".NETCoreApp,Version=v5.0", new Lazy(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v5.0")) }, { ".NETCoreApp,Version=v2.2", new Lazy(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v2.2")) }, }; static string GetTargetFrameworkAttributeSnippetFile(string targetFrameworkMoniker) { // Note: this leaks a temporary file, we're not attempting to delete it, because it is only one. var tempFile = Path.GetTempFileName(); File.WriteAllText(tempFile, $@" [assembly: System.Runtime.Versioning.TargetFramework(""{targetFrameworkMoniker}"")] "); return tempFile; } const string nonEmbeddedAttributesSnippet = @" using System; #if !NET60 namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public CompilerFeatureRequiredAttribute(string featureName) { } } internal class IsExternalInit { } #endif #if !NET70 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute { } #endif #if !NET60 } #endif "; static readonly Lazy nonEmbeddedAttributesSnippetFile = new Lazy(GetNonEmbeddedAttributesSnippetFile); static string GetNonEmbeddedAttributesSnippetFile() { // Note: this leaks a temporary file, we're not attempting to delete it, because it is only one. var tempFile = Path.GetTempFileName(); File.WriteAllText(tempFile, nonEmbeddedAttributesSnippet); return tempFile; } public static List GetPreprocessorSymbols(CompilerOptions flags) { var preprocessorSymbols = new List(); if (flags.HasFlag(CompilerOptions.UseDebug)) { preprocessorSymbols.Add("DEBUG"); } if (flags.HasFlag(CompilerOptions.Optimize)) { preprocessorSymbols.Add("OPT"); } if (flags.HasFlag(CompilerOptions.TargetNet40)) { preprocessorSymbols.Add("NET40"); } if ((flags & CompilerOptions.UseRoslynMask) != 0) { if (!flags.HasFlag(CompilerOptions.TargetNet40)) { preprocessorSymbols.Add("NETCORE"); } preprocessorSymbols.Add("ROSLYN"); preprocessorSymbols.Add("CS60"); preprocessorSymbols.Add("VB11"); preprocessorSymbols.Add("VB14"); if (flags.HasFlag(CompilerOptions.UseRoslyn2_10_0) || flags.HasFlag(CompilerOptions.UseRoslyn3_11_0) || flags.HasFlag(CompilerOptions.UseRoslynLatest)) { preprocessorSymbols.Add("ROSLYN2"); preprocessorSymbols.Add("CS70"); preprocessorSymbols.Add("CS71"); preprocessorSymbols.Add("CS72"); preprocessorSymbols.Add("CS73"); preprocessorSymbols.Add("VB15"); } if (flags.HasFlag(CompilerOptions.UseRoslyn3_11_0) || flags.HasFlag(CompilerOptions.UseRoslynLatest)) { if (!flags.HasFlag(CompilerOptions.TargetNet40)) { preprocessorSymbols.Add("NET50"); } preprocessorSymbols.Add("ROSLYN3"); preprocessorSymbols.Add("CS80"); preprocessorSymbols.Add("CS90"); preprocessorSymbols.Add("VB16"); } if (flags.HasFlag(CompilerOptions.UseRoslynLatest)) { if (!flags.HasFlag(CompilerOptions.TargetNet40)) { preprocessorSymbols.Add("NET60"); preprocessorSymbols.Add("NET70"); preprocessorSymbols.Add("NET80"); preprocessorSymbols.Add("NET90"); preprocessorSymbols.Add("NET100"); } preprocessorSymbols.Add("ROSLYN4"); preprocessorSymbols.Add("CS100"); preprocessorSymbols.Add("CS110"); preprocessorSymbols.Add("CS120"); preprocessorSymbols.Add("CS130"); } } else if ((flags & CompilerOptions.UseMcsMask) != 0) { preprocessorSymbols.Add("MCS"); if (flags.HasFlag(CompilerOptions.UseMcs2_6_4)) { preprocessorSymbols.Add("MCS2"); } if (flags.HasFlag(CompilerOptions.UseMcs5_23)) { preprocessorSymbols.Add("MCS5"); } } else { preprocessorSymbols.Add("LEGACY_CSC"); preprocessorSymbols.Add("LEGACY_VBC"); } return preprocessorSymbols; } public static async Task CompileCSharp(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug, string outputFileName = null) { List sourceFileNames = new List { sourceFileName }; foreach (Match match in Regex.Matches(File.ReadAllText(sourceFileName), @"#include ""([\w\d./]+)""")) { sourceFileNames.Add(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(sourceFileName), match.Groups[1].Value))); } bool targetNet40 = (flags & CompilerOptions.TargetNet40) != 0; bool useRoslyn = (flags & CompilerOptions.UseRoslynMask) != 0; if (targetNet40) { sourceFileNames.Add(nonEmbeddedAttributesSnippetFile.Value); } var preprocessorSymbols = GetPreprocessorSymbols(flags); if ((flags & CompilerOptions.UseMcsMask) == 0) { CompilerResults results = new CompilerResults(); results.PathToAssembly = outputFileName; var (roslynVersion, languageVersion, targetFramework) = (flags & CompilerOptions.UseRoslynMask) switch { 0 => ("legacy", "5", null), CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "6", null), CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v2.2"), CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v5.0"), _ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest", targetNet40 ? null : ".NETCoreApp,Version=v10.0") }; var cscPath = roslynToolset.GetCSharpCompiler(roslynVersion); string libPath, refAsmPath; IEnumerable references; if (useRoslyn && targetFramework != null) { refAsmPath = RefAssembliesToolset.GetPath(targetFramework); if (targetFramework == ".NETCoreApp,Version=v2.2") { references = core220DefaultReferences; if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { references = references.Append("Microsoft.VisualBasic.dll"); } } else { references = coreDefaultReferences; if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { references = references.Append("Microsoft.VisualBasic.dll"); references = references.Append("Microsoft.VisualBasic.Core.dll"); } } libPath = "\"" + refAsmPath + "\""; sourceFileNames.Add(targetFrameworkAttributeSnippetFiles[targetFramework].Value); } else { refAsmPath = RefAssembliesToolset.GetPath("legacy"); libPath = "\"" + refAsmPath + "\""; references = defaultReferences; if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { references = references.Append("Microsoft.VisualBasic.dll"); } } if (useRoslyn && !targetNet40 && flags.HasFlag(CompilerOptions.ReferenceUnsafe)) { references = references.Append("System.Runtime.CompilerServices.Unsafe.dll"); } references = references.Select(r => "-r:\"" + Path.Combine(refAsmPath, r) + "\""); string otherOptions = $"-nologo -noconfig " + $"-langversion:{languageVersion} " + $"-unsafe -o{(flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- ")}"; HashSet noWarn = new HashSet(StringComparer.OrdinalIgnoreCase); // note: the /shared switch is undocumented. It allows us to use the VBCSCompiler.exe compiler // server to speed up testing if (roslynVersion != "legacy") { otherOptions += "/shared "; if (!targetNet40 && Version.Parse(RoslynToolset.SanitizeVersion(roslynVersion)).Major > 2) { if (flags.HasFlag(CompilerOptions.NullableEnable)) otherOptions += "/nullable+ "; else otherOptions += "/nullable- "; } } if (flags.HasFlag(CompilerOptions.Library)) { otherOptions += "-t:library "; } else { otherOptions += "-t:exe "; } if (flags.HasFlag(CompilerOptions.GeneratePdb)) { otherOptions += "-debug:full "; } else { otherOptions += "-debug- "; } if (flags.HasFlag(CompilerOptions.Force32Bit)) { otherOptions += "-platform:x86 "; } else { otherOptions += "-platform:anycpu "; } if (flags.HasFlag(CompilerOptions.ProcessXmlDoc)) { otherOptions += $"-doc:\"{Path.ChangeExtension(results.PathToAssembly, ".xml")}\" "; noWarn.Add("CS1591"); // Missing XML comment for publicly visible type or member 'Type_or_Member' } if (flags.HasFlag(CompilerOptions.CheckForOverflowUnderflow)) { otherOptions += "-checked+ "; } else { otherOptions += "-checked- "; } if (preprocessorSymbols.Count > 0) { otherOptions += " \"-d:" + string.Join(";", preprocessorSymbols) + "\" "; } if (noWarn.Count > 0) { otherOptions += " -nowarn:" + string.Join(",", noWarn); } var command = Cli.Wrap(cscPath) .WithArguments($"{otherOptions} -lib:{libPath} {string.Join(" ", references)} -out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}") .WithValidation(CommandResultValidation.None); //Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } Assert.That(result.ExitCode, Is.EqualTo(0), "csc failed"); return results; } else { CompilerResults results = new CompilerResults(); results.PathToAssembly = outputFileName; string testBasePath = Roundtrip.RoundtripAssembly.TestDir; if (!Directory.Exists(testBasePath)) { Assert.Ignore($"Compilation with mcs ignored: test directory '{testBasePath}' needs to be checked out separately." + Environment.NewLine + $"git clone https://github.com/icsharpcode/ILSpy-tests \"{testBasePath}\""); } string mcsPath = (flags & CompilerOptions.UseMcsMask) switch { CompilerOptions.UseMcs5_23 => Path.Combine(testBasePath, @"mcs\5.23\bin\mcs.bat"), _ => Path.Combine(testBasePath, @"mcs\2.6.4\bin\gmcs.bat") }; string otherOptions = " -unsafe -o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+ " : "- "); if (flags.HasFlag(CompilerOptions.Library)) { otherOptions += "-t:library "; } else { otherOptions += "-t:exe "; } if (flags.HasFlag(CompilerOptions.UseDebug)) { otherOptions += "-g "; } if (flags.HasFlag(CompilerOptions.Force32Bit)) { otherOptions += "-platform:x86 "; } else { otherOptions += "-platform:anycpu "; } if (preprocessorSymbols.Count > 0) { otherOptions += " \"-d:" + string.Join(";", preprocessorSymbols) + "\" "; } var command = Cli.Wrap(mcsPath) .WithArguments($"{otherOptions}-out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}") .WithValidation(CommandResultValidation.None); //Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } Assert.That(result.ExitCode, Is.EqualTo(0), "mcs failed"); return results; } } internal static DecompilerSettings GetSettings(CompilerOptions cscOptions) { if ((cscOptions & CompilerOptions.UseRoslynMask) != 0) { var langVersion = (cscOptions & CompilerOptions.UseRoslynMask) switch { CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6, CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3, CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0, _ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp14_0, }; DecompilerSettings settings = new(langVersion) { // Never use file-scoped namespaces FileScopedNamespaces = false }; return settings; } else { var settings = new DecompilerSettings(CSharp.LanguageVersion.CSharp5); if ((cscOptions & CompilerOptions.UseMcsMask) != 0) { // we don't recompile with mcs but with roslyn, so we can use ref locals settings.UseRefLocalsForAccurateOrderOfEvaluation = true; } return settings; } } public static void CompileCSharpWithPdb(string assemblyName, Dictionary sourceFiles) { var parseOptions = new CSharpParseOptions(languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest); List embeddedTexts = new List(); List syntaxTrees = new List(); foreach (KeyValuePair file in sourceFiles) { var sourceText = SourceText.From(file.Value, new UTF8Encoding(false), SourceHashAlgorithm.Sha256); syntaxTrees.Add(SyntaxFactory.ParseSyntaxTree(sourceText, parseOptions, file.Key)); embeddedTexts.Add(EmbeddedText.FromSource(file.Key, sourceText)); } var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(assemblyName), syntaxTrees, coreDefaultReferences.Select(r => MetadataReference.CreateFromFile(Path.Combine(RefAssembliesToolset.GetPath(".NETCoreApp,Version=v10.0"), r))), new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, platform: Platform.AnyCpu, optimizationLevel: OptimizationLevel.Release, allowUnsafe: true, deterministic: true )); using (FileStream peStream = File.Open(assemblyName + ".dll", FileMode.OpenOrCreate, FileAccess.ReadWrite)) using (FileStream pdbStream = File.Open(assemblyName + ".pdb", FileMode.OpenOrCreate, FileAccess.ReadWrite)) { var emitResult = compilation.Emit(peStream, pdbStream, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: assemblyName + ".pdb"), embeddedTexts: embeddedTexts); if (!emitResult.Success) { StringBuilder b = new StringBuilder("Compiler error:"); foreach (var diag in emitResult.Diagnostics) { b.AppendLine(diag.ToString()); } throw new Exception(b.ToString()); } } } internal static string GetSuffix(CompilerOptions cscOptions) { string suffix = ""; if ((cscOptions & CompilerOptions.Optimize) != 0) suffix += ".opt"; if ((cscOptions & CompilerOptions.Force32Bit) != 0) suffix += ".32"; if ((cscOptions & CompilerOptions.UseDebug) != 0) suffix += ".dbg"; if ((cscOptions & CompilerOptions.TargetNet40) != 0) suffix += ".net40"; if ((cscOptions & CompilerOptions.UseRoslyn1_3_2) != 0) suffix += ".roslyn1"; if ((cscOptions & CompilerOptions.UseRoslyn2_10_0) != 0) suffix += ".roslyn2"; if ((cscOptions & CompilerOptions.UseRoslyn3_11_0) != 0) suffix += ".roslyn3"; if ((cscOptions & CompilerOptions.UseRoslynLatest) != 0) suffix += ".roslyn"; if ((cscOptions & CompilerOptions.UseMcs2_6_4) != 0) suffix += ".mcs2"; if ((cscOptions & CompilerOptions.UseMcs5_23) != 0) suffix += ".mcs5"; return suffix; } public static async Task<(int ExitCode, string Output, string Error)> Run(string assemblyFileName) { var command = Cli.Wrap(assemblyFileName) .WithValidation(CommandResultValidation.None); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); return (result.ExitCode, result.StandardOutput, result.StandardError); } public static async Task<(int ExitCode, string Output, string Error)> RunWithTestRunner(string assemblyFileName, bool force32Bit) { string testRunner = Path.Combine(testRunnerBasePath, force32Bit ? "win-x86" : "win-x64", "ICSharpCode.Decompiler.TestRunner.exe"); var command = Cli.Wrap(testRunner) .WithArguments(assemblyFileName) .WithValidation(CommandResultValidation.None); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); return (result.ExitCode, result.StandardOutput, result.StandardError); } public static Task DecompileCSharp(string assemblyFileName, DecompilerSettings settings = null) { if (settings == null) settings = new DecompilerSettings(); using (var file = new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read)) { var module = new PEFile(assemblyFileName, file, PEStreamOptions.PrefetchEntireImage); string targetFramework = module.Metadata.DetectTargetFrameworkId(); var resolver = new UniversalAssemblyResolver(assemblyFileName, false, targetFramework, null, PEStreamOptions.PrefetchMetadata); resolver.AddSearchDirectory(RefAssembliesToolset.GetPath(targetFramework)); var typeSystem = new DecompilerTypeSystem(module, resolver, settings); CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings); decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAttributes()); decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute()); decompiler.AstTransforms.Insert(0, new RemoveNamespaceMy()); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); var pdbFileName = Path.ChangeExtension(assemblyFileName, ".pdb"); if (File.Exists(pdbFileName)) decompiler.DebugInfoProvider = DebugInfoUtils.FromFile(module, pdbFileName); var xmlDocFileName = Path.ChangeExtension(assemblyFileName, ".xml"); if (File.Exists(xmlDocFileName)) decompiler.DocumentationProvider = new XmlDocumentationProvider(xmlDocFileName); var syntaxTree = decompiler.DecompileWholeModuleAsSingleFile(sortTypes: true); StringWriter output = new StringWriter(); CSharpFormattingOptions formattingPolicy = CreateFormattingPolicyForTests(); var visitor = new CSharpOutputVisitor(output, formattingPolicy); syntaxTree.AcceptVisitor(visitor); string fileName = Path.GetTempFileName(); File.WriteAllText(fileName, output.ToString()); return Task.FromResult(fileName); } } private static CSharpFormattingOptions CreateFormattingPolicyForTests() { var formattingPolicy = FormattingOptionsFactory.CreateSharpDevelop(); formattingPolicy.StatementBraceStyle = BraceStyle.NextLine; formattingPolicy.CatchNewLinePlacement = NewLinePlacement.NewLine; formattingPolicy.ElseNewLinePlacement = NewLinePlacement.NewLine; formattingPolicy.FinallyNewLinePlacement = NewLinePlacement.NewLine; formattingPolicy.SpaceBeforeAnonymousMethodParentheses = true; return formattingPolicy; } public static async Task RunAndCompareOutput(string testFileName, string outputFile, string decompiledOutputFile, string decompiledCodeFile = null, bool useTestRunner = false, bool force32Bit = false) { string output1, output2, error1, error2; int result1, result2; if (useTestRunner) { (result1, output1, error1) = await RunWithTestRunner(outputFile, force32Bit).ConfigureAwait(false); (result2, output2, error2) = await RunWithTestRunner(decompiledOutputFile, force32Bit).ConfigureAwait(false); } else { (result1, output1, error1) = await Run(outputFile).ConfigureAwait(false); (result2, output2, error2) = await Run(decompiledOutputFile).ConfigureAwait(false); } Assert.That(result1, Is.EqualTo(0), "Exit code != 0; did the test case crash?" + Environment.NewLine + error1); Assert.That(result2, Is.EqualTo(0), "Exit code != 0; did the decompiled code crash?" + Environment.NewLine + error2); if (output1 != output2 || error1 != error2) { StringBuilder b = new StringBuilder(); b.AppendLine($"Test {testFileName} failed: output does not match."); if (decompiledCodeFile != null) { b.AppendLine($"Decompiled code in {decompiledCodeFile}:line 1"); } if (error1 != error2) { b.AppendLine("Got different error output."); b.AppendLine("Original error:"); b.AppendLine(error1); b.AppendLine(); b.AppendLine("Error after de+re-compiling:"); b.AppendLine(error2); b.AppendLine(); } if (output1 != output2) { string outputFileName = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(testFileName)); File.WriteAllText(outputFileName + ".original.out", output1); File.WriteAllText(outputFileName + ".decompiled.out", output2); int diffLine = 0; string lastHeader = null; Tuple errorItem = null; foreach (var pair in output1.Replace("\r", "").Split('\n').Zip(output2.Replace("\r", "").Split('\n'), Tuple.Create)) { diffLine++; if (pair.Item1 != pair.Item2) { errorItem = pair; break; } if (pair.Item1.EndsWith(":", StringComparison.Ordinal)) { lastHeader = pair.Item1; } } b.AppendLine($"Output differs; first difference in line {diffLine}"); if (lastHeader != null) { b.AppendLine(lastHeader); } b.AppendLine($"{outputFileName}.original.out:line {diffLine}"); b.AppendLine(errorItem.Item1); b.AppendLine($"{outputFileName}.decompiled.out:line {diffLine}"); b.AppendLine(errorItem.Item2); } Assert.Fail(b.ToString()); } } internal static void RepeatOnIOError(Action action, int numTries = 5) { for (int i = 0; i < numTries - 1; i++) { try { action(); return; } catch (IOException) { } catch (UnauthorizedAccessException) { // potential virus scanner problem } Thread.Sleep(10); } // If the last try still fails, don't catch the exception action(); } public static async Task SignAssembly(string assemblyPath, string keyFilePath) { string snPath = SdkUtility.GetSdkPath("sn.exe"); var command = Cli.Wrap(snPath) .WithArguments($"-R \"{assemblyPath}\" \"{keyFilePath}\"") .WithValidation(CommandResultValidation.None); var result = await command.ExecuteBufferedAsync().ConfigureAwait(false); Assert.That(result.ExitCode, Is.EqualTo(0), "sn failed"); if (!string.IsNullOrWhiteSpace(result.StandardOutput)) { Console.WriteLine("output:" + Environment.NewLine + result.StandardOutput); } if (!string.IsNullOrWhiteSpace(result.StandardError)) { Console.WriteLine("errors:" + Environment.NewLine + result.StandardError); } } public static async Task FindMSBuild() { string path = vswhereToolset.GetVsWhere(); var result = await Cli.Wrap(path) .WithArguments(@"-latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe") .WithValidation(CommandResultValidation.None) .ExecuteBufferedAsync().ConfigureAwait(false); if (result.ExitCode != 0) throw new InvalidOperationException("Could not find MSBuild"); return result.StandardOutput.TrimEnd(); } } public class CompilerResults { readonly HashSet tempFiles = new(Decompiler.Util.Platform.FileNameComparer); string pathToAssembly; public string PathToAssembly { get { if (pathToAssembly == null) { pathToAssembly = TestsAssemblyOutput.GetTempFileName(); tempFiles.Add(pathToAssembly); } return pathToAssembly; } set { if (pathToAssembly != null) { throw new InvalidOperationException("PathToAssembly can only be set once"); } pathToAssembly = value; } } public void DeleteTempFiles() { foreach (var file in tempFiles) { Tester.RepeatOnIOError(() => File.Delete(file)); } } public void AddTempFile(string file) { if (!Path.IsPathFullyQualified(file)) { throw new InvalidOperationException("file must be a fully qualified path"); } tempFiles.Add(file); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Helpers/TestsAssemblyOutput.cs ================================================ // Copyright (c) 2024 Christoph Wille // // 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.IO; using Microsoft.Extensions.Configuration; namespace ICSharpCode.Decompiler.Tests.Helpers { /// /// Centralizes all file-path generation for compilation output (assemblies) /// /// DecompilerTests.config.json file format: /// { /// "TestsAssemblyTempPath": "d:\\test\\" /// } /// internal static class TestsAssemblyOutput { static string? TestsAssemblyTempPath = null; private static bool UseCustomPath => !string.IsNullOrWhiteSpace(TestsAssemblyTempPath); static TestsAssemblyOutput() { if (!File.Exists("DecompilerTests.config.json")) return; var builder = new ConfigurationBuilder() .AddJsonFile("DecompilerTests.config.json", optional: true, reloadOnChange: false); IConfigurationRoot configuration = builder.Build(); var pathRedirectIfAny = configuration["TestsAssemblyTempPath"]; if (!string.IsNullOrWhiteSpace(pathRedirectIfAny)) { TestsAssemblyTempPath = pathRedirectIfAny; } } public static string GetFilePath(string testCasePath, string testName, string computedExtension) { if (!UseCustomPath) return Path.Combine(testCasePath, testName) + computedExtension; // As we are using the TestsAssemblyTempPath flat, we need to make sure that duplicated test names don't create file name clashes return Path.Combine(TestsAssemblyTempPath, testName) + Guid.NewGuid().ToString() + computedExtension; } public static string GetTempFileName() { if (!UseCustomPath) return Path.GetTempFileName(); return Path.Combine(TestsAssemblyTempPath, Path.GetRandomFileName()); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj ================================================ true true net10.0-windows preview win-x64 win-arm64 false true ICSharpCode.Decompiler.Tests.MicrosoftTestingPlatformEntryPoint Exe True $(NoWarn);1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632;626;8618;8714;8602;8981;NU1902;NU1903 ROSLYN;ROSLYN2;ROSLYN3;ROSLYN4;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100;CS110;CS120;CS130 False False False false True True ..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk full true pdbonly true TRACE;DEBUG;$(DefineConstants) TRACE;$(DefineConstants) ================================================ FILE: ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class ILPrettyTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/ILPretty"; [Test] public void AllFilesHaveTests() { var testNames = typeof(ILPrettyTestRunner).GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase)) { var testName = file.Name.Split('.')[0]; Assert.That(testNames, Has.Member(testName)); Assert.That(File.Exists(Path.Combine(TestCasePath, testName + ".cs"))); } } } [Test, Ignore("Need to decide how to represent virtual methods without 'newslot' flag")] public async Task Issue379() { await Run(); } [Test] public async Task Issue646() { await Run(); } [Test] public async Task Issue684() { await Run(); } [Test] public async Task Issue959() { await Run(); } [Test] public async Task Issue982() { await Run(); } [Test] public async Task Issue1038() { await Run(); } [Test] public async Task Issue1047() { await Run(); } [Test] public async Task Issue1389() { await Run(); } [Test] public async Task Issue1918() { await Run(); } [Test] public async Task Issue1922() { await Run(); } [Test] public async Task FSharpUsing_Debug() { await Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false }); } [Test] public async Task FSharpUsing_Release() { await Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false }); } [Test] public async Task DirectCallToExplicitInterfaceImpl() { await Run(); } [Test] public async Task EvalOrder() { await Run(); } [Test] public async Task CS1xSwitch_Debug() { await Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false }); } [Test] public async Task CS1xSwitch_Release() { await Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false }); } [Test] public async Task UnknownTypes() { await Run(); } [Test] public async Task Issue1145() { await Run(); } [Test] public async Task Issue1157() { await Run(); } [Test] public async Task Issue1256() { await Run(); } [Test] public async Task Issue1323() { await Run(); } [Test] public async Task Issue1325() { await Run(); } [Test] public async Task Issue1681() { await Run(); } [Test] public async Task Issue1454() { await Run(); } [Test] public async Task Issue2104() { await Run(); } [Test] public async Task Issue2443() { await Run(); } [Test] public async Task Issue3344CkFinite() { await Run(); } [Test] public async Task Issue3421() { await Run(); } [Test] public async Task Issue3442() { await Run(); } [Test] public async Task Issue3465() { await Run(); } [Test] public async Task Issue3466() { await Run(); } [Test] public async Task Issue3504() { await Run(); } [Test] public async Task Issue3524() { await Run(); } [Test] public async Task Issue3552() { await Run(); } [Test] public async Task Issue2260SwitchString() { await Run(); } [Test] public async Task ConstantBlobs() { await Run(); } [Test] public async Task SequenceOfNestedIfs() { await Run(); } [Test] public async Task Unsafe() { await Run(assemblerOptions: AssemblerOptions.Library | AssemblerOptions.UseLegacyAssembler); } [Test] public async Task CallIndirect() { await Run(); } [Test] public async Task FSharpLoops_Debug() { CopyFSharpCoreDll(); await Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false }); } [Test] public async Task FSharpLoops_Release() { CopyFSharpCoreDll(); await Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false }); } [Test] public async Task WeirdEnums() { await Run(); } [Test] public async Task GuessAccessors() { await Run(); } [Test] public async Task EmptyBodies() { await Run(); } [Test] public async Task MonoFixed() { await Run(); } [Test] public async Task ExtensionEncodingV1() { // uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25380.108 // see ExtensionEncodingV1.il for details await Run(); } [Test] public async Task ExtensionEncodingV2() { // uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25451.107 // see ExtensionEncodingV2.il for details await Run(); } async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null, AssemblerOptions assemblerOptions = AssemblerOptions.Library) { if (settings == null) { // never use file-scoped namespaces, unless explicitly specified settings = new DecompilerSettings { FileScopedNamespaces = false }; } var ilFile = Path.Combine(TestCasePath, testName + ".il"); var csFile = Path.Combine(TestCasePath, testName + ".cs"); var executable = await Tester.AssembleIL(ilFile, assemblerOptions).ConfigureAwait(false); var decompiled = await Tester.DecompileCSharp(executable, settings).ConfigureAwait(false); CodeAssert.FilesAreEqual(csFile, decompiled, ["EXPECTED_OUTPUT"]); Tester.RepeatOnIOError(() => File.Delete(decompiled)); } static readonly object copyLock = new object(); static void CopyFSharpCoreDll() { lock (copyLock) { if (File.Exists(Path.Combine(TestCasePath, "FSharp.Core.dll"))) return; string fsharpCoreDll = Path.Combine(TestCasePath, "..\\..\\..\\ILSpy-tests\\FSharp\\FSharp.Core.dll"); if (!File.Exists(fsharpCoreDll)) Assert.Ignore("Ignored because of missing ILSpy-tests repo. Must be checked out separately from https://github.com/icsharpcode/ILSpy-tests!"); File.Copy(fsharpCoreDll, Path.Combine(TestCasePath, "FSharp.Core.dll")); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; using static ICSharpCode.Decompiler.Output.ConversionFlags; namespace ICSharpCode.Decompiler.Tests.Output { [TestFixture] public class CSharpAmbienceTests { ICompilation compilation; CSharpAmbience ambience; [OneTimeSetUp] public void FixtureSetUp() { ambience = new CSharpAmbience(); compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib.WithOptions(TypeSystemOptions.Default)); } ITypeDefinition GetDefinition(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } var foundType = compilation.FindType(type).GetDefinition(); Assert.That(foundType, Is.Not.Null); return foundType; } const ConversionFlags ILSpyMainTreeViewTypeFlags = ShowTypeParameterList | PlaceReturnTypeAfterParameterList; const ConversionFlags ILSpyMainTreeViewMemberFlags = ILSpyMainTreeViewTypeFlags | ShowParameterList | ShowReturnType | ShowParameterModifiers; #region ITypeDefinition tests [TestCase(None, "Dictionary")] [TestCase(ShowDefinitionKeyword, "class Dictionary")] [TestCase(ShowAccessibility, "public Dictionary")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, "public class Dictionary")] [TestCase(ShowTypeParameterList, "Dictionary")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "public class Dictionary")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.Dictionary")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "public class System.Collections.Generic.Dictionary")] [TestCase(ILSpyMainTreeViewTypeFlags, "Dictionary")] public void GenericType(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(Dictionary<,>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "Object")] [TestCase(ShowDefinitionKeyword, "class Object")] [TestCase(ShowAccessibility, "public Object")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, "public class Object")] [TestCase(ShowTypeParameterList, "Object")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "public class Object")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Object")] [TestCase(All, "public class System.Object")] [TestCase(ILSpyMainTreeViewTypeFlags, "Object")] public void SimpleType(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(object)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "IEnumerable")] [TestCase(ShowTypeParameterList, "IEnumerable")] [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "IEnumerable")] [TestCase(All, "public interface System.Collections.Generic.IEnumerable")] [TestCase(ILSpyMainTreeViewTypeFlags, "IEnumerable")] public void GenericInterface(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(IEnumerable<>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "Enumerator")] [TestCase(ShowDefinitionKeyword, "struct Enumerator")] [TestCase(ShowAccessibility, "public Enumerator")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, "public struct Enumerator")] [TestCase(ShowTypeParameterList, "Enumerator")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "public struct Enumerator")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.List.Enumerator")] [TestCase(ShowDeclaringType | ShowTypeParameterList, "List.Enumerator")] [TestCase(All, "public struct System.Collections.Generic.List.Enumerator")] [TestCase(ILSpyMainTreeViewTypeFlags, "Enumerator")] public void GenericTypeWithNested(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(List<>.Enumerator)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "StaticClass")] [TestCase(ShowDefinitionKeyword, "class StaticClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword, "static class StaticClass")] [TestCase(ShowModifiers | ShowAccessibility, "private static StaticClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, "private static class StaticClass")] [TestCase(ShowModifiers | ShowTypeParameterList, "static StaticClass")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "private static class StaticClass")] [TestCase(All, "private static class ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.StaticClass")] [TestCase(ILSpyMainTreeViewTypeFlags, "StaticClass")] public void StaticClassTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(StaticClass)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "SealedClass")] [TestCase(ShowDefinitionKeyword, "class SealedClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword, "sealed class SealedClass")] [TestCase(ShowModifiers | ShowAccessibility, "private sealed SealedClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, "private sealed class SealedClass")] [TestCase(ShowModifiers | ShowTypeParameterList, "sealed SealedClass")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "private sealed class SealedClass")] [TestCase(All, "private sealed class ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.SealedClass")] [TestCase(ILSpyMainTreeViewTypeFlags, "SealedClass")] public void SealedClassTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(SealedClass)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "RefStruct")] [TestCase(ShowDefinitionKeyword, "struct RefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, "ref struct RefStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private ref RefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, "private ref struct RefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "ref RefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "private ref struct RefStruct")] [TestCase(All, "private ref struct ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.RefStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "RefStruct")] public void RefStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(RefStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "ReadonlyStruct")] [TestCase(ShowDefinitionKeyword, "struct ReadonlyStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, "readonly struct ReadonlyStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private readonly ReadonlyStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, "private readonly struct ReadonlyStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "readonly ReadonlyStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "private readonly struct ReadonlyStruct")] [TestCase(All, "private readonly struct ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.ReadonlyStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyStruct")] public void ReadonlyStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(ReadonlyStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "ReadonlyRefStruct")] [TestCase(ShowDefinitionKeyword, "struct ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, "readonly ref struct ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private readonly ref ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, "private readonly ref struct ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "readonly ref ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, "private readonly ref struct ReadonlyRefStruct")] [TestCase(All, "private readonly ref struct ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.ReadonlyRefStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyRefStruct")] public void ReadonlyRefStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(ReadonlyRefStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } #endregion #region Delegate tests [TestCase(None, "Func")] [TestCase(ShowTypeParameterList, "Func")] [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "Func")] [TestCase(ShowTypeParameterList | ShowReturnType | ShowTypeParameterVarianceModifier, "TResult Func")] [TestCase(ShowTypeParameterList | ShowParameterList | ShowTypeParameterVarianceModifier, "Func(T)")] [TestCase(ShowTypeParameterList | ShowParameterList | ShowReturnType | ShowTypeParameterVarianceModifier, "TResult Func(T)")] [TestCase(All & ~PlaceReturnTypeAfterParameterList, "public delegate TResult System.Func(T arg);")] [TestCase(All, "public delegate System.Func(T arg) : TResult;")] [TestCase(ILSpyMainTreeViewTypeFlags, "Func")] public void FuncDelegate(ConversionFlags flags, string expectedOutput) { var func = GetDefinition(typeof(Func<,>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(func), Is.EqualTo(expectedOutput)); } #endregion #region IField tests [TestCase(All & ~PlaceReturnTypeAfterParameterList, "private int ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.Program.test;")] [TestCase(ILSpyMainTreeViewMemberFlags, "test : int")] [TestCase(ConversionFlags.All & ~(ConversionFlags.ShowDeclaringType | ConversionFlags.ShowModifiers | ConversionFlags.ShowAccessibility | ConversionFlags.PlaceReturnTypeAfterParameterList), "int test;")] public void SimpleField(ConversionFlags flags, string expectedOutput) { var field = GetDefinition(typeof(CSharpAmbienceTests.Program)).GetFields(f => f.Name == "test").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); } [TestCase(All & ~PlaceReturnTypeAfterParameterList, "private const int ICSharpCode.Decompiler.Tests.Output.CSharpAmbienceTests.Program.TEST2;")] [TestCase(ILSpyMainTreeViewMemberFlags, "TEST2 : int")] public void SimpleConstField(ConversionFlags flags, string expectedOutput) { var field = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetFields(f => f.Name == "TEST2").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); } #endregion #region IEvent tests [Test] public void EventWithDeclaringType() { var ev = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetEvents(f => f.Name == "ProgramChanged").Single(); ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; string result = ambience.ConvertSymbol(ev); Assert.That(result, Is.EqualTo("public event EventHandler Program.ProgramChanged;")); } [Test] public void CustomEvent() { var ev = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetEvents(f => f.Name == "SomeEvent").Single(); ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; string result = ambience.ConvertSymbol(ev); Assert.That(result, Is.EqualTo("public event EventHandler SomeEvent;")); } #endregion #region Property tests [TestCase(StandardConversionFlags, "public int Test { get; set; }")] [TestCase(ILSpyMainTreeViewMemberFlags, "Test : int")] public void AutomaticProperty(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.Name == "Test").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, "public int this[int index] { get; }")] [TestCase(ILSpyMainTreeViewMemberFlags, "this[int] : int")] public void Indexer(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, "int Interface.this[int index] { get; }")] [TestCase(ILSpyMainTreeViewMemberFlags, "Interface.this[int] : int")] public void ExplicitIndexer(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && p.IsExplicitInterfaceImplementation).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } #endregion #region IMethod tests [TestCase(StandardConversionFlags, "public Program(int x);")] [TestCase(ILSpyMainTreeViewMemberFlags, "Program(int)")] public void ConstructorTests(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetConstructors().Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, "~Program();")] [TestCase(ILSpyMainTreeViewMemberFlags, "~Program()")] public void DestructorTests(ConversionFlags flags, string expectedOutput) { var dtor = compilation.FindType(typeof(CSharpAmbienceTests.Program)) .GetMembers(m => m.SymbolKind == SymbolKind.Destructor, GetMemberOptions.IgnoreInheritedMembers).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(dtor), Is.EqualTo(expectedOutput)); } #endregion #region Test types #pragma warning disable 169, 67 class Test { } static class StaticClass { } sealed class SealedClass { } ref struct RefStruct { } readonly struct ReadonlyStruct { } readonly ref struct ReadonlyRefStruct { } interface Interface { int this[int x] { get; } } class Program : Interface { int test; const int TEST2 = 2; public int Test { get; set; } public int this[int index] { get { return index; } } int Interface.this[int index] { get { return index; } } public event EventHandler ProgramChanged; public event EventHandler SomeEvent { add { } remove { } } public static bool operator +(Program lhs, Program rhs) { throw new NotImplementedException(); } public static implicit operator Test(Program lhs) { throw new NotImplementedException(); } public static explicit operator int(Program lhs) { throw new NotImplementedException(); } public Program(int x) { } ~Program() { } public static void Main(string[] args) { Console.WriteLine("Hello World!"); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } public static void InParameter(in int a) { } } #endregion } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Output/ILAmbienceTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; using static ICSharpCode.Decompiler.Output.ConversionFlags; namespace ICSharpCode.Decompiler.Tests.Output { [TestFixture] public class ILAmbienceTests { ICompilation compilation; ILAmbience ambience; [OneTimeSetUp] public void FixtureSetUp() { ambience = new ILAmbience(); compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib.WithOptions(TypeSystemOptions.Default)); } ITypeDefinition GetDefinition(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } var foundType = compilation.FindType(type).GetDefinition(); Assert.That(foundType, Is.Not.Null); return foundType; } const ConversionFlags ILSpyMainTreeViewTypeFlags = ShowTypeParameterList | PlaceReturnTypeAfterParameterList; const ConversionFlags ILSpyMainTreeViewMemberFlags = ILSpyMainTreeViewTypeFlags | ShowParameterList | ShowReturnType | ShowParameterModifiers; #region ITypeDefinition tests [TestCase(None, "Dictionary`2")] [TestCase(ShowDefinitionKeyword, ".class Dictionary`2")] [TestCase(ShowAccessibility, "public Dictionary`2")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class public Dictionary`2")] [TestCase(ShowTypeParameterList, "Dictionary`2")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public Dictionary`2")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.Dictionary`2")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public System.Collections.Generic.Dictionary`2")] [TestCase(ILSpyMainTreeViewTypeFlags, "Dictionary`2")] public void GenericType(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(Dictionary<,>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "Object")] [TestCase(ShowDefinitionKeyword, ".class Object")] [TestCase(ShowAccessibility, "public Object")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class public Object")] [TestCase(ShowTypeParameterList, "Object")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public Object")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Object")] [TestCase(All, ".class public serializable beforefieldinit System.Object")] [TestCase(ILSpyMainTreeViewTypeFlags, "Object")] public void SimpleType(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(object)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "IEnumerable`1")] [TestCase(ShowTypeParameterList, "IEnumerable`1")] [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "IEnumerable`1<+T>")] [TestCase(All, ".class interface public abstract System.Collections.Generic.IEnumerable`1<+T>")] [TestCase(ILSpyMainTreeViewTypeFlags, "IEnumerable`1")] public void GenericInterface(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(IEnumerable<>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "Enumerator")] [TestCase(ShowDefinitionKeyword, ".class Enumerator")] [TestCase(ShowAccessibility, "nested public Enumerator")] [TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class nested public Enumerator")] [TestCase(ShowTypeParameterList, "Enumerator")] [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class nested public Enumerator")] [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.List`1.Enumerator")] [TestCase(ShowDeclaringType | ShowTypeParameterList, "List`1.Enumerator")] [TestCase(All, ".class nested public sealed serializable beforefieldinit System.Collections.Generic.List`1.Enumerator")] [TestCase(ILSpyMainTreeViewTypeFlags, "Enumerator")] public void GenericTypeWithNested(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(List<>.Enumerator)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "StaticClass")] [TestCase(ShowDefinitionKeyword, ".class StaticClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword, ".class abstract sealed beforefieldinit StaticClass")] [TestCase(ShowModifiers | ShowAccessibility, "private abstract sealed beforefieldinit StaticClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private abstract sealed beforefieldinit StaticClass")] [TestCase(ShowModifiers | ShowTypeParameterList, "abstract sealed beforefieldinit StaticClass")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private abstract sealed beforefieldinit StaticClass")] [TestCase(All, ".class private abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.StaticClass")] [TestCase(ILSpyMainTreeViewTypeFlags, "StaticClass")] public void StaticClassTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(StaticClass)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "SealedClass")] [TestCase(ShowDefinitionKeyword, ".class SealedClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit SealedClass")] [TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit SealedClass")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit SealedClass")] [TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit SealedClass")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit SealedClass")] [TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.SealedClass")] [TestCase(ILSpyMainTreeViewTypeFlags, "SealedClass")] public void SealedClassTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(SealedClass)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "RefStruct")] [TestCase(ShowDefinitionKeyword, ".class RefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit RefStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit RefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit RefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit RefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit RefStruct")] [TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.RefStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "RefStruct")] public void RefStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(RefStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "ReadonlyStruct")] [TestCase(ShowDefinitionKeyword, ".class ReadonlyStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit ReadonlyStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit ReadonlyStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit ReadonlyStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyStruct")] [TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.ReadonlyStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyStruct")] public void ReadonlyStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(ReadonlyStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } [TestCase(None, "ReadonlyRefStruct")] [TestCase(ShowDefinitionKeyword, ".class ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit ReadonlyRefStruct")] [TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyRefStruct")] [TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.ReadonlyRefStruct")] [TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyRefStruct")] public void ReadonlyRefStructTest(ConversionFlags flags, string expectedOutput) { var typeDef = GetDefinition(typeof(ReadonlyRefStruct)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); } #endregion #region Delegate tests [TestCase(None, "Func`2")] [TestCase(ShowTypeParameterList, "Func`2")] [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "Func`2<-T,+TResult>")] [TestCase(All, ".class public sealed System.Func`2<-T,+TResult>")] [TestCase(ILSpyMainTreeViewTypeFlags, "Func`2")] public void FuncDelegate(ConversionFlags flags, string expectedOutput) { var func = GetDefinition(typeof(Func<,>)); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(func), Is.EqualTo(expectedOutput)); } #endregion #region IField tests [TestCase(All & ~PlaceReturnTypeAfterParameterList, ".field private instance int32 ICSharpCode.Decompiler.Tests.Output.Program::test")] [TestCase(ILSpyMainTreeViewMemberFlags, "test : int32")] [TestCase(All & ~(ShowDeclaringType | ShowModifiers | ShowAccessibility | PlaceReturnTypeAfterParameterList), ".field int32 ICSharpCode.Decompiler.Tests.Output.Program::test")] public void SimpleField(ConversionFlags flags, string expectedOutput) { var field = GetDefinition(typeof(Program)).GetFields(f => f.Name == "test").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); } [TestCase(All & ~PlaceReturnTypeAfterParameterList, ".field private static literal int32 ICSharpCode.Decompiler.Tests.Output.Program::TEST2")] [TestCase(ILSpyMainTreeViewMemberFlags, "TEST2 : int32")] public void SimpleConstField(ConversionFlags flags, string expectedOutput) { var field = compilation.FindType(typeof(Program)).GetFields(f => f.Name == "TEST2").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); } #endregion #region IEvent tests [Test] public void EventWithDeclaringType() { var ev = compilation.FindType(typeof(Program)).GetEvents(f => f.Name == "ProgramChanged").Single(); ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; string result = ambience.ConvertSymbol(ev); Assert.That(result, Is.EqualTo(".event instance EventHandler Program::ProgramChanged")); } [Test] public void CustomEvent() { var ev = compilation.FindType(typeof(Program)).GetEvents(f => f.Name == "SomeEvent").Single(); ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; string result = ambience.ConvertSymbol(ev); Assert.That(result, Is.EqualTo(".event instance EventHandler SomeEvent")); } #endregion #region Property tests [TestCase(StandardConversionFlags, ".property instance int32 Test")] [TestCase(ILSpyMainTreeViewMemberFlags, "Test : int32")] public void AutomaticProperty(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.Name == "Test").Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, ".property instance int32 Item(int32 index)")] [TestCase(ILSpyMainTreeViewMemberFlags, "Item(int32) : int32")] public void Indexer(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, ".property instance int32 ICSharpCode.Decompiler.Tests.Output.Interface.Item(int32 index)")] [TestCase(ILSpyMainTreeViewMemberFlags, "ICSharpCode.Decompiler.Tests.Output.Interface.Item(int32) : int32")] public void ExplicitIndexer(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.IsIndexer && p.IsExplicitInterfaceImplementation).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } #endregion #region IMethod tests [TestCase(StandardConversionFlags, ".method public hidebysig specialname rtspecialname instance .ctor(int32 x)")] [TestCase(ILSpyMainTreeViewMemberFlags, ".ctor(int32)")] public void ConstructorTests(ConversionFlags flags, string expectedOutput) { var prop = compilation.FindType(typeof(Program)).GetConstructors().Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); } [TestCase(StandardConversionFlags, ".method family hidebysig virtual instance void Finalize()")] [TestCase(ILSpyMainTreeViewMemberFlags, "Finalize() : void")] public void DestructorTests(ConversionFlags flags, string expectedOutput) { var dtor = compilation.FindType(typeof(Program)) .GetMembers(m => m.SymbolKind == SymbolKind.Destructor, GetMemberOptions.IgnoreInheritedMembers).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(dtor), Is.EqualTo(expectedOutput)); } #endregion } #region Test types #pragma warning disable 169, 67 class Test { } static class StaticClass { } sealed class SealedClass { } ref struct RefStruct { } readonly struct ReadonlyStruct { } readonly ref struct ReadonlyRefStruct { } interface Interface { int this[int x] { get; } } class Program : Interface { int test; const int TEST2 = 2; public int Test { get; set; } public int this[int index] { get { return index; } } int Interface.this[int index] { get { return index; } } public event EventHandler ProgramChanged; public event EventHandler SomeEvent { add { } remove { } } public static bool operator +(Program lhs, Program rhs) { throw new NotImplementedException(); } public static implicit operator Test(Program lhs) { throw new NotImplementedException(); } public static explicit operator int(Program lhs) { throw new NotImplementedException(); } public Program(int x) { } ~Program() { } public static void Main(string[] args) { Console.WriteLine("Hello World!"); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } public static void InParameter(in int a) { } } #endregion } ================================================ FILE: ICSharpCode.Decompiler.Tests/Output/InsertParenthesesVisitorTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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.IO; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Output { [TestFixture] public class InsertParenthesesVisitorTests { CSharpFormattingOptions policy; [SetUp] public void SetUp() { policy = FormattingOptionsFactory.CreateMono(); } string InsertReadable(Expression expr) { expr = expr.Clone(); expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); StringWriter w = new StringWriter(); w.NewLine = " "; expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterTokenWriter(w) { IndentationString = "" }, policy)); return w.ToString(); } string InsertRequired(Expression expr) { expr = expr.Clone(); expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = false }); StringWriter w = new StringWriter(); w.NewLine = " "; expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterTokenWriter(w) { IndentationString = "" }, policy)); return w.ToString(); } [Test] public void EqualityInAssignment() { Expression expr = new AssignmentExpression( new IdentifierExpression("cond"), new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.Equality, new IdentifierExpression("b") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("cond = a == b")); Assert.That(InsertReadable(expr), Is.EqualTo("cond = a == b")); } [Test] public void LambdaInAssignment() { Expression expr = new AssignmentExpression( new IdentifierExpression("p"), new LambdaExpression { Body = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.Add, new IdentifierExpression("b") ) } ); Assert.That(InsertRequired(expr), Is.EqualTo("p = () => a + b")); Assert.That(InsertReadable(expr), Is.EqualTo("p = () => a + b")); } [Test] public void LambdaInDelegateAdditionRHS() { Expression expr = new BinaryOperatorExpression { Left = new IdentifierExpression("p"), Operator = BinaryOperatorType.Add, Right = new LambdaExpression { Body = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.Add, new IdentifierExpression("b") ) } }; Assert.That(InsertRequired(expr), Is.EqualTo("p + () => a + b")); Assert.That(InsertReadable(expr), Is.EqualTo("p + (() => a + b)")); } [Test] public void LambdaInDelegateAdditionLHS() { Expression expr = new BinaryOperatorExpression { Left = new LambdaExpression { Body = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.Add, new IdentifierExpression("b") ) }, Operator = BinaryOperatorType.Add, Right = new IdentifierExpression("p"), }; Assert.That(InsertRequired(expr), Is.EqualTo("(() => a + b) + p")); Assert.That(InsertReadable(expr), Is.EqualTo("(() => a + b) + p")); } [Test] public void TrickyCast1() { Expression expr = new CastExpression { Type = new PrimitiveType("int"), Expression = new UnaryOperatorExpression( UnaryOperatorType.Minus, new IdentifierExpression("a") ) }; Assert.That(InsertRequired(expr), Is.EqualTo("(int)-a")); Assert.That(InsertReadable(expr), Is.EqualTo("(int)(-a)")); } [Test] public void TrickyCast2() { Expression expr = new CastExpression { Type = new SimpleType("MyType"), Expression = new UnaryOperatorExpression( UnaryOperatorType.Minus, new IdentifierExpression("a") ) }; Assert.That(InsertRequired(expr), Is.EqualTo("(MyType)(-a)")); Assert.That(InsertReadable(expr), Is.EqualTo("(MyType)(-a)")); } [Test] public void TrickyCast3() { Expression expr = new CastExpression { Type = new SimpleType("MyType"), Expression = new UnaryOperatorExpression( UnaryOperatorType.Not, new IdentifierExpression("a") ) }; Assert.That(InsertRequired(expr), Is.EqualTo("(MyType)!a")); Assert.That(InsertReadable(expr), Is.EqualTo("(MyType)(!a)")); } [Test] public void TrickyCast4() { Expression expr = new CastExpression { Type = new SimpleType("MyType"), Expression = new PrimitiveExpression(int.MinValue), }; Assert.That(InsertRequired(expr), Is.EqualTo("(MyType)(-2147483648)")); Assert.That(InsertReadable(expr), Is.EqualTo("(MyType)(-2147483648)")); } [Test] public void TrickyCast5() { Expression expr = new CastExpression { Type = new SimpleType("MyType"), Expression = new PrimitiveExpression(-1.0), }; Assert.That(InsertRequired(expr), Is.EqualTo("(MyType)(-1.0)")); Assert.That(InsertReadable(expr), Is.EqualTo("(MyType)(-1.0)")); } [Test] public void TrickyCast6() { Expression expr = new CastExpression { Type = new PrimitiveType("double"), Expression = new PrimitiveExpression(int.MinValue), }; Assert.That(InsertRequired(expr), Is.EqualTo("(double)-2147483648")); Assert.That(InsertReadable(expr), Is.EqualTo("(double)(-2147483648)")); } [Test] public void CastAndInvoke() { Expression expr = new MemberReferenceExpression { Target = new CastExpression { Type = new PrimitiveType("string"), Expression = new IdentifierExpression("a") }, MemberName = "Length" }; Assert.That(InsertRequired(expr), Is.EqualTo("((string)a).Length")); Assert.That(InsertReadable(expr), Is.EqualTo("((string)a).Length")); } [Test] public void DoubleNegation() { Expression expr = new UnaryOperatorExpression( UnaryOperatorType.Minus, new UnaryOperatorExpression(UnaryOperatorType.Minus, new IdentifierExpression("a")) ); Assert.That(InsertRequired(expr), Is.EqualTo("- -a")); Assert.That(InsertReadable(expr), Is.EqualTo("-(-a)")); } [Test] public void AdditionWithConditional() { Expression expr = new BinaryOperatorExpression { Left = new IdentifierExpression("a"), Operator = BinaryOperatorType.Add, Right = new ConditionalExpression { Condition = new BinaryOperatorExpression { Left = new IdentifierExpression("b"), Operator = BinaryOperatorType.Equality, Right = new PrimitiveExpression(null) }, TrueExpression = new IdentifierExpression("c"), FalseExpression = new IdentifierExpression("d") } }; Assert.That(InsertRequired(expr), Is.EqualTo("a + (b == null ? c : d)")); Assert.That(InsertReadable(expr), Is.EqualTo("a + ((b == null) ? c : d)")); } [Test] public void TypeTestInConditional() { Expression expr = new ConditionalExpression { Condition = new IsExpression { Expression = new IdentifierExpression("a"), Type = new ComposedType { BaseType = new PrimitiveType("int"), HasNullableSpecifier = true } }, TrueExpression = new IdentifierExpression("b"), FalseExpression = new IdentifierExpression("c") }; Assert.That(InsertRequired(expr), Is.EqualTo("a is int? ? b : c")); Assert.That(InsertReadable(expr), Is.EqualTo("(a is int?) ? b : c")); policy.SpaceBeforeConditionalOperatorCondition = false; policy.SpaceAfterConditionalOperatorCondition = false; policy.SpaceBeforeConditionalOperatorSeparator = false; policy.SpaceAfterConditionalOperatorSeparator = false; Assert.That(InsertRequired(expr), Is.EqualTo("a is int? ?b:c")); Assert.That(InsertReadable(expr), Is.EqualTo("(a is int?)?b:c")); } [Test] public void MethodCallOnQueryExpression() { Expression expr = new InvocationExpression(new MemberReferenceExpression { Target = new QueryExpression { Clauses = { new QueryFromClause { Identifier = "a", Expression = new IdentifierExpression("b") }, new QuerySelectClause { Expression = new InvocationExpression(new MemberReferenceExpression { Target = new IdentifierExpression("a"), MemberName = "c" }) } } }, MemberName = "ToArray" }); Assert.That(InsertRequired(expr), Is.EqualTo("(from a in b select a.c ()).ToArray ()")); Assert.That(InsertReadable(expr), Is.EqualTo("(from a in b select a.c ()).ToArray ()")); } [Test] public void SumOfQueries() { QueryExpression query = new QueryExpression { Clauses = { new QueryFromClause { Identifier = "a", Expression = new IdentifierExpression("b") }, new QuerySelectClause { Expression = new IdentifierExpression("a") } } }; Expression expr = new BinaryOperatorExpression( query, BinaryOperatorType.Add, query.Clone() ); Assert.That(InsertRequired(expr), Is.EqualTo("(from a in b select a) + " + "from a in b select a")); Assert.That(InsertReadable(expr), Is.EqualTo("(from a in b select a) + " + "(from a in b select a)")); } [Test] public void QueryInTypeTest() { Expression expr = new IsExpression { Expression = new QueryExpression { Clauses = { new QueryFromClause { Identifier = "a", Expression = new IdentifierExpression("b") }, new QuerySelectClause { Expression = new IdentifierExpression("a") } } }, Type = new PrimitiveType("int") }; Assert.That(InsertRequired(expr), Is.EqualTo("(from a in b select a) is int")); Assert.That(InsertReadable(expr), Is.EqualTo("(from a in b select a) is int")); } [Test] public void PrePost() { Expression expr = new UnaryOperatorExpression( UnaryOperatorType.Increment, new UnaryOperatorExpression( UnaryOperatorType.PostIncrement, new IdentifierExpression("a") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("++a++")); Assert.That(InsertReadable(expr), Is.EqualTo("++(a++)")); } [Test] public void PostPre() { Expression expr = new UnaryOperatorExpression( UnaryOperatorType.PostIncrement, new UnaryOperatorExpression( UnaryOperatorType.Increment, new IdentifierExpression("a") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("(++a)++")); Assert.That(InsertReadable(expr), Is.EqualTo("(++a)++")); } [Test] public void Logical1() { Expression expr = new BinaryOperatorExpression( new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.ConditionalAnd, new IdentifierExpression("b") ), BinaryOperatorType.ConditionalAnd, new IdentifierExpression("c") ); Assert.That(InsertRequired(expr), Is.EqualTo("a && b && c")); Assert.That(InsertReadable(expr), Is.EqualTo("a && b && c")); } [Test] public void Logical2() { Expression expr = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.ConditionalAnd, new BinaryOperatorExpression( new IdentifierExpression("b"), BinaryOperatorType.ConditionalAnd, new IdentifierExpression("c") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("a && (b && c)")); Assert.That(InsertReadable(expr), Is.EqualTo("a && (b && c)")); } [Test] public void Logical3() { Expression expr = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.ConditionalOr, new BinaryOperatorExpression( new IdentifierExpression("b"), BinaryOperatorType.ConditionalAnd, new IdentifierExpression("c") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("a || b && c")); Assert.That(InsertReadable(expr), Is.EqualTo("a || (b && c)")); } [Test] public void Logical4() { Expression expr = new BinaryOperatorExpression( new IdentifierExpression("a"), BinaryOperatorType.ConditionalAnd, new BinaryOperatorExpression( new IdentifierExpression("b"), BinaryOperatorType.ConditionalOr, new IdentifierExpression("c") ) ); Assert.That(InsertRequired(expr), Is.EqualTo("a && (b || c)")); Assert.That(InsertReadable(expr), Is.EqualTo("a && (b || c)")); } [Test] public void ArrayCreationInIndexer() { Expression expr = new IndexerExpression { Target = new ArrayCreateExpression { Type = new PrimitiveType("int"), Arguments = { new PrimitiveExpression(1) } }, Arguments = { new PrimitiveExpression(0) } }; Assert.That(InsertRequired(expr), Is.EqualTo("(new int[1]) [0]")); Assert.That(InsertReadable(expr), Is.EqualTo("(new int[1]) [0]")); } [Test] public void ArrayCreationWithInitializerInIndexer() { Expression expr = new IndexerExpression { Target = new ArrayCreateExpression { Type = new PrimitiveType("int"), Arguments = { new PrimitiveExpression(1) }, Initializer = new ArrayInitializerExpression { Elements = { new PrimitiveExpression(42) } } }, Arguments = { new PrimitiveExpression(0) } }; Assert.That(InsertRequired(expr), Is.EqualTo("new int[1] { 42 } [0]")); Assert.That(InsertReadable(expr), Is.EqualTo("(new int[1] { 42 }) [0]")); } [Test] public void AssignmentInObjectOrCollectionInitializer() { Expression expr = new ObjectCreateExpression { Type = AstType.Create("List"), Initializer = new ArrayInitializerExpression { Elements = { new AssignmentExpression(new IdentifierExpression("x"), new PrimitiveExpression(42)) } } }; Expression expr2 = new ObjectCreateExpression { Type = AstType.Create("Data"), Initializer = new ArrayInitializerExpression { Elements = { new NamedExpression("X", new PrimitiveExpression(42)) } } }; Expression expr3 = new ObjectCreateExpression { Type = AstType.Create("Dictionary"), Initializer = new ArrayInitializerExpression { Elements = { new AssignmentExpression(new IndexerExpression(null, new PrimitiveExpression(0)), new PrimitiveExpression("42")) } } }; Assert.That(InsertRequired(expr), Is.EqualTo("new List { (x = 42) }")); Assert.That(InsertRequired(expr2), Is.EqualTo("new Data { X = 42 }")); Assert.That(InsertRequired(expr3), Is.EqualTo("new Dictionary { [0] = \"42\" }")); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Text; using System.Xml.Linq; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Tests.Helpers; using Microsoft.DiaSymReader.Tools; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class PdbGenerationTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen"; [Test] public void HelloWorld() { TestGeneratePdb(); } [Test] [Ignore("Missing nested local scopes for loops, differences in IL ranges")] public void ForLoopTests() { TestGeneratePdb(); } [Test] [Ignore("Differences in IL ranges")] public void LambdaCapturing() { TestGeneratePdb(); } [Test] [Ignore("Duplicate sequence points for local function")] public void Members() { TestGeneratePdb(); } [Test] public void CustomPdbId() { // Generate a PDB for an assembly using a randomly-generated ID, then validate that the PDB uses the specified ID (string peFileName, string pdbFileName) = CompileTestCase(nameof(CustomPdbId)); var moduleDefinition = new PEFile(peFileName); var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); var expectedPdbId = new BlobContentId(Guid.NewGuid(), (uint)Random.Shared.Next()); using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(CustomPdbId) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) { pdbStream.SetLength(0); PortablePdbWriter.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream, noLogo: true, pdbId: expectedPdbId); pdbStream.Position = 0; var metadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); var generatedPdbId = new BlobContentId(metadataReader.DebugMetadataHeader.Id); Assert.That(generatedPdbId.Guid, Is.EqualTo(expectedPdbId.Guid)); Assert.That(generatedPdbId.Stamp, Is.EqualTo(expectedPdbId.Stamp)); } } [Test] public void ProgressReporting() { // Generate a PDB for an assembly and validate that the progress reporter is called with reasonable values (string peFileName, string pdbFileName) = CompileTestCase(nameof(ProgressReporting)); var moduleDefinition = new PEFile(peFileName); var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); var lastFilesWritten = 0; var totalFiles = -1; Action reportFunc = progress => { if (totalFiles == -1) { // Initialize value on first call totalFiles = progress.TotalUnits; } Assert.That(totalFiles, Is.EqualTo(progress.TotalUnits)); Assert.That(lastFilesWritten + 1, Is.EqualTo(progress.UnitsCompleted)); lastFilesWritten = progress.UnitsCompleted; }; using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) { pdbStream.SetLength(0); PortablePdbWriter.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream, noLogo: true, progress: new TestProgressReporter(reportFunc)); pdbStream.Position = 0; var metadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); var generatedPdbId = new BlobContentId(metadataReader.DebugMetadataHeader.Id); } Assert.That(lastFilesWritten, Is.EqualTo(totalFiles)); } private class TestProgressReporter : IProgress { private Action reportFunc; public TestProgressReporter(Action reportFunc) { this.reportFunc = reportFunc; } public void Report(DecompilationProgress value) { reportFunc(value); } } private void TestGeneratePdb([CallerMemberName] string testName = null) { const PdbToXmlOptions options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.ThrowOnError | PdbToXmlOptions.IncludeTokens | PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeMethodSpans; string xmlFile = Path.Combine(TestCasePath, testName + ".xml"); (string peFileName, string pdbFileName) = CompileTestCase(testName); var moduleDefinition = new PEFile(peFileName); var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, testName + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) { pdbStream.SetLength(0); PortablePdbWriter.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream, noLogo: true); pdbStream.Position = 0; using (Stream peStream = File.OpenRead(peFileName)) using (Stream expectedPdbStream = File.OpenRead(pdbFileName)) { using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(pdbFileName, ".xml"), false, Encoding.UTF8)) { PdbToXmlConverter.ToXml(writer, expectedPdbStream, peStream, options); } peStream.Position = 0; using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(xmlFile, ".generated.xml"), false, Encoding.UTF8)) { PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options); } } } string expectedFileName = Path.ChangeExtension(xmlFile, ".expected.xml"); ProcessXmlFile(expectedFileName); string generatedFileName = Path.ChangeExtension(xmlFile, ".generated.xml"); ProcessXmlFile(generatedFileName); CodeAssert.AreEqual(Normalize(expectedFileName), Normalize(generatedFileName)); } private (string peFileName, string pdbFileName) CompileTestCase(string testName) { string xmlFile = Path.Combine(TestCasePath, testName + ".xml"); string xmlContent = File.ReadAllText(xmlFile); XDocument document = XDocument.Parse(xmlContent); var files = document.Descendants("file").ToDictionary(f => f.Attribute("name").Value, f => f.Value); Tester.CompileCSharpWithPdb(Path.Combine(TestCasePath, testName + ".expected"), files); string peFileName = Path.Combine(TestCasePath, testName + ".expected.dll"); string pdbFileName = Path.Combine(TestCasePath, testName + ".expected.pdb"); return (peFileName, pdbFileName); } private void ProcessXmlFile(string fileName) { var document = XDocument.Load(fileName); foreach (var file in document.Descendants("file")) { file.Attribute("checksum").Remove(); file.Attribute("embeddedSourceLength")?.Remove(); file.ReplaceNodes(new XCData(file.Value.Replace("\uFEFF", ""))); } document.Save(fileName, SaveOptions.None); } private string Normalize(string inputFileName) { return File.ReadAllText(inputFileName).Replace("\r\n", "\n").Replace("\r", "\n"); } } class StringWriterWithEncoding : StringWriter { readonly Encoding encoding; public StringWriterWithEncoding(Encoding encoding) { this.encoding = encoding ?? throw new ArgumentNullException("encoding"); } public override Encoding Encoding => encoding; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class PrettyTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/Pretty"; [Test] public void AllFilesHaveTests() { var testNames = typeof(PrettyTestRunner).GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase) || file.Extension.Equals(".cs", StringComparison.OrdinalIgnoreCase)) { var testName = file.Name.Split('.')[0]; Assert.That(testNames, Has.Member(testName)); } } } static readonly CompilerOptions[] noRoslynOptions = { CompilerOptions.None, CompilerOptions.Optimize }; static readonly CompilerOptions[] roslynOnlyWithNet40Options = { CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslynOnlyOptions = { CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn2OrNewerWithNet40Options = { CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn2OrNewerOptions = { CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn3OrNewerWithNet40Options = { CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn3OrNewerOptions = { CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn4OrNewerWithNet40Options = { CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslyn4OrNewerOptions = { CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] defaultOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] defaultOptionsWithMcs = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslyn3_11_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, CompilerOptions.UseMcs2_6_4, CompilerOptions.Optimize | CompilerOptions.UseMcs2_6_4, CompilerOptions.UseMcs5_23, CompilerOptions.Optimize | CompilerOptions.UseMcs5_23 }; [Test] public async Task HelloWorld() { await RunForLibrary(); await RunForLibrary(asmOptions: AssemblerOptions.UseDebug); } [Test] public async Task IndexRangeTest([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { if (cscOptions.HasFlag(CompilerOptions.UseRoslynLatest)) { Assert.Ignore("See https://github.com/icsharpcode/ILSpy/issues/2540"); } await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task InlineAssignmentTest([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CompoundAssignmentTest([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ShortCircuit([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CustomShortCircuitOperators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ExceptionHandling([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => { settings.NullPropagation = false; // legacy csc generates a dead store in debug builds settings.RemoveDeadStores = (cscOptions == CompilerOptions.None); settings.FileScopedNamespaces = false; }); } [Test] public async Task Switch([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => { // legacy csc generates a dead store in debug builds settings.RemoveDeadStores = (cscOptions == CompilerOptions.None); settings.SwitchExpressions = false; }); } [Test] public async Task SwitchExpressions([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ReduceNesting([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task DelegateConstruction([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => { settings.QueryExpressions = false; settings.NullableReferenceTypes = false; }); } [Test] public async Task AnonymousTypes([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Async([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Lock([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Using([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary( cscOptions: cscOptions, configureDecompiler: settings => { settings.UseEnhancedUsing = false; settings.FileScopedNamespaces = false; } ); } [Test] public async Task UsingVariables([ValueSource(nameof(roslyn3OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task GloballyQualifiedTypeInStringInterpolation([ValueSource(nameof(roslynOnlyWithNet40Options))] CompilerOptions cscOptions) { // https://github.com/icsharpcode/ILSpy/issues/3447 await RunForLibrary( cscOptions: cscOptions, configureDecompiler: settings => { settings.UsingDeclarations = false; settings.AlwaysUseGlobal = true; } ); } [Test] public async Task LiftedOperators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Operators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Loops([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => { // legacy csc generates a dead store in debug builds settings.RemoveDeadStores = (cscOptions == CompilerOptions.None); settings.UseExpressionBodyForCalculatedGetterOnlyProperties = false; }); } [Test] public async Task LocalFunctions([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.NullableEnable); } [Test] public async Task AutoProperties([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task QueryExpressions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task TypeAnalysisTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CheckedUnchecked([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ReferenceUnsafe); } [Test] public async Task ConstructorInitializers([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ProcessXmlDoc); } [Test] public async Task PInvoke([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { // This tests needs our own disassembler; ildasm has a bug with marshalinfo. await RunForLibrary(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler); } [Test] public async Task OutVariables([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task PatternMatching([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.NullableEnable); } [Test] public async Task DynamicTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ExpressionTrees([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task FixProxyCalls([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ValueTypes([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task VariableNaming([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.GeneratePdb); } [Test] public async Task VariableNamingWithoutSymbols([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => settings.UseDebugSymbols = false); } [Test] public async Task CS72_PrivateProtected([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task AsyncForeach([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task AsyncMain([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await Run(cscOptions: cscOptions); } [Test] public async Task AsyncStreams([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task AsyncUsing([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary( cscOptions: cscOptions, configureDecompiler: settings => { settings.UseEnhancedUsing = false; } ); } [Test] public async Task CustomTaskType([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task NullableRefTypes([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.NullableEnable); } [Test] public async Task NativeInts([ValueSource(nameof(roslyn3OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task FileScopedNamespaces([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => settings.FileScopedNamespaces = true); } [Test] public async Task Structs([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task FunctionPointers([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Records([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3610([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3611([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3452([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.NullableEnable); } [Test] public async Task Issue3598([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.NullableEnable); } [Test] public async Task ExtensionProperties([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview | CompilerOptions.NullableEnable); } [Test] public async Task NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task StringInterpolation([ValueSource(nameof(roslynOnlyWithNet40Options))] CompilerOptions cscOptions) { await Run(cscOptions: cscOptions); } [Test] public async Task CS73_StackAllocInitializers([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task RefLocalsAndReturns([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task RefFields([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ThrowExpressions([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task WellKnownConstants([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task QualifierTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task TupleTests([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task NamedArguments([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task OptionalArguments([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task OptionalArgumentsDisabled([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary( cscOptions: cscOptions, configureDecompiler: settings => { settings.OptionalArguments = false; } ); } [Test] public async Task Comparisons([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ConstantsTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ParamsCollections([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task ExpandParamsArgumentsDisabled([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => settings.ExpandParamsArguments = false); } [Test] public async Task Issue1080([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, configureDecompiler: settings => settings.SetLanguageVersion(CSharp.LanguageVersion.CSharp6)); } [Test] public async Task Issue3439([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3406([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3442([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3483([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions | CompilerOptions.CheckForOverflowUnderflow, configureDecompiler: settings => settings.CheckForOverflowUnderflow = true); } [Test] public async Task Issue3541([ValueSource(nameof(roslyn4OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3571_A([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3571_B([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3571_C([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3576([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Issue3584([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CustomAttributes2([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CustomAttributeConflicts([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CustomAttributeSamples([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task MemberTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task MultidimensionalArray([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task EnumTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task InterfaceTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task TypeMemberTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task YieldReturn([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task Discards([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task DeconstructionTests([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CS9_ExtensionGetEnumerator([ValueSource(nameof(roslyn3OrNewerWithNet40Options))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task CovariantReturns([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task StaticAbstractInterfaceMembers([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task MetadataAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task PointerArithmetic([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } [Test] public async Task InlineArrayTests([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action configureDecompiler = null) { await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, configureDecompiler); } async Task Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action configureDecompiler = null) { var csFile = Path.Combine(TestCasePath, testName + ".cs"); var exeFile = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(cscOptions) + ".exe"); if (cscOptions.HasFlag(CompilerOptions.Library)) { exeFile = Path.ChangeExtension(exeFile, ".dll"); } // 1. Compile CompilerResults output = null; try { output = await Tester.CompileCSharp(csFile, cscOptions, exeFile).ConfigureAwait(false); } finally { if (output != null) output.DeleteTempFiles(); } // 2. Decompile var settings = Tester.GetSettings(cscOptions); configureDecompiler?.Invoke(settings); var decompiled = await Tester.DecompileCSharp(exeFile, settings).ConfigureAwait(false); // 3. Compare CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).Append("EXPECTED_OUTPUT").ToArray()); Tester.RepeatOnIOError(() => File.Delete(decompiled)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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 ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler { [TestFixture] public sealed class TargetFrameworkTests { [TestCase(-1)] [TestCase(0)] [TestCase(1)] [TestCase(99)] [TestCase(int.MinValue)] public void VerifyThrowsForInvalidVersion(int invalidVersion) { // Arrange - nothing // Act void CreateInstance() => new TargetFramework(identifier: null, invalidVersion, profile: null); // Assert Assert.Throws(CreateInstance); } [TestCase(100, "v1.0")] [TestCase(102, "v1.0.2")] [TestCase(130, "v1.3")] [TestCase(145, "v1.4.5")] [TestCase(1670, "v16.7")] [TestCase(1800, "v18.0")] public void VerifyVersion(int version, string expectedVersion) { // Arrange - nothing // Act var targetFramework = new TargetFramework(identifier: null, version, profile: null); // Assert Assert.That(targetFramework.VersionNumber, Is.EqualTo(version)); Assert.That(targetFramework.VersionString, Is.EqualTo(expectedVersion)); } [Test] public void VerifyPortableLibrary() { // Arrange const string identifier = ".NETPortable"; // Act var targetFramework = new TargetFramework(identifier, 100, profile: null); // Assert Assert.That(targetFramework.IsPortableClassLibrary); Assert.That(targetFramework.Identifier, Is.EqualTo(identifier)); } [Test] [Pairwise] public void VerifyIdentifierAndProfile( [Values(null, "", ".NETFramework")] string identifier, [Values(null, "", ".Client")] string profile) { // Arrange - nothing // Act var targetFramework = new TargetFramework(identifier, 100, profile); // Assert Assert.That(targetFramework.Identifier, Is.EqualTo(identifier)); Assert.That(targetFramework.Profile, Is.EqualTo(profile)); } [TestCase(null, 350, "net35")] [TestCase(".NETFramework", 350, "net35")] [TestCase(".NETFramework", 400, "net40")] [TestCase(".NETFramework", 451, "net451")] [TestCase(".NETCoreApp", 200, "netcoreapp2.0")] [TestCase(".NETCoreApp", 310, "netcoreapp3.1")] [TestCase(".NETStandard", 130, "netstandard1.3")] [TestCase(".NETStandard", 200, "netstandard2.0")] [TestCase("Silverlight", 400, "sl4")] [TestCase("Silverlight", 550, "sl5")] [TestCase(".NETCore", 450, "netcore45")] [TestCase(".NETCore", 451, "netcore451")] [TestCase("WindowsPhone", 700, "wp7")] [TestCase("WindowsPhone", 810, "wp81")] [TestCase(".NETMicroFramework", 100, "netmf")] [TestCase(".NETMicroFramework", 210, "netmf")] [TestCase(".NETPortable", 100, null)] [TestCase("Unsupported", 100, null)] public void VerifyMoniker(string identifier, int version, string expectedMoniker) { // Arrange - nothing // Act var targetFramework = new TargetFramework(identifier, version, profile: null); // Assert Assert.That(targetFramework.Moniker, Is.EqualTo(expectedMoniker)); } [TestCase(".NETCoreApp, Version=v5.0", TargetFrameworkIdentifier.NET, "5.0.0")] [TestCase(".NETCoreApp, Version=v10.0", TargetFrameworkIdentifier.NET, "10.0.0")] public void VerifyUniversalAssemblyResolverParseTargetFramework(string targetFramework, TargetFrameworkIdentifier identifier, string version) { var (id, v) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework); Assert.That(id, Is.EqualTo(identifier)); Assert.That(v.ToString(3), Is.EqualTo(version)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs ================================================ // Copyright (c) 2025 Daniel Grunwald // // 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.IO; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler; [TestFixture] public sealed class WholeProjectDecompilerTests { [Test] public void UseNestedDirectoriesForNamespacesTrueWorks() { string targetDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetRandomFileName()); TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(null, false, null)); decompiler.Settings.UseNestedDirectoriesForNamespaces = true; decompiler.DecompileProject(new PEFile("ICSharpCode.Decompiler.dll"), targetDirectory); AssertDirectoryDoesntExist(targetDirectory); string projectDecompilerDirectory = Path.Combine(targetDirectory, "ICSharpCode", "Decompiler", "CSharp", "ProjectDecompiler"); string projectDecompilerFile = Path.Combine(projectDecompilerDirectory, $"{nameof(WholeProjectDecompiler)}.cs"); using (Assert.EnterMultipleScope()) { Assert.That(decompiler.Files.ContainsKey(projectDecompilerFile), Is.True); Assert.That(decompiler.Directories.Contains(projectDecompilerDirectory), Is.True); } } [Test] public void UseNestedDirectoriesForNamespacesFalseWorks() { string targetDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetRandomFileName()); TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(null, false, null)); decompiler.Settings.UseNestedDirectoriesForNamespaces = false; decompiler.DecompileProject(new PEFile("ICSharpCode.Decompiler.dll"), targetDirectory); AssertDirectoryDoesntExist(targetDirectory); string projectDecompilerDirectory = Path.Combine(targetDirectory, "ICSharpCode.Decompiler.CSharp.ProjectDecompiler"); string projectDecompilerFile = Path.Combine(projectDecompilerDirectory, $"{nameof(WholeProjectDecompiler)}.cs"); using (Assert.EnterMultipleScope()) { Assert.That(decompiler.Files.ContainsKey(projectDecompilerFile), Is.True); Assert.That(decompiler.Directories.Contains(projectDecompilerDirectory), Is.True); } } static void AssertDirectoryDoesntExist(string directory) { if (Directory.Exists(directory)) { Directory.Delete(directory, recursive: true); Assert.Fail("Directory should not have been created."); } } sealed class TestFriendlyProjectDecompiler(IAssemblyResolver assemblyResolver) : WholeProjectDecompiler(assemblyResolver) { public Dictionary Files { get; } = []; public HashSet Directories { get; } = []; protected override TextWriter CreateFile(string path) { StringWriter writer = new(); lock (Files) { Files[path] = writer; } return writer; } protected override void CreateDirectory(string path) { lock (Directories) { Directories.Add(path); } } protected override IEnumerable WriteMiscellaneousFilesInProject(PEFile module) => []; protected override IEnumerable WriteResourceFilesInProject(MetadataFile module) => []; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Properties/AssemblyInfo.cs ================================================ #region Using directives using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; #endregion [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] [assembly: AssemblyVersion(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision)] [assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithCommitHash)] [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] ================================================ FILE: ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Diagnostics; using System.IO; using System.Reflection.PortableExecutable; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CliWrap; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Tests; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Roundtrip { [TestFixture, Parallelizable(ParallelScope.All)] public class RoundtripAssembly { public static readonly string TestDir = Path.GetFullPath(Path.Combine(Tester.TestCasePath, "../../ILSpy-tests")); static readonly string nunit = Path.Combine(TestDir, "nunit", "nunit3-console.exe"); [Test] public async Task Cecil_net45() { await RunWithTest("Mono.Cecil-net45", "Mono.Cecil.dll", "Mono.Cecil.Tests.dll"); } [Test] public async Task NewtonsoftJson_net45() { await RunWithTest("Newtonsoft.Json-net45", "Newtonsoft.Json.dll", "Newtonsoft.Json.Tests.dll"); } [Test] public async Task NewtonsoftJson_pcl_debug() { try { await RunWithTest("Newtonsoft.Json-pcl-debug", "Newtonsoft.Json.dll", "Newtonsoft.Json.Tests.dll", useOldProjectFormat: true); } catch (CompilationFailedException) { Assert.Ignore("Cannot yet re-compile PCL projects."); } } [Test] public async Task NRefactory_CSharp() { await RunWithTest("NRefactory", "ICSharpCode.NRefactory.CSharp.dll", "ICSharpCode.NRefactory.Tests.dll"); } [Test] public async Task ICSharpCode_Decompiler() { await RunOnly("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll"); } [Test] public async Task ImplicitConversions() { await RunWithOutput("Random Tests\\TestCases", "ImplicitConversions.exe"); } [Test] public async Task ImplicitConversions_32() { await RunWithOutput("Random Tests\\TestCases", "ImplicitConversions_32.exe"); } [Test] public async Task ExplicitConversions() { await RunWithOutput("Random Tests\\TestCases", "ExplicitConversions.exe", LanguageVersion.CSharp8_0); } [Test] public async Task ExplicitConversions_32() { await RunWithOutput("Random Tests\\TestCases", "ExplicitConversions_32.exe", LanguageVersion.CSharp8_0); } [Test] public async Task ExplicitConversions_With_NativeInts() { await RunWithOutput("Random Tests\\TestCases", "ExplicitConversions.exe", LanguageVersion.CSharp9_0); } [Test] public async Task ExplicitConversions_32_With_NativeInts() { await RunWithOutput("Random Tests\\TestCases", "ExplicitConversions_32.exe", LanguageVersion.CSharp9_0); } [Test] public async Task Random_TestCase_1() { await RunWithOutput("Random Tests\\TestCases", "TestCase-1.exe", LanguageVersion.CSharp8_0); } [Test] [Ignore("See https://github.com/icsharpcode/ILSpy/issues/2541 - Waiting for https://github.com/dotnet/roslyn/issues/45929")] public async Task Random_TestCase_1_With_NativeInts() { await RunWithOutput("Random Tests\\TestCases", "TestCase-1.exe", LanguageVersion.CSharp9_0); } // Let's limit the roundtrip tests to C# 8.0 for now; because 9.0 is still in preview // and the generated project doesn't build as-is. const LanguageVersion defaultLanguageVersion = LanguageVersion.CSharp8_0; async Task RunWithTest(string dir, string fileToRoundtrip, string fileToTest, LanguageVersion languageVersion = defaultLanguageVersion, string keyFile = null, bool useOldProjectFormat = false) { await RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest).GetAwaiter().GetResult(), languageVersion, snkFilePath: keyFile, useOldProjectFormat: useOldProjectFormat); } async Task RunWithOutput(string dir, string fileToRoundtrip, LanguageVersion languageVersion = defaultLanguageVersion) { string inputDir = Path.Combine(TestDir, dir); await RunInternal(dir, fileToRoundtrip, outputDir => Tester.RunAndCompareOutput(fileToRoundtrip, Path.Combine(inputDir, fileToRoundtrip), Path.Combine(outputDir, fileToRoundtrip)).GetAwaiter().GetResult(), languageVersion); } async Task RunOnly(string dir, string fileToRoundtrip, LanguageVersion languageVersion = defaultLanguageVersion) { await RunInternal(dir, fileToRoundtrip, outputDir => { }, languageVersion); } async Task RunInternal(string dir, string fileToRoundtrip, Action testAction, LanguageVersion languageVersion, string snkFilePath = null, bool useOldProjectFormat = false) { if (!Directory.Exists(TestDir)) { Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{TestDir}' needs to be checked out separately." + Environment.NewLine + $"git clone https://github.com/icsharpcode/ILSpy-tests \"{TestDir}\""); } string inputDir = Path.Combine(TestDir, dir); string decompiledDir = inputDir + "-decompiled"; string outputDir = inputDir + "-output"; if (inputDir.EndsWith("TestCases")) { // make sure output dir names are unique so that we don't get trouble due to parallel test execution decompiledDir += Path.GetFileNameWithoutExtension(fileToRoundtrip) + "_" + languageVersion.ToString(); outputDir += Path.GetFileNameWithoutExtension(fileToRoundtrip) + "_" + languageVersion.ToString(); } ClearDirectory(decompiledDir); ClearDirectory(outputDir); string projectFile = null; foreach (string file in Directory.EnumerateFiles(inputDir, "*", SearchOption.AllDirectories)) { if (!file.StartsWith(inputDir + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { Assert.Fail($"Unexpected file name: {file}"); } string relFile = file.Substring(inputDir.Length + 1); Directory.CreateDirectory(Path.Combine(outputDir, Path.GetDirectoryName(relFile))); if (relFile.Equals(fileToRoundtrip, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"Decompiling {fileToRoundtrip}..."); Stopwatch w = Stopwatch.StartNew(); using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) { PEFile module = new PEFile(file, fileStream, PEStreamOptions.PrefetchEntireImage); var resolver = new TestAssemblyResolver(file, inputDir, module.Metadata.DetectTargetFrameworkId()); resolver.AddSearchDirectory(inputDir); resolver.RemoveSearchDirectory("."); // use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes var projectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}"); var settings = new DecompilerSettings(languageVersion); if (useOldProjectFormat) { settings.UseSdkStyleProjectFormat = false; } var decompiler = new TestProjectDecompiler(projectGuid, resolver, resolver, settings); if (snkFilePath != null) { decompiler.StrongNameKeyFile = Path.Combine(inputDir, snkFilePath); } decompiler.DecompileProject(module, decompiledDir); Console.WriteLine($"Decompiled {fileToRoundtrip} in {w.Elapsed.TotalSeconds:f2}"); projectFile = Path.Combine(decompiledDir, module.Name + ".csproj"); } } else { File.Copy(file, Path.Combine(outputDir, relFile)); } } Assert.That(projectFile, Is.Not.Null, $"Could not find {fileToRoundtrip}"); await Compile(projectFile, outputDir); testAction(outputDir); } static void ClearDirectory(string dir) { Directory.CreateDirectory(dir); foreach (string subdir in Directory.EnumerateDirectories(dir)) { for (int attempt = 0; ; attempt++) { try { Directory.Delete(subdir, true); break; } catch (IOException) { if (attempt >= 10) throw; Thread.Sleep(100); } } } foreach (string file in Directory.EnumerateFiles(dir)) { File.Delete(file); } } static async Task Compile(string projectFile, string outputDir) { Regex errorRegex = new Regex(@"^[\w\d.\\-]+\(\d+,\d+\):"); string suffix = $" [{projectFile}]"; var command = Cli.Wrap(await Tester.FindMSBuild()) .WithArguments($"/nologo /v:minimal /restore /p:OutputPath=\"{outputDir}\" \"{projectFile}\"") .WithValidation(CommandResultValidation.None) .WithStandardOutputPipe(PipeTarget.ToDelegate(PrintLine)); Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); var result = await command.ExecuteAsync().ConfigureAwait(false); if (result.ExitCode != 0) throw new CompilationFailedException($"Compilation of {Path.GetFileName(projectFile)} failed"); void PrintLine(string line) { if (line.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) { line = line.Substring(0, line.Length - suffix.Length); } Match m = errorRegex.Match(line); if (m.Success) { // Make path absolute so that it gets hyperlinked line = Path.GetDirectoryName(projectFile) + Path.DirectorySeparatorChar + line; } Console.WriteLine(line); } } static async Task RunTest(string outputDir, string fileToTest) { var command = Cli.Wrap(nunit) .WithWorkingDirectory(outputDir) .WithArguments($"\"{fileToTest}\"") .WithValidation(CommandResultValidation.None) .WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine)); Console.WriteLine($"\"{command.TargetFilePath}\" {command.Arguments}"); var result = await command.ExecuteAsync().ConfigureAwait(false); if (result.ExitCode != 0) throw new TestRunFailedException($"Test execution of {Path.GetFileName(fileToTest)} failed"); } class TestProjectDecompiler : WholeProjectDecompiler { public TestProjectDecompiler(Guid projectGuid, IAssemblyResolver resolver, AssemblyReferenceClassifier assemblyReferenceClassifier, DecompilerSettings settings) : base(settings, projectGuid, resolver, null, assemblyReferenceClassifier, debugInfoProvider: null) { } } class CompilationFailedException : Exception { public CompilationFailedException(string message) : base(message) { } } class TestRunFailedException : Exception { public TestRunFailedException(string message) : base(message) { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Collections.Generic; using System.Collections.Immutable; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Semantics { // assign short names to the fake reflection types using C = Conversion; using dynamic = ConversionTest.Dynamic; using nint = ConversionTest.NInt; using nuint = ConversionTest.NUInt; [TestFixture, Parallelizable(ParallelScope.All)] public unsafe class ConversionTest { /// /// A reflection class used to represent null. /// public sealed class Null { } /// /// A reflection class used to represent dynamic. /// public sealed class Dynamic { } /// /// A reflection class used to represent nint. /// public sealed class NInt { } /// /// A reflection class used to represent nuint. /// public sealed class NUInt { } CSharpConversions conversions; ICompilation compilation; [OneTimeSetUp] public void SetUp() { compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib, TypeSystemLoaderTests.SystemCore); conversions = new CSharpConversions(compilation); } public class ReplaceSpecialTypesVisitor : TypeVisitor { public override IType VisitTypeDefinition(ITypeDefinition type) { switch (type.FullName) { case "ICSharpCode.Decompiler.Tests.Semantics.ConversionTest.Dynamic": return SpecialType.Dynamic; case "ICSharpCode.Decompiler.Tests.Semantics.ConversionTest.Null": return SpecialType.NullType; case "ICSharpCode.Decompiler.Tests.Semantics.ConversionTest.NInt": return SpecialType.NInt; case "ICSharpCode.Decompiler.Tests.Semantics.ConversionTest.NUInt": return SpecialType.NUInt; default: return base.VisitTypeDefinition(type); } } } Conversion ImplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from).AcceptVisitor(new ReplaceSpecialTypesVisitor()); IType to2 = compilation.FindType(to).AcceptVisitor(new ReplaceSpecialTypesVisitor()); return conversions.ImplicitConversion(from2, to2); } Conversion ExplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from).AcceptVisitor(new ReplaceSpecialTypesVisitor()); IType to2 = compilation.FindType(to).AcceptVisitor(new ReplaceSpecialTypesVisitor()); return conversions.ExplicitConversion(from2, to2); } [Test] public void IdentityConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(char)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(string), typeof(string)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(object), typeof(object)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(char)), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(SpecialType.Dynamic, SpecialType.Dynamic), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(SpecialType.UnknownType, SpecialType.UnknownType), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, SpecialType.NullType), Is.EqualTo(C.IdentityConversion)); } [Test] public void DynamicIdentityConversions() { Assert.That(ImplicitConversion(typeof(object), typeof(dynamic)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(dynamic), typeof(object)), Is.EqualTo(C.IdentityConversion)); } [Test] public void ComplexDynamicIdentityConversions() { Assert.That(ImplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List[]>), typeof(List[]>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List[]>), typeof(List[]>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List[,]>), typeof(List[]>)), Is.EqualTo(C.None)); } [Test] public void TupleIdentityConversions() { var intType = compilation.FindType(typeof(int)); var stringType = compilation.FindType(typeof(string)); Assert.That(conversions.ImplicitConversion( new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "b")), new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "c"))), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion( new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "b")), new TupleType(compilation, ImmutableArray.Create(stringType, intType), ImmutableArray.Create("a", "b"))), Is.EqualTo(C.None)); } [Test] public void TupleConversions() { Assert.That( ImplicitConversion(typeof((int, string)), typeof((long, object))), Is.EqualTo(C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion, C.ImplicitReferenceConversion)))); Assert.That( ImplicitConversion(typeof(ValueTuple), typeof(ValueTuple)), Is.EqualTo(C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion)))); } [Test] public void PrimitiveConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(ushort)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(byte), typeof(char)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(long), typeof(int)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(float)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(float), typeof(decimal)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(char), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(uint), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); } [Test] public void EnumerationConversion() { ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); C implicitEnumerationConversion = C.EnumerationConversion(true, false); Assert.That(conversions.ImplicitConversion(zero, compilation.FindType(typeof(StringComparison))), Is.EqualTo(implicitEnumerationConversion)); Assert.That(conversions.ImplicitConversion(one, compilation.FindType(typeof(StringComparison))), Is.EqualTo(C.None)); } [Test] public void NullableConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(ushort?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(byte), typeof(char?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(long), typeof(int?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(float?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(float?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float), typeof(double?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(float), typeof(decimal?)), Is.EqualTo(C.None)); } [Test] public void NullableConversions2() { Assert.That(ImplicitConversion(typeof(char?), typeof(ushort?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(byte?), typeof(char?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int?), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(long?), typeof(int?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int?), typeof(float?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(bool?), typeof(float?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float?), typeof(double?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(float?), typeof(decimal?)), Is.EqualTo(C.None)); } [Test] public void NullableEnumerationConversion() { ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); C implicitEnumerationConversion = C.EnumerationConversion(true, true); Assert.That(conversions.ImplicitConversion(zero, compilation.FindType(typeof(StringComparison?))), Is.EqualTo(implicitEnumerationConversion)); Assert.That(conversions.ImplicitConversion(one, compilation.FindType(typeof(StringComparison?))), Is.EqualTo(C.None)); } [Test] public void NullLiteralConversions() { Assert.That(ImplicitConversion(typeof(Null), typeof(int?)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(char?)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(int)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Null), typeof(object)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(dynamic)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(string)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(int[])), Is.EqualTo(C.NullLiteralConversion)); } [Test] public void SimpleReferenceConversions() { Assert.That(ImplicitConversion(typeof(string), typeof(object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(BitArray), typeof(ICollection)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(IList), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(object), typeof(string)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(ICollection), typeof(BitArray)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable), typeof(IList)), Is.EqualTo(C.None)); } [Test] public void ConversionToDynamic() { Assert.That(ImplicitConversion(typeof(string), typeof(dynamic)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(int), typeof(dynamic)), Is.EqualTo(C.BoxingConversion)); } [Test] public void ConversionFromDynamic() { // There is no conversion from the type 'dynamic' to other types (except the identity conversion to object). // Such conversions only exists from dynamic expression. // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) Assert.That(ImplicitConversion(typeof(dynamic), typeof(string)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(dynamic), typeof(int)), Is.EqualTo(C.None)); var dynamicRR = new ResolveResult(SpecialType.Dynamic); Assert.That(conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(string))), Is.EqualTo(C.ImplicitDynamicConversion)); Assert.That(conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(int))), Is.EqualTo(C.ImplicitDynamicConversion)); } [Test] public void ParameterizedTypeConversions() { Assert.That(ImplicitConversion(typeof(List), typeof(ICollection)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(IList), typeof(ICollection)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(List), typeof(ICollection)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IList), typeof(ICollection)), Is.EqualTo(C.None)); } [Test] public void ArrayConversions() { Assert.That(ImplicitConversion(typeof(string[]), typeof(object[])), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[,]), typeof(object[,])), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(object[,])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(object[]), typeof(string[])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(string[]), typeof(IList)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[,]), typeof(IList)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(string[]), typeof(IList)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(Array)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(ICloneable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Array), typeof(string[])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(object), typeof(object[])), Is.EqualTo(C.None)); } [Test] public void VarianceConversions() { Assert.That(ImplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(ICollection), typeof(ICollection)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Comparer), typeof(IComparer)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Comparer), typeof(IComparer)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Comparer), typeof(Comparer)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.None)); } [Test] public void ImplicitPointerConversion() { Assert.That(ImplicitConversion(typeof(Null), typeof(int*)), Is.EqualTo(C.ImplicitPointerConversion)); Assert.That(ImplicitConversion(typeof(int*), typeof(void*)), Is.EqualTo(C.ImplicitPointerConversion)); } [Test] public void NoConversionFromPointerTypeToObject() { Assert.That(ImplicitConversion(typeof(int*), typeof(object)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int*), typeof(dynamic)), Is.EqualTo(C.None)); } [Test] public void ConversionToNInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(object), typeof(nint)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(void*), typeof(nint)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(sbyte), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(short), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ushort), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(uint), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(long), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ulong), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(char), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(float), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(IntPtr), typeof(nint)), Is.EqualTo(C.IdentityConversion)); Assert.That(ExplicitConversion(typeof(UIntPtr), typeof(nint)), Is.EqualTo(C.None)); } [Test] public void ConversionToNUInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(object), typeof(nuint)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(void*), typeof(nuint)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(sbyte), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(short), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ushort), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(uint), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(long), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ulong), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(char), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(float), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(IntPtr), typeof(nuint)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(UIntPtr), typeof(nuint)), Is.EqualTo(C.IdentityConversion)); } [Test] public void ConversionFromNInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(nint), typeof(object)), Is.EqualTo(C.BoxingConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(void*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(sbyte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(byte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(short)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(ushort)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(int)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(uint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(ulong)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(decimal)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(IntPtr)), Is.EqualTo(C.IdentityConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(UIntPtr)), Is.EqualTo(C.None)); } [Test] public void ConversionFromNUInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(nuint), typeof(object)), Is.EqualTo(C.BoxingConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(void*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(sbyte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(byte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(short)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(ushort)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(int)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(uint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(long)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(ulong)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(decimal)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(IntPtr)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(nuint), typeof(UIntPtr)), Is.EqualTo(C.IdentityConversion)); } [Test] public void NIntEnumConversion() { var explicitEnumConversion = C.EnumerationConversion(isImplicit: false, isLifted: false); Assert.That(ExplicitConversion(typeof(nint), typeof(StringComparison)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(StringComparison)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(nint)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(nuint)), Is.EqualTo(explicitEnumConversion)); } [Test] public void IntegerLiteralToNIntConversions() { Assert.That(IntegerLiteralConversion(0, typeof(nint))); Assert.That(IntegerLiteralConversion(-1, typeof(nint))); Assert.That(!IntegerLiteralConversion(uint.MaxValue, typeof(nint))); Assert.That(!IntegerLiteralConversion(long.MaxValue, typeof(nint))); } [Test] public void IntegerLiteralToNUIntConversions() { Assert.That(IntegerLiteralConversion(0, typeof(nuint))); Assert.That(!IntegerLiteralConversion(-1, typeof(nuint))); Assert.That(IntegerLiteralConversion(uint.MaxValue, typeof(nuint))); Assert.That(!IntegerLiteralConversion(long.MaxValue, typeof(nuint))); } [Test] public void UnconstrainedTypeParameter() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T"); ITypeParameter t2 = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 1, "T2"); ITypeParameter tm = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "TM"); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, t), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(t2, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, t2), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, tm), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(tm, t), Is.EqualTo(C.None)); } [Test] public void TypeParameterWithReferenceTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", hasReferenceTypeConstraint: true); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.NullLiteralConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); } [Test] public void TypeParameterWithValueTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", hasValueTypeConstraint: true); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.BoxingConversion)); } [Test] public void TypeParameterWithClassConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", constraints: new[] { compilation.FindType(typeof(StringComparer)) }); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.NullLiteralConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(StringComparer))), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer))), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer))), Is.EqualTo(C.ImplicitReferenceConversion)); } [Test] public void TypeParameterWithInterfaceConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", constraints: new[] { compilation.FindType(typeof(IList)) }); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IList))), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IEnumerable))), Is.EqualTo(C.BoxingConversion)); } [Test] public void UserDefinedImplicitConversion() { Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset)); Assert.That(c.IsImplicit && c.IsUserDefined); Assert.That(c.Method.FullName, Is.EqualTo("System.DateTimeOffset.op_Implicit")); Assert.That(ImplicitConversion(typeof(DateTimeOffset), typeof(DateTime)), Is.EqualTo(C.None)); ITypeDefinition classImplementingIDisposable = compilation.FindType(typeof(ClassImplementingIDisposable)).GetDefinition(); ITypeDefinition genericStructWithIDisposableConstraintAndImplicitConversion = compilation.FindType(typeof(GenericStructWithIDisposableConstraintAndImplicitConversion<>)).GetDefinition(); IType genericStructIDisposableInstance = new ParameterizedType(genericStructWithIDisposableConstraintAndImplicitConversion, ImmutableArray.Create(compilation.FindType(typeof(IDisposable)))); // C => S Conversion c2 = conversions.ImplicitConversion(classImplementingIDisposable, genericStructIDisposableInstance); Assert.That(c2.IsImplicit && c2.IsUserDefined); Assert.That(c2.Method.FullName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.GenericStructWithIDisposableConstraintAndImplicitConversion.op_Implicit")); Assert.That(conversions.ImplicitConversion(genericStructIDisposableInstance, classImplementingIDisposable), Is.EqualTo(C.None)); } [Test] public void UserDefinedImplicitNullableConversion() { // User-defined conversion followed by nullable conversion Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset?)); Assert.That(c.IsValid && c.IsUserDefined); Assert.That(!c.IsLifted); // Lifted user-defined conversion c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset?)); Assert.That(c.IsValid && c.IsUserDefined && c.IsLifted); // User-defined conversion doesn't drop the nullability c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset)); Assert.That(!c.IsValid); } bool IntegerLiteralConversion(object value, Type to) { IType fromType = compilation.FindType(value.GetType()).AcceptVisitor(new ReplaceSpecialTypesVisitor()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType to2 = compilation.FindType(to).AcceptVisitor(new ReplaceSpecialTypesVisitor()); return conversions.ImplicitConversion(crr, to2).IsValid; } [Test] public void IntegerLiteralToEnumConversions() { Assert.That(IntegerLiteralConversion(0, typeof(LoaderOptimization))); Assert.That(IntegerLiteralConversion(0L, typeof(LoaderOptimization))); Assert.That(IntegerLiteralConversion(0, typeof(LoaderOptimization?))); Assert.That(!IntegerLiteralConversion(0, typeof(string))); Assert.That(!IntegerLiteralConversion(1, typeof(LoaderOptimization))); } [Test] public void ImplicitConstantExpressionConversion() { Assert.That(IntegerLiteralConversion(0, typeof(int))); Assert.That(IntegerLiteralConversion(0, typeof(ushort))); Assert.That(IntegerLiteralConversion(0, typeof(sbyte))); Assert.That(IntegerLiteralConversion(-1, typeof(int))); Assert.That(!IntegerLiteralConversion(-1, typeof(ushort))); Assert.That(IntegerLiteralConversion(-1, typeof(sbyte))); Assert.That(IntegerLiteralConversion(200, typeof(int))); Assert.That(IntegerLiteralConversion(200, typeof(ushort))); Assert.That(!IntegerLiteralConversion(200, typeof(sbyte))); } [Test] public void ImplicitLongConstantExpressionConversion() { Assert.That(!IntegerLiteralConversion(0L, typeof(int))); Assert.That(!IntegerLiteralConversion(0L, typeof(short))); Assert.That(IntegerLiteralConversion(0L, typeof(long))); Assert.That(IntegerLiteralConversion(0L, typeof(ulong))); Assert.That(IntegerLiteralConversion(-1L, typeof(long))); Assert.That(!IntegerLiteralConversion(-1L, typeof(ulong))); } [Test] public void ImplicitConstantExpressionConversionToNullable() { Assert.That(IntegerLiteralConversion(0, typeof(uint?))); Assert.That(IntegerLiteralConversion(0, typeof(short?))); Assert.That(IntegerLiteralConversion(0, typeof(byte?))); Assert.That(!IntegerLiteralConversion(-1, typeof(uint?))); Assert.That(IntegerLiteralConversion(-1, typeof(short?))); Assert.That(!IntegerLiteralConversion(-1, typeof(byte?))); Assert.That(IntegerLiteralConversion(200, typeof(uint?))); Assert.That(IntegerLiteralConversion(200, typeof(short?))); Assert.That(IntegerLiteralConversion(200, typeof(byte?))); Assert.That(!IntegerLiteralConversion(0L, typeof(uint?))); Assert.That(IntegerLiteralConversion(0L, typeof(long?))); Assert.That(IntegerLiteralConversion(0L, typeof(ulong?))); Assert.That(IntegerLiteralConversion(-1L, typeof(long?))); Assert.That(!IntegerLiteralConversion(-1L, typeof(ulong?))); } [Test] public void ImplicitConstantExpressionConversionNumberInterfaces() { Assert.That(IntegerLiteralConversion(0, typeof(IFormattable))); Assert.That(IntegerLiteralConversion(0, typeof(IComparable))); Assert.That(!IntegerLiteralConversion(0, typeof(IComparable))); Assert.That(!IntegerLiteralConversion(0, typeof(IComparable))); } int BetterConversion(Type s, Type t1, Type t2) { IType sType = compilation.FindType(s).AcceptVisitor(new ReplaceSpecialTypesVisitor()); IType t1Type = compilation.FindType(t1).AcceptVisitor(new ReplaceSpecialTypesVisitor()); IType t2Type = compilation.FindType(t2).AcceptVisitor(new ReplaceSpecialTypesVisitor()); return conversions.BetterConversion(sType, t1Type, t2Type); } int BetterConversion(object value, Type t1, Type t2) { IType fromType = compilation.FindType(value.GetType()).AcceptVisitor(new ReplaceSpecialTypesVisitor()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType t1Type = compilation.FindType(t1).AcceptVisitor(new ReplaceSpecialTypesVisitor()); IType t2Type = compilation.FindType(t2).AcceptVisitor(new ReplaceSpecialTypesVisitor()); return conversions.BetterConversion(crr, t1Type, t2Type); } [Test] public void BetterConversion() { Assert.That(BetterConversion(typeof(string), typeof(string), typeof(object)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(string), typeof(object), typeof(IComparable)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(string), typeof(IEnumerable), typeof(IComparable)), Is.EqualTo(0)); } [Test] public void BetterPrimitiveConversion() { Assert.That(BetterConversion(typeof(short), typeof(int), typeof(long)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(short), typeof(int), typeof(uint)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(ushort), typeof(uint), typeof(int)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(char), typeof(short), typeof(int)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(char), typeof(ushort), typeof(int)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(sbyte), typeof(long), typeof(ulong)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(byte), typeof(ushort), typeof(short)), Is.EqualTo(2)); Assert.That(BetterConversion(1, typeof(sbyte), typeof(byte)), Is.EqualTo(1)); Assert.That(BetterConversion(1, typeof(ushort), typeof(sbyte)), Is.EqualTo(2)); } [Test] public void BetterNullableConversion() { Assert.That(BetterConversion(typeof(byte), typeof(int), typeof(uint?)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte?), typeof(int?), typeof(uint?)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(uint?)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(byte?), typeof(ulong?), typeof(uint?)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(uint)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(int)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ulong?), typeof(uint)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(byte), typeof(ulong?), typeof(int)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(ushort?), typeof(long?), typeof(int?)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(sbyte), typeof(int?), typeof(uint?)), Is.EqualTo(0)); } /* TODO: we should probably revive these tests somehow [Test] public void ExpansiveInheritance() { var a = new DefaultUnresolvedTypeDefinition(string.Empty, "A"); var b = new DefaultUnresolvedTypeDefinition(string.Empty, "B"); // interface A a.Kind = TypeKind.Interface; a.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.TypeDefinition, 0, "U") { Variance = VarianceModifier.Contravariant }); // interface B : A>> { } b.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.TypeDefinition, 0, "X")); b.BaseTypes.Add(new ParameterizedTypeReference( a, new[] { new ParameterizedTypeReference( a, new [] { new ParameterizedTypeReference( b, new [] { new TypeParameterReference(SymbolKind.TypeDefinition, 0) } ) } ) })); ICompilation compilation = TypeSystemHelper.CreateCompilation(a, b); ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a.FullTypeName); ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b.FullTypeName); IType type1 = new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.Double) }); IType type2 = new ParameterizedType(resolvedA, new[] { new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.String) }) }); Assert.That(!conversions.ImplicitConversion(type1, type2).IsValid); } [Test] public void ImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M(T t) where T : U { U u = $t$; } }"; Assert.AreEqual(C.BoxingConversion, GetConversion(program)); } [Test] public void InvalidImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M(T t) where U : T { U u = $t$; } }"; Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversion() { string program = @"using System; class Test { public void M(T[] t) where T : U { U[] u = $t$; } }"; // invalid, e.g. T=int[], U=object[] Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T t) where T : class, U where U : class { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T[] t) where T : class, U where U : class { U[] u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T t) where T : class, U { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T[] t) where T : class, U { U[] u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void MethodGroupConversion_Void() { string program = @"using System; delegate void D(); class Test { D d = $M$; public static void M() {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(!c.DelegateCapturesFirstArgument); Assert.IsNotNull(c.Method); } [Test] public void MethodGroupConversion_Void_InstanceMethod() { string program = @"using System; delegate void D(); class Test { D d; public void M() { d = $M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(c.DelegateCapturesFirstArgument); Assert.IsNotNull(c.Method); } [Test] public void MethodGroupConversion_MatchingSignature() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static object M(int argument) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_InvalidReturnType() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static int M(int argument) {} }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_CovariantReturnType() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static string M(int argument) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefArgumentTypesEqual() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(ref object o) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefArgumentObjectVsDynamic() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(ref dynamic o) {} }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefVsOut() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(out object o) {} }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void MethodGroupConversion_RefVsNormal() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void MethodGroupConversion_NormalVsOut() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(out object o) {} }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void MethodGroupConversion_MatchingNormalParameter() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_IdentityConversion() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(dynamic o) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_Contravariance() { string program = @"using System; delegate void D(string o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test, Ignore("Not sure if this conversion should be valid or not... NR and mcs both accept it as valid, csc treats it as invalid")] public void MethodGroupConversion_NoContravarianceDynamic() { string program = @"using System; delegate void D(string o); class Test { D d = $M$; public static void M(dynamic o) {} }"; var c = GetConversion(program); //Assert.IsFrue(c.IsValid); Assert.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_ExactMatchIsBetter() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x = null) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.String", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotLeaveOutOptionalParameters() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x, string y = null) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotUseExpandedParams() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(params string[] x) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_ExtensionMethod() { string program = @"using System; static class Ext { public static void M(this string s, int x) {} } class Test { delegate void D(int a); void F() { string s = """"; D d = $s.M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(c.DelegateCapturesFirstArgument); } [Test] public void MethodGroupConversion_ExtensionMethodUsedAsStaticMethod() { string program = @"using System; static class Ext { public static void M(this string s, int x) {} } class Test { delegate void D(string s, int a); void F() { D d = $Ext.M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(!c.DelegateCapturesFirstArgument); } [Test] public void MethodGroupConversion_ObjectToDynamic() { string program = @"using System; class Test { public void F(object o) {} public void M() { Action x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_ObjectToDynamicGenericArgument() { string program = @"using System; using System.Collections.Generic; class Test { public void F(List l) {} public void M() { Action> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_ObjectToDynamicReturnValue() { string program = @"using System; class Test { public object F() {} public void M() { Func x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObject() { string program = @"using System; class Test { public void F(dynamic o) {} public void M() { Action x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObjectGenericArgument() { string program = @"using System; using System.Collections.Generic; class Test { public void F(List l) {} public void M() { Action> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObjectReturnValue() { string program = @"using System; class Test { public dynamic F() {} public void M() { Func x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void UserDefined_IntLiteral_ViaUInt_ToCustomStruct() { string program = @"using System; struct T { public static implicit operator T(uint a) { return new T(); } } class Test { static void M() { T t = $1$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefined_NullLiteral_ViaString_ToCustomStruct() { string program = @"using System; struct T { public static implicit operator T(string a) { return new T(); } } class Test { static void M() { T t = $null$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefined_CanUseLiftedEvenIfReturnTypeAlreadyNullable() { string program = @"using System; struct S { public static implicit operator short?(S s) { return 0; } } class Test { static void M(S? s) { int? i = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.That(c.IsLifted); } [Test] public void UserDefinedImplicitConversion_PicksExactSourceTypeIfPossible() { string program = @"using System; class Convertible { public static implicit operator Convertible(int i) {return new Convertible(); } public static implicit operator Convertible(short s) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_PicksMostEncompassedSourceType() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } public static implicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $(ushort)33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NoMostEncompassedSourceTypeIsInvalid() { string program = @"using System; class Convertible { public static implicit operator Convertible(ulong l) {return new Convertible(); } public static implicit operator Convertible(int ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $(ushort)33$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_PicksExactTargetTypeIfPossible() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } public static implicit operator short(Convertible s) {return 0; } } class Test { public void M() { int a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_PicksMostEncompassingTargetType() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } public static implicit operator ushort(Convertible us) {return 0; } } class Test { public void M() { ulong a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("us", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NoMostEncompassingTargetTypeIsInvalid() { string program = @"using System; class Convertible { public static implicit operator uint(Convertible i) {return 0; } public static implicit operator short(Convertible us) {return 0; } } class Test { public void M() { long a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_AmbiguousIsInvalid() { string program = @"using System; class Convertible1 { public static implicit operator Convertible2(Convertible1 c) {return 0; } } class Convertible2 { public static implicit operator Convertible2(Convertible1 c) {return 0; } } class Test { public void M() { Convertible2 a = $new Convertible1()$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_DefinedNullableTakesPrecedenceOverLifted() { string program = @"using System; struct Convertible { public static implicit operator Convertible(int i) {return new Convertible(); } public static implicit operator Convertible?(int? ni) {return new Convertible(); } } class Test { public void M() { Convertible? a = $(int?)33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.That(!c.IsLifted); Assert.AreEqual("ni", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_UIntConstant() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } public static implicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NullableUIntConstant() { string program = @"using System; class Convertible { public static implicit operator Convertible(long? l) {return new Convertible(); } public static implicit operator Convertible(uint? ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_UseShortResult_BecauseNullableCannotBeUnpacked() { string program = @"using System; class Test { public static implicit operator int?(Test i) { return 0; } public static implicit operator short(Test s) { return 0; } } class Program { public static void Main(string[] args) { int x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName); } [Test] public void UserDefinedImplicitConversion_Short_Or_NullableByte_Target() { string program = @"using System; class Test { public static implicit operator short(Test s) { return 0; } public static implicit operator byte?(Test b) { return 0; } } class Program { public static void Main(string[] args) { int? x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName); } [Test] public void UserDefinedImplicitConversion_Byte_Or_NullableShort_Target() { string program = @"using System; class Test { public static implicit operator byte(Test b) { return 0; } public static implicit operator short?(Test s) { return 0; } } class Program { public static void Main(string[] args) { int? x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("s", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_Int_Or_NullableLong_Source() { string program = @"using System; class Test { public static implicit operator Test(int i) { return new Test(); } public static implicit operator Test(long? l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_Long_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_Long_Constant_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long l) { return new Test(); } } class Program { static void Main() { Test t = $1$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_NullableLong_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long? l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void PreferUserDefinedConversionOverReferenceConversion() { // actually this is not because user-defined conversions are better; // but because string is a better conversion target string program = @" class AA { public static implicit operator string(AA a) { return null; } } class Test { static void M(object obj) {} static void M(string str) {} static void Main() { $M(new AA())$; } }"; var rr = Resolve(program); Assert.That(!rr.IsError); Assert.AreEqual("str", rr.Member.Parameters[0].Name); } [Test] public void PreferAmbiguousConversionOverReferenceConversion() { // Ambiguous conversions are a compiler error; but they are not // preventing the overload from being chosen. // The user-defined conversion wins because BB is a better conversion target than object. string program = @" class AA { public static implicit operator BB(AA a) { return null; } } class BB { public static implicit operator BB(AA a) { return null; } } class Test { static void M(BB b) {} static void M(object o) {} static void Main() { M($new AA()$); } }"; var c = GetConversion(program); Assert.That(c.IsUserDefined); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } } class Test { public void M() { int i = 33; Convertible a = $i$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.ConversionBeforeUserDefinedOperator.IsImplicit); Assert.That(c.ConversionBeforeUserDefinedOperator.IsNumericConversion); Assert.That(c.ConversionBeforeUserDefinedOperator.IsValid); Assert.That(c.ConversionAfterUserDefinedOperator.IsIdentityConversion); } [Test] public void UserDefinedImplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } } class Test { public void M() { long a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.ConversionBeforeUserDefinedOperator.IsIdentityConversion); Assert.That(c.ConversionAfterUserDefinedOperator.IsImplicit); Assert.That(c.ConversionAfterUserDefinedOperator.IsNumericConversion); Assert.That(c.ConversionAfterUserDefinedOperator.IsValid); } [Test] public void UserDefinedImplicitConversion_IsImplicit() { // Bug icsharpcode/NRefactory#183: conversions from constant expressions were incorrectly marked as explicit string program = @"using System; class Test { void Hello(JsNumber3 x) { Hello($7$); } } public class JsNumber3 { public static implicit operator JsNumber3(int d) { return null; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsImplicit); Assert.That(!c.IsExplicit); Assert.AreEqual(Conversion.IdentityConversion, c.ConversionBeforeUserDefinedOperator); Assert.AreEqual(Conversion.IdentityConversion, c.ConversionAfterUserDefinedOperator); } */ } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs ================================================ // Copyright (c) 2010-2013 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 // 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 ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Semantics { using C = Conversion; using dynamic = ConversionTest.Dynamic; [TestFixture, Parallelizable(ParallelScope.All)] public class ExplicitConversionsTest { CSharpConversions conversions; ICompilation compilation; [OneTimeSetUp] public void SetUp() { compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib, TypeSystemLoaderTests.SystemCore); conversions = new CSharpConversions(compilation); } Conversion ExplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from).AcceptVisitor(new ConversionTest.ReplaceSpecialTypesVisitor()); IType to2 = compilation.FindType(to).AcceptVisitor(new ConversionTest.ReplaceSpecialTypesVisitor()); return conversions.ExplicitConversion(from2, to2); } [Test] public void PointerConversion() { Assert.That(ExplicitConversion(typeof(int*), typeof(short)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(short), typeof(void*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(void*), typeof(int*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(long*), typeof(byte*)), Is.EqualTo(C.ExplicitPointerConversion)); } [Test] public void ConversionFromDynamic() { // Explicit dynamic conversion is for resolve results only; // otherwise it's an explicit reference / unboxing conversion Assert.That(ExplicitConversion(typeof(dynamic), typeof(string)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(dynamic), typeof(int)), Is.EqualTo(C.UnboxingConversion)); var dynamicRR = new ResolveResult(SpecialType.Dynamic); Assert.That(conversions.ExplicitConversion(dynamicRR, compilation.FindType(typeof(string))), Is.EqualTo(C.ExplicitDynamicConversion)); Assert.That(conversions.ExplicitConversion(dynamicRR, compilation.FindType(typeof(int))), Is.EqualTo(C.ExplicitDynamicConversion)); } [Test] public void NumericConversions() { Assert.That(ExplicitConversion(typeof(sbyte), typeof(uint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(sbyte), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(sbyte)), Is.EqualTo(C.ExplicitNumericConversion)); // if an implicit conversion exists, ExplicitConversion() should return that Assert.That(ExplicitConversion(typeof(byte), typeof(int)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(float)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(decimal)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(double)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(decimal)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(bool), typeof(int)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(int), typeof(bool)), Is.EqualTo(C.None)); } [Test] public void EnumerationConversions() { var explicitEnumerationConversion = C.EnumerationConversion(false, false); Assert.That(ExplicitConversion(typeof(sbyte), typeof(StringComparison)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(char), typeof(StringComparison)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(StringComparison)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(StringComparison)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(char)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(int)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(decimal)), Is.EqualTo(explicitEnumerationConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(StringSplitOptions)), Is.EqualTo(explicitEnumerationConversion)); } [Test] public void NullableConversion_BasedOnIdentityConversion() { Assert.That(ExplicitConversion(typeof(ArraySegment?), typeof(ArraySegment?)), Is.EqualTo(C.IdentityConversion)); Assert.That(ExplicitConversion(typeof(ArraySegment), typeof(ArraySegment?)), Is.EqualTo(C.ImplicitNullableConversion)); Assert.That(ExplicitConversion(typeof(ArraySegment?), typeof(ArraySegment)), Is.EqualTo(C.ExplicitNullableConversion)); } [Test] public void NullableConversion_BasedOnImplicitNumericConversion() { Assert.That(ExplicitConversion(typeof(int?), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ExplicitConversion(typeof(int?), typeof(long)), Is.EqualTo(C.ExplicitLiftedNumericConversion)); } [Test] public void NullableConversion_BasedOnImplicitEnumerationConversion() { ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); Assert.That(conversions.ExplicitConversion(zero, compilation.FindType(typeof(StringComparison?))), Is.EqualTo(C.EnumerationConversion(true, true))); Assert.That(conversions.ExplicitConversion(one, compilation.FindType(typeof(StringComparison?))), Is.EqualTo(C.EnumerationConversion(false, true))); } [Test] public void NullableConversion_BasedOnExplicitNumericConversion() { Assert.That(ExplicitConversion(typeof(int?), typeof(short?)), Is.EqualTo(C.ExplicitLiftedNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(short?)), Is.EqualTo(C.ExplicitLiftedNumericConversion)); Assert.That(ExplicitConversion(typeof(int?), typeof(short)), Is.EqualTo(C.ExplicitLiftedNumericConversion)); } [Test] public void NullableConversion_BasedOnExplicitEnumerationConversion() { C c = C.EnumerationConversion(false, true); // c = explicit lifted enumeration conversion Assert.That(ExplicitConversion(typeof(int?), typeof(StringComparison?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(int), typeof(StringComparison?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(int?), typeof(StringComparison)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison?), typeof(int?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(int?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison?), typeof(int)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison?), typeof(StringSplitOptions?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(StringSplitOptions?)), Is.EqualTo(c)); Assert.That(ExplicitConversion(typeof(StringComparison?), typeof(StringSplitOptions)), Is.EqualTo(c)); } [Test] public void ExplicitReferenceConversion_SealedClass() { Assert.That(ExplicitConversion(typeof(object), typeof(string)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(string)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(string)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(string)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(string), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(string), typeof(IEnumerable)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(string), typeof(IEnumerable)), Is.EqualTo(C.None)); } [Test] public void ExplicitReferenceConversion_NonSealedClass() { Assert.That(ExplicitConversion(typeof(object), typeof(List)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(List)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(List)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(List)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(List), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(List), typeof(List)), Is.EqualTo(C.None)); } [Test] public void ExplicitReferenceConversion_Interfaces() { Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(IConvertible)), Is.EqualTo(C.ExplicitReferenceConversion)); } [Test] public void ExplicitReferenceConversion_Arrays() { Assert.That(ExplicitConversion(typeof(object[]), typeof(string[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(dynamic[]), typeof(string[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(object[]), typeof(object[,])), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(object[]), typeof(int[])), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(short[]), typeof(int[])), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Array), typeof(int[])), Is.EqualTo(C.ExplicitReferenceConversion)); } [Test] public void ExplicitReferenceConversion_InterfaceToArray() { Assert.That(ExplicitConversion(typeof(ICloneable), typeof(int[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(string[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(string[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(object[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(dynamic[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(int[])), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(object[,])), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(object[])), Is.EqualTo(C.None)); } [Test] public void ExplicitReferenceConversion_ArrayToInterface() { Assert.That(ExplicitConversion(typeof(int[]), typeof(ICloneable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(string[]), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(string[]), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(object[]), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(dynamic[]), typeof(IEnumerable)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(int[]), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(object[,]), typeof(IEnumerable)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(object[]), typeof(IEnumerable)), Is.EqualTo(C.None)); } [Test] public void ExplicitReferenceConversion_Delegates() { Assert.That(ExplicitConversion(typeof(MulticastDelegate), typeof(Action)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Delegate), typeof(Action)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(ICloneable), typeof(Action)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(System.Threading.ThreadStart), typeof(Action)), Is.EqualTo(C.None)); } [Test] public void ExplicitReferenceConversion_GenericDelegates() { Assert.That(ExplicitConversion(typeof(Action), typeof(Action)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Action), typeof(Action)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Action), typeof(Action)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Action), typeof(Action)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Action), typeof(Action>)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.ExplicitReferenceConversion)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func>)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Func), typeof(Func>)), Is.EqualTo(C.None)); } [Test] public void UnboxingConversion() { Assert.That(ExplicitConversion(typeof(object), typeof(int)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(object), typeof(decimal)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(ValueType), typeof(int)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(IFormattable), typeof(int)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(int)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Enum), typeof(StringComparison)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(Enum), typeof(int)), Is.EqualTo(C.None)); } [Test] public void LiftedUnboxingConversion() { Assert.That(ExplicitConversion(typeof(object), typeof(int?)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(object), typeof(decimal?)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(ValueType), typeof(int?)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(IFormattable), typeof(int?)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(IEnumerable), typeof(int?)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(Enum), typeof(StringComparison?)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(Enum), typeof(int?)), Is.EqualTo(C.None)); } /* TODO: we should probably revive these tests somehow Conversion ResolveCast(string program) { return Resolve(program).Conversion; } [Test] public void ObjectToTypeParameter() { string program = @"using System; class Test { public void M(object o) { T t = $(T)o$; } }"; Assert.AreEqual(C.UnboxingConversion, ResolveCast(program)); } [Test] public void UnrelatedClassToTypeParameter() { string program = @"using System; class Test { public void M(string o) { T t = $(T)o$; } }"; Assert.AreEqual(C.None, ResolveCast(program)); } [Test] public void IntefaceToTypeParameter() { string program = @"using System; class Test { public void M(IDisposable o) { T t = $(T)o$; } }"; Assert.AreEqual(C.UnboxingConversion, ResolveCast(program)); } [Test] public void TypeParameterToInterface() { string program = @"using System; class Test { public void M(T t) { IDisposable d = $(IDisposable)t$; } }"; Assert.AreEqual(C.BoxingConversion, ResolveCast(program)); } [Test] public void ValueTypeToTypeParameter() { string program = @"using System; class Test { public void M(ValueType o) where T : struct { T t = $(T)o$; } }"; Assert.AreEqual(C.UnboxingConversion, ResolveCast(program)); } [Test] public void InvalidTypeParameterConversion() { string program = @"using System; class Test { public void M(T t) { U u = $(U)t$; } }"; Assert.AreEqual(C.None, ResolveCast(program)); } [Test] public void TypeParameterConversion1() { string program = @"using System; class Test { public void M(T t) where T : U { U u = $(U)t$; } }"; Assert.AreEqual(C.BoxingConversion, ResolveCast(program)); } [Test] public void TypeParameterConversion1Array() { string program = @"using System; class Test { public void M(T[] t) where T : U { U[] u = $(U[])t$; } }"; Assert.AreEqual(C.None, ResolveCast(program)); } [Test] public void TypeParameterConversion2() { string program = @"using System; class Test { public void M(T t) where U : T { U u = $(U)t$; } }"; Assert.AreEqual(C.UnboxingConversion, ResolveCast(program)); } [Test] public void TypeParameterConversion2Array() { string program = @"using System; class Test { public void M(T[] t) where U : T { U[] u = $(U[])t$; } }"; Assert.AreEqual(C.None, ResolveCast(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T t) where T : class where U : class, T { U u = $(U)t$; } }"; Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T[] t) where T : class where U : class, T { U[] u = $(U[])t$; } }"; Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T t) where U : class, T { U u = $(U)t$; } }"; Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T[] t) where U : class, T { U[] u = $(U[])t$; } }"; Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program)); } [Test] public void SimpleUserDefinedConversion() { var rr = Resolve(@" class C1 {} class C2 { public static explicit operator C1(C2 c2) { return null; } } class C { public void M() { var c2 = new C2(); C1 c1 = $(C1)c2$; } }"); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("op_Explicit", rr.Conversion.Method.Name); } [Test] public void ExplicitReferenceConversionFollowedByUserDefinedConversion() { var rr = Resolve(@" class B {} class S : B {} class T { public static explicit operator T(S s) { return null; } } class Test { void Run(B b) { T t = $(T)b$; } }"); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("B", rr.Input.Type.Name); } [Test] public void ImplicitUserDefinedConversionFollowedByExplicitNumericConversion() { var rr = Resolve(@" struct T { public static implicit operator float(T t) { return 0; } } class Test { void Run(T t) { int x = $(int)t$; } }"); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); // even though the user-defined conversion is implicit, the combined conversion is explicit Assert.That(rr.Conversion.IsExplicit); } [Test] public void BothDirectConversionAndBaseClassConversionAvailable() { var rr = Resolve(@" class B {} class S : B {} class T { public static explicit operator T(S s) { return null; } public static explicit operator T(B b) { return null; } } class Test { void Run(B b) { T t = $(T)b$; } }"); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("b", rr.Conversion.Method.Parameters.Single().Name); } [Test] public void UserDefinedExplicitConversion_PicksExactSourceTypeIfPossible() { string program = @"using System; class Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } public static explicit operator Convertible(short s) {return new Convertible(); } } class Test { public void M() { var a = $(Convertible)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_PicksMostEncompassedSourceTypeIfPossible() { string program = @"using System; class Convertible { public static explicit operator Convertible(long l) {return new Convertible(); } public static explicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { var a = $(Convertible)(ushort)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_PicksMostEncompassingSourceType() { string program = @"using System; class Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } public static explicit operator Convertible(ushort us) {return new Convertible(); } } class Test { public void M() { var a = $(Convertible)(long)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_NoMostEncompassingSourceTypeIsInvalid() { string program = @"using System; class Convertible { public static explicit operator Convertible(uint i) {return new Convertible(); } public static explicit operator Convertible(short us) {return new Convertible(); } } class Test { public void M() { var a = $(Convertible)(long)33$; } }"; var rr = Resolve(program); Assert.That(!rr.Conversion.IsValid); } [Test] public void UserDefinedExplicitConversion_PicksExactTargetTypeIfPossible() { string program = @"using System; class Convertible { public static explicit operator int(Convertible i) {return 0; } public static explicit operator short(Convertible s) {return 0; } } class Test { public void M() { var a = $(int)new Convertible()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_PicksMostEncompassingTargetTypeIfPossible() { string program = @"using System; class Convertible { public static explicit operator int(Convertible i) {return 0; } public static explicit operator ushort(Convertible us) {return 0; } } class Test { public void M() { var a = $(ulong)new Convertible()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("us", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_PicksMostEncompassedTargetType() { string program = @"using System; class Convertible { public static explicit operator long(Convertible l) { return 0; } public static explicit operator uint(Convertible ui) { return 0; } } class Test { public void M() { var a = $(ushort)new Convertible()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_NoMostEncompassedTargetTypeIsInvalid() { string program = @"using System; class Convertible { public static explicit operator ulong(Convertible l) { return 0; } public static explicit operator int(Convertible ui) { return 0; } } class Test { public void M() { var a = $(ushort)new Convertible()$; } }"; var rr = Resolve(program); Assert.That(!rr.Conversion.IsValid); } [Test] public void UserDefinedExplicitConversion_AmbiguousIsInvalid() { string program = @"using System; class Convertible1 { public static explicit operator Convertible2(Convertible1 c) {return 0; } } class Convertible2 { public static explicit operator Convertible2(Convertible1 c) {return 0; } } class Test { public void M() { var a = $(Convertible2)new Convertible1()$; } }"; var rr = Resolve(program); Assert.That(!rr.Conversion.IsValid); } [Test] public void UserDefinedExplicitConversion_Lifted() { string program = @"using System; struct Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } } class Test { public void M(int? i) { a = $(Convertible?)i$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.That(rr.Conversion.IsLifted); } [Test] public void UserDefinedExplicitConversionFollowedByImplicitNullableConversion() { string program = @"using System; struct Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } } class Test { public void M(int i) { a = $(Convertible?)i$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.That(!rr.Conversion.IsLifted); } [Test] public void UserDefinedExplicitConversion_ExplicitNullable_ThenUserDefined() { string program = @"using System; struct Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } public static explicit operator Convertible?(int? ni) {return new Convertible(); } } class Test { public void M(int? i) { a = $(Convertible)i$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.That(!rr.Conversion.IsLifted); Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_DefinedNullableTakesPrecedenceOverLifted() { string program = @"using System; struct Convertible { public static explicit operator Convertible(int i) {return new Convertible(); } public static explicit operator Convertible?(int? ni) {return new Convertible(); } } class Test { public void M() { a = $(Convertible?)(int?)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.That(!rr.Conversion.IsLifted); Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_UIntConstant() { string program = @"using System; class Convertible { public static explicit operator Convertible(long l) {return new Convertible(); } public static explicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { var a = $(Convertible)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_NullableUIntConstant() { string program = @"using System; class Convertible { public static explicit operator Convertible(long? l) {return new Convertible(); } public static explicit operator Convertible(uint? ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $(Convertible)33$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UseDefinedExplicitConversion_Lifted() { string program = @" struct Convertible { public static explicit operator Convertible(int i) { return new Convertible(); } } class Test { public void M(int? i) { a = $(Convertible?)i$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.That(rr.Conversion.IsLifted); Assert.That(rr.Input is LocalResolveResult); } [Test] public void UserDefinedExplicitConversion_Short_Or_NullableByte_Target() { string program = @"using System; class Test { public static explicit operator short(Test s) { return 0; } public static explicit operator byte?(Test b) { return 0; } } class Program { public static void Main(string[] args) { int? x = $(int?)new Test()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("System.Int16", rr.Conversion.Method.ReturnType.FullName); } [Test] public void UserDefinedExplicitConversion_Byte_Or_NullableShort_Target() { string program = @"using System; class Test { public static explicit operator byte(Test b) { return 0; } public static explicit operator short?(Test s) { return 0; } } class Program { public static void Main(string[] args) { int? x = $(int?)new Test()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("s", rr.Conversion.Method.Parameters[0].Name); } [Test] public void ExplicitConversionOperatorsCanOverrideApplicableImplicitOnes() { string program = @" struct Convertible { public static explicit operator int(Convertible ci) {return 0; } public static implicit operator short(Convertible cs) {return 0; } } class Test { static void Main() { int i = $(int)new Convertible()$; // csc uses the explicit conversion operator } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.IsUserDefined); Assert.AreEqual("ci", rr.Conversion.Method.Parameters[0].Name); } [Test] public void UserDefinedExplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator Convertible(int l) {return new Convertible(); } } class Test { public void M() { long i = 33; Convertible a = $(Convertible)i$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsValid); Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsExplicit); Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsNumericConversion); Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsIdentityConversion); } [Test] public void UserDefinedExplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator long(Convertible i) {return 0; } } class Test { public void M() { int a = $(int)new Convertible()$; } }"; var rr = Resolve(program); Assert.That(rr.Conversion.IsValid); Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsIdentityConversion); Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsValid); Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsExplicit); Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsNumericConversion); }*/ } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Semantics/OverloadResolutionTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using System.Linq.Expressions; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Semantics { [TestFixture, Parallelizable(ParallelScope.All)] public class OverloadResolutionTests { ICompilation compilation; [OneTimeSetUp] public void SetUp() { compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib, TypeSystemLoaderTests.SystemCore); } ResolveResult[] MakeArgumentList(params Type[] argumentTypes) { return argumentTypes.Select(t => new ResolveResult(compilation.FindType(t))).ToArray(); } IMethod MakeMethod(params object[] parameterTypesOrDefaultValues) { var context = new SimpleTypeResolveContext(compilation.MainModule); var m = new FakeMethod(compilation, SymbolKind.Method); m.Name = "Method"; var parameters = new List(); foreach (var typeOrDefaultValue in parameterTypesOrDefaultValues) { Type type = typeOrDefaultValue as Type; if (type != null) parameters.Add(new DefaultParameter(compilation.FindType(type), string.Empty, owner: m)); else if (Type.GetTypeCode(typeOrDefaultValue.GetType()) > TypeCode.Object) parameters.Add(new DefaultParameter(compilation.FindType(typeOrDefaultValue.GetType()), string.Empty, owner: m, isOptional: true, defaultValue: typeOrDefaultValue)); else throw new ArgumentException(typeOrDefaultValue.ToString()); } m.Parameters = parameters; return m; } IMethod MakeParamsMethod(params object[] parameterTypesOrDefaultValues) { var m = (FakeMethod)MakeMethod(parameterTypesOrDefaultValues); var parameters = m.Parameters.ToList(); parameters[parameters.Count - 1] = new DefaultParameter( parameters.Last().Type, parameters.Last().Name, isParams: true); m.Parameters = parameters; return m; } [Test] public void PreferIntOverUInt() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(ushort))); var c1 = MakeMethod(typeof(int)); Assert.That(r.AddCandidate(c1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(MakeMethod(typeof(uint))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(!r.IsAmbiguous); Assert.That(r.BestCandidate, Is.SameAs(c1)); } [Test] public void PreferUIntOverLong_FromIntLiteral() { ResolveResult[] args = { new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1) }; OverloadResolution r = new OverloadResolution(compilation, args); var c1 = MakeMethod(typeof(uint)); Assert.That(r.AddCandidate(c1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(MakeMethod(typeof(long))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(!r.IsAmbiguous); Assert.That(r.BestCandidate, Is.SameAs(c1)); } [Test] public void NullableIntAndNullableUIntIsAmbiguous() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(ushort?))); Assert.That(r.AddCandidate(MakeMethod(typeof(int?))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(MakeMethod(typeof(uint?))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateErrors, Is.EqualTo(OverloadResolutionErrors.AmbiguousMatch)); // then adding a matching overload solves the ambiguity: Assert.That(r.AddCandidate(MakeMethod(typeof(ushort?))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateErrors, Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateAmbiguousWith, Is.Null); } [Test] public void ParamsMethodMatchesEmptyArgumentList() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList()); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int[]))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateIsExpandedForm); } [Test] public void ParamsMethodMatchesOneArgumentInExpandedForm() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int))); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int[]))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateIsExpandedForm); } [Test] public void ParamsMethodMatchesInUnexpandedForm() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int[]))); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int[]))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(!r.BestCandidateIsExpandedForm); } [Test] public void LessArgumentsPassedToParamsIsBetter() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int), typeof(int), typeof(int))); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int[]))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int), typeof(int[]))), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(!r.IsAmbiguous); Assert.That(r.BestCandidate.Parameters.Count, Is.EqualTo(2)); } [Test] public void CallInvalidParamsDeclaration() { OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int[,]))); Assert.That(r.AddCandidate(MakeParamsMethod(typeof(int))), Is.EqualTo(OverloadResolutionErrors.ArgumentTypeMismatch)); Assert.That(!r.BestCandidateIsExpandedForm); } [Test] public void PreferMethodWithoutOptionalParameters() { var m1 = MakeMethod(); var m2 = MakeMethod(1); OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList()); Assert.That(r.AddCandidate(m1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(m2), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(!r.IsAmbiguous); Assert.That(r.BestCandidate, Is.SameAs(m1)); } [Test] public void SkeetEvilOverloadResolution() { // http://msmvps.com/blogs/jon_skeet/archive/2010/11/02/evil-code-overload-resolution-workaround.aspx var container = compilation.FindType(typeof(SkeetEvilOverloadResolutionTestCase)).GetDefinition(); IMethod resolvedM1 = container.GetMethods(m => m.Name == "Foo").First(); IMethod resolvedM2 = container.GetMethods(m => m.Name == "Foo").Skip(1).First(); IMethod resolvedM3 = container.GetMethods(m => m.Name == "Foo").Skip(2).First(); // Call: Foo(); OverloadResolution o; o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(int)) }); Assert.That(o.AddCandidate(resolvedM1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(o.AddCandidate(resolvedM2), Is.EqualTo(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint)); Assert.That(o.BestCandidate, Is.SameAs(resolvedM1)); // Call: Foo(); o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(string)) }); Assert.That(o.AddCandidate(resolvedM1), Is.EqualTo(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint)); Assert.That(o.AddCandidate(resolvedM2), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(o.BestCandidate, Is.SameAs(resolvedM2)); // Call: Foo(); o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(int?)) }); Assert.That(o.AddCandidate(resolvedM1), Is.EqualTo(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint)); Assert.That(o.AddCandidate(resolvedM2), Is.EqualTo(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint)); Assert.That(o.AddCandidate(resolvedM3), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(o.BestCandidate, Is.SameAs(resolvedM3)); } class SkeetEvilOverloadResolutionTestCase { class ClassConstraint where T : class { } static void Foo(T? ignored = default(T?)) where T : struct { } static void Foo(ClassConstraint ignored = default(ClassConstraint)) where T : class { } static void Foo() { } } /// /// A lambda of the form "() => default(returnType)" /// class MockLambda : LambdaResolveResult { readonly IType inferredReturnType; internal readonly List parameters = new List(); public MockLambda(IType returnType) { this.inferredReturnType = returnType; } public override IReadOnlyList Parameters { get { return parameters; } } public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions) { return conversions.ImplicitConversion(inferredReturnType, returnType); } public override bool IsImplicitlyTyped { get { return false; } } public override bool IsAnonymousMethod { get { return false; } } public override bool HasParameterList { get { return true; } } public override bool IsAsync { get { return false; } } public override ResolveResult Body { get { throw new NotImplementedException(); } } public override IType ReturnType { get { throw new NotImplementedException(); } } public override IType GetInferredReturnType(IType[] parameterTypes) { return inferredReturnType; } } [Test] public void BetterConversionByLambdaReturnValue() { var m1 = MakeMethod(typeof(Func)); var m2 = MakeMethod(typeof(Func)); // M(() => default(byte)); ResolveResult[] args = { new MockLambda(compilation.FindType(KnownTypeCode.Byte)) }; OverloadResolution r = new OverloadResolution(compilation, args); Assert.That(r.AddCandidate(m1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(m2), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidate, Is.SameAs(m2)); Assert.That(r.BestCandidateErrors, Is.EqualTo(OverloadResolutionErrors.None)); } [Test] public void BetterConversionByLambdaReturnValue_ExpressionTree() { var m1 = MakeMethod(typeof(Func)); var m2 = MakeMethod(typeof(Expression>)); // M(() => default(byte)); ResolveResult[] args = { new MockLambda(compilation.FindType(KnownTypeCode.Byte)) }; OverloadResolution r = new OverloadResolution(compilation, args); Assert.That(r.AddCandidate(m1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(m2), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidate, Is.SameAs(m2)); Assert.That(r.BestCandidateErrors, Is.EqualTo(OverloadResolutionErrors.None)); } [Test] public void Lambda_DelegateAndExpressionTreeOverloadsAreAmbiguous() { var m1 = MakeMethod(typeof(Func)); var m2 = MakeMethod(typeof(Expression>)); // M(() => default(int)); ResolveResult[] args = { new MockLambda(compilation.FindType(KnownTypeCode.Int32)) }; OverloadResolution r = new OverloadResolution(compilation, args); Assert.That(r.AddCandidate(m1), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.AddCandidate(m2), Is.EqualTo(OverloadResolutionErrors.None)); Assert.That(r.BestCandidateErrors, Is.EqualTo(OverloadResolutionErrors.AmbiguousMatch)); } [Test, Ignore("Overload Resolution bug")] public void BetterFunctionMemberIsNotTransitive() { var container = compilation.FindType(typeof(BetterFunctionMemberIsNotTransitiveTestCase)).GetDefinition(); var args = new ResolveResult[] { new MockLambda(compilation.FindType(KnownTypeCode.String)) { parameters = { new DefaultParameter(SpecialType.UnknownType, "arg") } } }; OverloadResolution r = new OverloadResolution(compilation, args); foreach (var method in container.GetMethods(m => m.Name == "Method")) { Assert.That(r.AddCandidate(method), Is.EqualTo(OverloadResolutionErrors.None)); } Assert.That(r.BestCandidate, Is.EqualTo(container.GetMethods(m => m.Name == "Method").Last())); } class BetterFunctionMemberIsNotTransitiveTestCase { static void Method(Action a) { } static void Method(Func a) { } static void Method(Action a) { } static void Method(Func a) { } public static void Main(string[] args) { Method(a => a.ToString()); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs ================================================ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.Tests { sealed class TestAssemblyResolver : UniversalAssemblyResolver { readonly HashSet localAssemblies = new HashSet(); public TestAssemblyResolver(string mainAssemblyFileName, string baseDir, string targetFramework) : base(mainAssemblyFileName, false, targetFramework, null, PEStreamOptions.PrefetchMetadata, MetadataReaderOptions.ApplyWindowsRuntimeProjections) { var assemblyNames = new DirectoryInfo(baseDir).EnumerateFiles("*.dll").Select(f => Path.GetFileNameWithoutExtension(f.Name)); foreach (var name in assemblyNames) { localAssemblies.Add(name); } } public override bool IsGacAssembly(IAssemblyReference reference) { return reference != null && !localAssemblies.Contains(reference.Name); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Async.cs ================================================ // Copyright (c) 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 // 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. #pragma warning disable 1998 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class Async { public static void Main() { new Async().Run().Wait(); } public async Task Run() { Console.WriteLine("SimpleBoolTaskMethod:"); await SimpleBoolTaskMethod(); Console.WriteLine("StreamCopyTo:"); StreamCopyTo(new MemoryStream(new byte[1024]), 16); Console.WriteLine("StreamCopyToWithConfigureAwait:"); StreamCopyToWithConfigureAwait(new MemoryStream(new byte[1024]), 16); Console.WriteLine("AwaitInForEach:"); await AwaitInForEach(Enumerable.Range(0, 100).Select(i => Task.FromResult(i))); Console.WriteLine("TaskMethodWithoutAwaitButWithExceptionHandling:"); await TaskMethodWithoutAwaitButWithExceptionHandling(); #if CS60 Console.WriteLine($"{nameof(AwaitCatch)}:"); await AwaitCatch(Task.FromResult(1)); Console.WriteLine($"{nameof(AwaitMultipleCatchBlocks)}:"); await AwaitMultipleCatchBlocks(Task.FromResult(1)); Console.WriteLine($"{nameof(AwaitMultipleCatchBlocks2)}:"); await AwaitMultipleCatchBlocks2(Task.FromResult(1)); Console.WriteLine($"{nameof(AwaitInComplexFinally)}:"); Console.WriteLine(await AwaitInComplexFinally()); try { Console.WriteLine($"{nameof(AwaitFinally)}:"); await AwaitFinally(Task.FromResult(2)); } catch (Exception ex) { Console.WriteLine(ex + " caught!"); } #endif Console.WriteLine("NestedAwait:"); await NestedAwait(Task.FromResult(Task.FromResult(5))); Console.WriteLine("AwaitWithStack:"); await AwaitWithStack(Task.FromResult(3)); Console.WriteLine("AwaitWithStack2:"); await AwaitWithStack2(Task.FromResult(4)); #if CS60 Console.WriteLine($"{nameof(AwaitInCatch)}:"); await AwaitInCatch(Task.FromResult(1), Task.FromResult(2)); Console.WriteLine($"{nameof(AwaitInFinally)}:"); await AwaitInFinally(Task.FromResult(2), Task.FromResult(4)); Console.WriteLine($"{nameof(AwaitInCatchAndFinally)}:"); await AwaitInCatchAndFinally(Task.FromResult(3), Task.FromResult(6), Task.FromResult(9)); Console.WriteLine($"{nameof(AwaitInFinallyInUsing)}:"); Console.WriteLine(await AwaitInFinallyInUsing(Task.FromResult(new StringWriter()), Task.FromResult(6), Task.FromResult(9))); #endif } public async Task SimpleBoolTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); return true; } public async void StreamCopyTo(Stream destination, int bufferSize) { Console.WriteLine("Before"); byte[] array = new byte[bufferSize]; int count; Console.WriteLine("BeforeLoop"); while ((count = await destination.ReadAsync(array, 0, array.Length)) != 0) { Console.WriteLine("In Loop after condition!"); await destination.WriteAsync(array, 0, count); Console.WriteLine("In Loop after inner await"); } Console.WriteLine("After"); } public async void StreamCopyToWithConfigureAwait(Stream destination, int bufferSize) { Console.WriteLine("Before"); byte[] array = new byte[bufferSize]; int count; Console.WriteLine("Before Loop"); while ((count = await destination.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) { Console.WriteLine("Before Inner Await"); await destination.WriteAsync(array, 0, count).ConfigureAwait(false); Console.WriteLine("After Inner Await"); } Console.WriteLine("After"); } public async Task AwaitInForEach(IEnumerable> elements) { int num = 0; Console.WriteLine("Before Loop"); foreach (Task current in elements) { Console.WriteLine("Before Inner Await"); num += await current; Console.WriteLine("After Inner Await"); } Console.WriteLine("After"); return num; } public async Task TaskMethodWithoutAwaitButWithExceptionHandling() { try { using (new StringWriter()) { Console.WriteLine("No Await"); } } catch (Exception) { Console.WriteLine("Crash"); } } #if CS60 public async Task AwaitCatch(Task task) { try { Console.WriteLine("Before throw"); throw new Exception(); } catch { Console.WriteLine(await task); } } public async Task AwaitMultipleCatchBlocks(Task task) { try { Console.WriteLine("Before throw"); throw new Exception(); } catch (OutOfMemoryException ex) { Console.WriteLine(ex.ToString()); Console.WriteLine(await task); } catch { Console.WriteLine(await task); } } public async Task AwaitMultipleCatchBlocks2(Task task) { try { Console.WriteLine("Before throw"); throw new Exception(); } catch (OutOfMemoryException ex) { Console.WriteLine(ex.ToString()); Console.WriteLine(await task); } catch (InternalBufferOverflowException ex) { Console.WriteLine(ex.ToString()); } catch { Console.WriteLine(await task); } } public async Task AwaitFinally(Task task) { try { Console.WriteLine("Before throw"); throw new Exception(); } finally { Console.WriteLine(await task); } } #endif public async Task NestedAwait(Task> task) { return await (await task); } public async Task AwaitWithStack(Task task) { Console.WriteLine("A", 1, await task); } public async Task AwaitWithStack2(Task task) { if (await this.SimpleBoolTaskMethod()) { Console.WriteLine("A", 1, await task); } else { int num = 1; Console.WriteLine("A", 1, num); } } #if CS60 public async Task AwaitInCatch(Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception) { Console.WriteLine("Start catch"); await task2; Console.WriteLine("End catch"); } Console.WriteLine("End Method"); } public async Task AwaitInFinally(Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } finally { Console.WriteLine("Start finally"); await task2; Console.WriteLine("End finally"); } Console.WriteLine("End Method"); } public static async Task AwaitInComplexFinally() { Console.WriteLine("a"); try { Console.WriteLine("b"); await Task.Delay(1); Console.WriteLine("c"); } catch (Exception ex) { await Task.Delay(ex.HResult); } finally { Console.WriteLine("d"); int i = 0; if (Console.CapsLock) { i++; await Task.Delay(i); } else { while (i < 5) { Console.WriteLine("i: " + i); i++; } } Console.WriteLine("e"); } Console.WriteLine("f"); return 1; } public async Task AwaitInCatchAndFinally(Task task1, Task task2, Task task3) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception ex) { Console.WriteLine("Start catch"); await task2; Console.WriteLine("End catch"); } finally { Console.WriteLine("Start finally"); await task3; Console.WriteLine("End finally"); } Console.WriteLine("End Method"); } public async Task AwaitInFinallyInUsing(Task task1, Task task2, Task task3) { using (await task1) { Console.WriteLine("Start using"); try { Console.WriteLine("Before return"); return await task2; } finally { Console.WriteLine("Start finally"); await task3; Console.WriteLine("End finally"); } } } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/BitNot.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly 'ConvTest' { .ver 0:0:0:0 } .module ConvTest.exe .corflags 0x00000001 // ILOnly .class private auto ansi abstract sealed beforefieldinit Program extends [mscorlib]System.Object { .method public hidebysig static void Main (string[] args) cil managed { .maxstack 8 .entrypoint ldc.i8 0xCCCCCCCCCCCCCCCC call int64 Program::BitwiseComplementInNativeSize(int64) call void Program::PrintHex(int64) ldc.i4 0x99999999 call int64 Program::BitwiseComplementWithUndersizedValue(int32) call void Program::PrintHex(int64) ldc.i4 0x9999 call int32 Program::BitwiseComplementWithSmallInteger(uint16) call void Program::PrintHex(int32) ldc.i4 0x9999 call int32 Program::BitwiseComplementWithSmallEnum(valuetype Enums.EUInt16) call void Program::PrintHex(int32) ret } // end of method Main .method public static int64 BitwiseComplementInNativeSize(int64 val) { ldarg.0 conv.i // truncate 64-bits to native-size bits not // negate those native size bits conv.i8 // sign extend back to 64-bits ret } .method public static int64 BitwiseComplementWithUndersizedValue(int32 val) { ldarg.0 conv.u8 // zero extend up to 64-bits not // negate those 64-bits ret } .method public static int32 BitwiseComplementWithSmallInteger(uint16 val) { ldarg.0 // zero extend up to 32-bits not // negate those 32-bits ret } .method public static int32 BitwiseComplementWithSmallEnum(valuetype Enums.EUInt16 val) { ldarg.0 // zero extend up to 32-bits not // negate those 32-bits ret } .method public static void PrintHex(int32 val) { ldstr "{0:x8}" ldarg.0 box valuetype [mscorlib]System.UInt32 call void [mscorlib]System.Console::WriteLine(string, object) ret } .method public static void PrintHex(int64 val) { ldstr "{0:x16}" ldarg.0 box valuetype [mscorlib]System.UInt64 call void [mscorlib]System.Console::WriteLine(string, object) ret } } .class public auto ansi sealed Enums.EUInt16 extends [mscorlib]System.Enum { .field public specialname uint16 __value } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Capturing.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class Capturing { static void Main(string[] args) { TestCase1(); TestCase2(); TestCase3(); TestCase4("TestCase4"); OutsideLoop(); InsideLoop(); OutsideLoopOverArray(); OutsideLoopOverArray2(); InsideLoopOverArray2(); NotWhileDueToVariableInsideLoop(); NotDoWhileDueToVariableInsideLoop(); Issue1936(); } static void TestCase1() { Console.WriteLine("TestCase1"); for (int i = 0; i < 10; i++) Console.WriteLine(i); // i no longer declared List actions = new List(); int max = 5; string line; while (ReadLine(out line, ref max)) { actions.Add(() => Console.WriteLine(line)); } // line still declared line = null; Console.WriteLine("----"); foreach (var action in actions) action(); } static void TestCase2() { Console.WriteLine("TestCase2"); List actions = new List(); int max = 5; string line; while (ReadLine(out line, ref max)) { string capture = line; actions.Add(() => Console.WriteLine(capture)); } // line still declared line = null; Console.WriteLine("----"); foreach (var action in actions) action(); } static void TestCase3() { Console.WriteLine("TestCase3"); List actions = new List(); int max = 5; string line, capture; while (ReadLine(out line, ref max)) { capture = line; actions.Add(() => Console.WriteLine(capture)); } // line still declared line = null; Console.WriteLine("----"); foreach (var action in actions) action(); } static void TestCase4(string capture) { Console.WriteLine("TestCase4"); List actions = new List(); actions.Add(() => Console.WriteLine(capture)); Console.WriteLine("----"); foreach (var action in actions) action(); } private static bool ReadLine(out string line, ref int v) { line = v + " line"; return --v > 0; } static void OutsideLoop() { Console.WriteLine("OutsideLoop"); var list = new List { 1, 2, 3 }; var functions = new List>(); using (var e = list.GetEnumerator()) { int val; // declared outside loop // The decompiler cannot convert this to a foreach-loop without // changing the lambda capture semantics. while (e.MoveNext()) { val = e.Current; functions.Add(() => val); } } foreach (var func in functions) { Console.WriteLine(func()); } } static void InsideLoop() { Console.WriteLine("InsideLoop"); var list = new List { 1, 2, 3 }; var functions = new List>(); using (var e = list.GetEnumerator()) { while (e.MoveNext()) { int val = e.Current; functions.Add(() => val); } } foreach (var func in functions) { Console.WriteLine(func()); } } static void OutsideLoopOverArray() { Console.WriteLine("OutsideLoopOverArray:"); var functions = new List>(); var array = new int[] { 1, 2, 3 }; int val; // declared outside loop // The decompiler cannot convert this to a foreach-loop without // changing the lambda capture semantics. for (int i = 0; i < array.Length; ++i) { val = array[i]; functions.Add(() => val); } foreach (var func in functions) { Console.WriteLine(func()); } } static void OutsideLoopOverArray2() { Console.WriteLine("OutsideLoopOverArray2:"); var functions = new List>(); var array = new int[] { 1, 2, 3 }; int val; // declared outside loop // The decompiler can convert this to a foreach-loop, but the 'val' // variable must be declared outside. for (int i = 0; i < array.Length; ++i) { int element = array[i]; val = element * 2; functions.Add(() => val); } foreach (var func in functions) { Console.WriteLine(func()); } } static void InsideLoopOverArray2() { Console.WriteLine("InsideLoopOverArray2:"); var functions = new List>(); var array = new int[] { 1, 2, 3 }; for (int i = 0; i < array.Length; ++i) { int element = array[i]; int val = element * 2; functions.Add(() => val); } foreach (var func in functions) { Console.WriteLine(func()); } } static int nextVal; static int GetVal() { return ++nextVal & 7; } static void NotWhileDueToVariableInsideLoop() { Console.WriteLine("NotWhileDueToVariableInsideLoop:"); var functions = new List>(); while (true) { int v; if ((v = GetVal()) == 0) break; functions.Add(() => v); } foreach (var f in functions) { Console.WriteLine(f()); } } static void NotDoWhileDueToVariableInsideLoop() { Console.WriteLine("NotDoWhileDueToVariableInsideLoop:"); var functions = new List>(); while (true) { int v = GetVal(); functions.Add(() => v); if (v == 0) break; } foreach (var f in functions) { Console.WriteLine(f()); } } public static void Issue1936() { IEnumerable outerCapture = null; for (int i = 0; i < 10; i++) { int innerCapture = 0; Action a = (delegate { List list = new List(); Console.WriteLine("before inc: " + innerCapture); ++innerCapture; Console.WriteLine("after inc: " + innerCapture); Console.WriteLine("before assign: " + outerCapture); outerCapture = outerCapture == null ? list : outerCapture.Concat(list); Console.WriteLine("after assign: " + outerCapture); }); a.Invoke(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/ComInterop.cs ================================================ using System; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class ComInterop { public static void Main() { Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetMethod("MyMethod1"))); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetProperty("MyProperty1").GetMethod)); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetMethod("MyMethod2"))); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetProperty("MyProperty2").GetMethod)); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetProperty("MyProperty2").SetMethod)); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetEvent("MyEvent1").AddMethod)); Console.WriteLine(Marshal.GetComSlotForMethodInfo(typeof(IMixedPropsAndMethods).GetEvent("MyEvent1").RemoveMethod)); } [Guid("761618B8-3994-449A-A96B-F1FF2795EA85")] [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IMixedPropsAndMethods { int MyMethod1(); int MyProperty1 { get; } int MyOverload(); int MyMethod2(); int MyProperty2 { get; set; } int MyOverload(int x); event EventHandler MyEvent1; int MyMethod3(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Comparisons.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Globalization; #pragma warning disable 652 namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class Comparisons { public static int Main() { #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator TestFloatOp("==", (a, b) => a == b); TestFloatOp("!=", (a, b) => a != b); TestFloatOp("<", (a, b) => a < b); TestFloatOp(">", (a, b) => a > b); TestFloatOp("<=", (a, b) => a <= b); TestFloatOp(">=", (a, b) => a >= b); TestFloatOp("!<", (a, b) => !(a < b)); TestFloatOp("!>", (a, b) => !(a > b)); TestFloatOp("!<=", (a, b) => !(a <= b)); TestFloatOp("!>=", (a, b) => !(a >= b)); TestUInt(0); TestUInt(uint.MaxValue); TestUShort(0); TestUShort(ushort.MaxValue); Console.WriteLine("Issue2398:"); Issue2398(0x100000000); Console.WriteLine("OverloadedOperators:"); Console.WriteLine(IsNotNull(new OverloadedOperators())); Console.WriteLine(IsNull(new OverloadedOperators())); Console.WriteLine(NullIs(new OverloadedOperators())); Console.WriteLine(NullIsNot(new OverloadedOperators())); return 0; } static void TestFloatOp(string name, Func f) { float[] vals = { -1, 0, 3, float.PositiveInfinity, float.NaN }; for (int i = 0; i < vals.Length; i++) { for (int j = 0; j < vals.Length; j++) { Console.WriteLine("Float: {0} {1} {2:r} = {3}", vals[i].ToString("r", CultureInfo.InvariantCulture), name, vals[j].ToString("r", CultureInfo.InvariantCulture), f(vals[i], vals[j])); } } } static T Id(T arg) { return arg; } static void TestUShort(ushort i) { Console.WriteLine("ushort: {0} == ushort.MaxValue = {1}", i, i == ushort.MaxValue); Console.WriteLine("ushort: {0} == -1 = {1}", i, i == -1); Console.WriteLine("ushort: {0} == Id(-1) = {1}", i, i == Id(-1)); Console.WriteLine("ushort: {0} == 0x1ffff = {1}", i, i == 0x1ffff); } static void TestUInt(uint i) { Console.WriteLine("uint: {0} == uint.MaxValue = {1}", i, i == uint.MaxValue); Console.WriteLine("uint: {0} == Id(uint.MaxValue) = {1}", i, i == Id(uint.MaxValue)); Console.WriteLine("uint: {0} == -1 = {1}", i, i == -1); Console.WriteLine("uint: {0} == Id(-1) = {1}", i, i == Id(-1)); } static void Issue2398(long value) { if ((int)value != 0) Console.WriteLine("TRUE"); } static bool IsNull(OverloadedOperators oo) { return (object)oo == null; } static bool IsNotNull(OverloadedOperators oo) { return (object)oo != null; } static bool NullIs(OverloadedOperators oo) { return null == (object)oo; } static bool NullIsNot(OverloadedOperators oo) { return null != (object)oo; } } #pragma warning disable CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) #pragma warning disable CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() class OverloadedOperators { public static bool operator ==(OverloadedOperators oo, object b) { throw new NotSupportedException("Not supported to call the user-defined operator"); } public static bool operator !=(OverloadedOperators oo, object b) { throw new NotSupportedException("Not supported to call the user-defined operator"); } public static bool operator ==(object oo, OverloadedOperators b) { throw new NotSupportedException("Not supported to call the user-defined operator"); } public static bool operator !=(object oo, OverloadedOperators b) { throw new NotSupportedException("Not supported to call the user-defined operator"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class CompoundAssignment { static void Main() { PreIncrementProperty(); PreIncrementIndexer(); CallTwice(); UnsignedShiftRightInstanceField(); UnsignedShiftRightStaticProperty(); DivideByBigValue(); Overflow(); IntPtr_CompoundAssign(); } static void Test(int a, int b) { Console.WriteLine("{0} {1}", a, b); } static int x; static int X() { Console.Write("X "); return ++x; } static int instanceCount; int instanceNumber = ++instanceCount; int instanceField; public int InstanceProperty { get { Console.WriteLine("In {0}.get_InstanceProperty", instanceNumber); return instanceField; } set { Console.WriteLine("In {0}.set_InstanceProperty, value=" + value, instanceNumber); instanceField = value; } } static int staticField; public static int StaticProperty { get { Console.WriteLine("In get_StaticProperty"); return staticField; } set { Console.WriteLine("In set_StaticProperty, value=" + value); staticField = value; } } static short shortField; public static short ShortProperty { get { Console.WriteLine("In get_ShortProperty"); return shortField; } set { Console.WriteLine("In set_ShortProperty, value={0}", value); shortField = value; } } static byte byteField; public static byte ByteProperty { get { Console.WriteLine("In get_ByteProperty"); return byteField; } set { Console.WriteLine("In set_ByteProperty, value={0}", value); byteField = value; } } IntPtr intPtrField = new IntPtr(IntPtr.Size == 8 ? long.MaxValue : int.MaxValue); public IntPtr IntPtrProperty { get { Console.WriteLine("In {0}.get_IntPtrProperty", instanceNumber); return intPtrField; } set { Console.WriteLine("In {0}.set_IntPtrProperty, value={1}", instanceNumber, value); intPtrField = value; } } public static Dictionary GetDict() { Console.WriteLine("In GetDict()"); return new Dictionary() { { GetString(), 5 } }; } static CompoundAssignment GetObject() { var obj = new CompoundAssignment(); Console.WriteLine("In GetObject() (instance #{0})", obj.instanceNumber); return obj; } static string GetString() { Console.WriteLine("In GetString()"); return "the string"; } static void PreIncrementProperty() { Console.WriteLine("PreIncrementProperty:"); Test(X(), ++new CompoundAssignment().InstanceProperty); Test(X(), ++StaticProperty); } static void PreIncrementIndexer() { Console.WriteLine("PreIncrementIndexer:"); Test(X(), ++GetDict()[GetString()]); } static void CallTwice() { Console.WriteLine("CallTwice: instanceField:"); GetObject().instanceField = GetObject().instanceField + 1; Test(X(), GetObject().instanceField = GetObject().instanceField + 1); Console.WriteLine("CallTwice: InstanceProperty:"); GetObject().InstanceProperty = GetObject().InstanceProperty + 1; Test(X(), GetObject().InstanceProperty = GetObject().InstanceProperty + 1); Console.WriteLine("CallTwice: dict indexer:"); GetDict()[GetString()] = GetDict()[GetString()] + 1; Test(X(), GetDict()[GetString()] = GetDict()[GetString()] + 1); } static void UnsignedShiftRightInstanceField() { #if CS70 ref int f = ref new CompoundAssignment().instanceField; Test(X(), f = (int)((uint)f >> 2)); #endif } static void UnsignedShiftRightStaticProperty() { Console.WriteLine("UnsignedShiftRightStaticProperty:"); StaticProperty = -15; Test(X(), StaticProperty = (int)((uint)StaticProperty >> 2)); ShortProperty = -20; ShortProperty = (short)((uint)StaticProperty >> 2); ShortProperty = -30; ShortProperty = (short)((ushort)StaticProperty >> 2); } static void DivideByBigValue() { Console.WriteLine("DivideByBigValue:"); ByteProperty = 5; // can't use "ByteProperty /= (byte)(byte.MaxValue + 3)" because that would be division by 2. ByteProperty = (byte)(ByteProperty / (byte.MaxValue + 3)); ByteProperty = 200; ByteProperty = (byte)(ByteProperty / Id(byte.MaxValue + 3)); ShortProperty = short.MaxValue; ShortProperty = (short)(ShortProperty / (short.MaxValue + 3)); } static void Overflow() { Console.WriteLine("Overflow:"); ByteProperty = 0; ByteProperty = (byte)checked(ByteProperty + 300); try { ByteProperty = checked((byte)(ByteProperty + 300)); } catch (OverflowException) { Console.WriteLine("Overflow OK"); } ByteProperty = 200; ByteProperty = (byte)checked(ByteProperty + 100); ByteProperty = 201; try { ByteProperty = checked((byte)(ByteProperty + 100)); } catch (OverflowException) { Console.WriteLine("Overflow OK"); } } static T Id(T val) { return val; } static void IntPtr_CompoundAssign() { Console.WriteLine("IntPtr_CompoundAssign:"); #if !MCS GetObject().IntPtrProperty -= 2; GetObject().IntPtrProperty += 2; #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/ConditionalAttr.cs ================================================ #define PRINT using System; using System.Diagnostics; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class ConditionalAttr { [Conditional("PRINT")] static void Print() { Console.WriteLine("Text!"); } static void Main() { Print(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class ControlFlow { public static int Main() { int result = 0; EmptyIf("Empty", ref result); EmptyIf("test", ref result); NormalIf("none", ref result); NormalIf("test", ref result); NormalIf2("none", ref result); NormalIf2("test", ref result); NormalIf3("none", ref result); NormalIf3("test", ref result); Test("none", ref result); Test("test", ref result); Console.WriteLine(result); ForeachWithAssignment(new int[] { 1, 5, 25 }); BreakUnlessContinue(true); BreakUnlessContinue(false); TestConditionals(); Console.WriteLine("Issue1946:\n" + Issue1946()); return 0; } static void EmptyIf(string input, ref int result) { if (input.Contains("test")) { } result = result + 1; Console.WriteLine("EmptyIf"); } static void NormalIf(string input, ref int result) { if (input.Contains("test")) { Console.WriteLine("result"); } else { Console.WriteLine("else"); } result = result + 1; Console.WriteLine("end"); } static void NormalIf2(string input, ref int result) { if (input.Contains("test")) { Console.WriteLine("result"); } result = result + 1; Console.WriteLine("end"); } static void NormalIf3(string input, ref int result) { if (input.Contains("test")) { Console.WriteLine("result"); } else { Console.WriteLine("else"); } result = result + 1; } static void Test(string input, ref int result) { foreach (char c in input) { Console.Write(c); result = result + 1; } if (input.Contains("test")) { Console.WriteLine("result"); } else { Console.WriteLine("else"); } } int Dim2Search(int arg) { var tens = new[] { 10, 20, 30 }; var ones = new[] { 1, 2, 3 }; for (int i = 0; i < tens.Length; i++) { for (int j = 0; j < ones.Length; j++) { if (tens[i] + ones[j] == arg) return i; } } return -1; } static void ForeachWithAssignment(IEnumerable inputs) { Console.WriteLine("ForeachWithAssignment"); foreach (int input in inputs) { int i = input; if (i < 10) i *= 2; Console.WriteLine(i); } } static void BreakUnlessContinue(bool b) { Console.WriteLine("BreakUnlessContinue({0})", b); for (int i = 0; i < 5; i++) { if ((i % 3) == 0) continue; Console.WriteLine(i); if (b) { Console.WriteLine("continuing"); continue; } Console.WriteLine("breaking out of loop"); break; } Console.WriteLine("BreakUnlessContinue (end)"); } static void TestConditionals() { Console.WriteLine(CastAfterConditional(0)); Console.WriteLine(CastAfterConditional(128)); } static byte CastAfterConditional(int value) { byte answer = (byte)(value == 128 ? 255 : 0); return answer; } static string Issue1946() { string obj = "1"; try { obj = "2"; } catch { obj = "3"; } finally { obj = "4"; } return obj; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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. // #include "../../../ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs" using System; using System.Runtime.CompilerServices; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class Conversions { static readonly TypeCode[] targetTypes = { TypeCode.Char, TypeCode.SByte, TypeCode.Byte, TypeCode.Int16, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal }; static object[] inputValues = { '\0', 'a', '\uFFFE', sbyte.MinValue, sbyte.MaxValue, (sbyte)-1, (sbyte)1, byte.MinValue, byte.MaxValue, (byte)1, short.MinValue, short.MaxValue, (short)-1, (short)1, ushort.MinValue, ushort.MaxValue, (ushort)1, int.MinValue, int.MaxValue, (int)-1, (int)1, uint.MinValue, uint.MaxValue, (uint)1, long.MinValue, long.MaxValue, (long)-1, (long)1, ulong.MinValue, ulong.MaxValue, (ulong)1, -1.1f, 1.1f, float.MinValue, float.MaxValue, float.NegativeInfinity, float.PositiveInfinity, float.NaN, -1.1, 1.1, double.MinValue, double.MaxValue, double.NegativeInfinity, double.PositiveInfinity, double.NaN, decimal.MinValue, decimal.MaxValue, decimal.MinusOne, decimal.One }; static void Main(string[] args) { RunTest(checkForOverflow: false); RunTest(checkForOverflow: true); Console.WriteLine(ReadZeroTerminatedString("Hello World!".Length)); C1.Test(); #if CS120 && !NET40 C3.Run(); #endif } static void RunTest(bool checkForOverflow) { string mode = checkForOverflow ? "checked" : "unchecked"; foreach (object input in inputValues) { string inputType = input.GetType().Name; foreach (var targetType in targetTypes) { try { object result = CSharpPrimitiveCast.Cast(targetType, input, checkForOverflow); Console.WriteLine("{0} ({1})({2}){3} = ({4}){5}", mode, targetType, inputType, input, result.GetType().Name, result); } catch (Exception ex) { Console.WriteLine("{0} ({1})({2}){3} = {4}", mode, targetType, inputType, input, ex.GetType().Name); } } } } static object MM(sbyte c) { checked { return (UInt64)c; } } static string ReadZeroTerminatedString(int length) { int read = 0; var buffer = new char[length]; var bytes = ReadBytes(length); while (read < length) { var current = bytes[read]; if (current == 0) break; buffer[read++] = (char)current; } return new string(buffer, 0, read); } static byte[] ReadBytes(int length) { return System.Text.Encoding.ASCII.GetBytes("Hello World!"); } } class C1 { public static implicit operator Type(C1 c) { return c.GetType(); } public static void Test() { Console.WriteLine("op_Implicit tests"); ExplicitUseOfImplicitConversion(new C1()); Console.WriteLine(ChainedImplicitConversions(new C2()).Name); } static void ExplicitUseOfImplicitConversion(C1 c) { Console.WriteLine(((Type)c).Name); } static Type ChainedImplicitConversions(C2 c) { return (C1)c; } } class C2 { public static implicit operator C1(C2 c) { return new C1(); } } #if CS120 && !NET40 class C3 { [InlineArray(4)] struct MyArray { private int elem; } static void Foo(object o) { Console.WriteLine("Foo(object) called"); } static void Foo(ReadOnlySpan o) { Console.WriteLine("Foo(ReadOnlySpan) called"); } static void Test(MyArray arr) { Foo((object)arr); } public static void Run() { Console.WriteLine("C3.Run() called"); Test(default); } } #endif } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/DecimalFields.cs ================================================ // Copyright (c) 2014 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { /// /// Description of DecimalFields. /// public class DecimalFields { public const decimal field1 = 1.5m; public const decimal field2 = decimal.One; public const decimal field3 = decimal.MaxValue; public const decimal field4 = decimal.MinValue; public static int Main() { Console.WriteLine(field1); Console.WriteLine(field2); Console.WriteLine(field3); Console.WriteLine(field4); Console.WriteLine(IntToDecimal()); Console.WriteLine(UIntToDecimal()); Console.WriteLine(LongToDecimal()); Console.WriteLine(ULongToDecimal()); return 0; } public static decimal IntToDecimal() { return (decimal)int.MaxValue; } public static decimal UIntToDecimal() { return (decimal)uint.MaxValue; } public static decimal LongToDecimal() { return (decimal)long.MaxValue; } public static decimal ULongToDecimal() { return (decimal)ulong.MaxValue; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class DeconstructionTests { public static void Main() { new DeconstructionTests().Test(); } public struct MyInt { public static implicit operator int(MyInt x) { Console.WriteLine("int op_Implicit(MyInt)"); return 0; } public static implicit operator MyInt(int x) { Console.WriteLine("MyInt op_Implicit(int)"); return default(MyInt); } } private class DeconstructionSource { public int Dummy { get; set; } public void Deconstruct(out T a, out T2 b) { Console.WriteLine("Deconstruct"); a = default(T); b = default(T2); } } private class AssignmentTargets { int id; public AssignmentTargets(int id) { this.id = id; } public int IntField; public int? NullableIntField; public MyInt MyIntField; public MyInt? NullableMyIntField; public MyInt My { get { Console.WriteLine($"{id}.get_My()"); return default(MyInt); } set { Console.WriteLine($"{id}.set_My({value})"); } } public MyInt? NMy { get { Console.WriteLine($"{id}.get_NMy()"); return default(MyInt?); } set { Console.WriteLine($"{id}.set_NMy({value})"); } } public int IntProperty { get { Console.WriteLine($"{id}.get_IntProperty()"); return default(int); } set { Console.WriteLine($"{id}.set_IntProperty({value})"); } } public uint UIntProperty { get { Console.WriteLine($"{id}.get_UIntProperty()"); return default(uint); } set { Console.WriteLine($"{id}.set_UIntProperty({value})"); } } } private DeconstructionSource GetSource() { Console.WriteLine("GetSource()"); return new DeconstructionSource(); } private (T, T2) GetTuple() { Console.WriteLine("GetTuple()"); return default(ValueTuple); } private (T, T2, T3) GetTuple() { Console.WriteLine("GetTuple()"); return default(ValueTuple); } private AssignmentTargets Get(int i) { Console.WriteLine($"Get({i})"); return new AssignmentTargets(i); } public void Test() { Property_NoDeconstruction_SwappedAssignments(); Property_NoDeconstruction_SwappedInits(); Property_IntToUIntConversion(); NoDeconstruction_NotUsingConver(); NoDeconstruction_NotUsingConver_Tuple(); NullReferenceException_Field_Deconstruction(out _); NullReferenceException_RefLocalReferencesField_Deconstruction(out _); NullReferenceException_RefLocalReferencesArrayElement_Deconstruction(out _, null); DeconstructTupleSameVar(("a", "b")); DeconstructTupleListForEachSameVar(new List<(string, string)> { ("a", "b") }); } public void Property_NoDeconstruction_SwappedAssignments() { Console.WriteLine("Property_NoDeconstruction_SwappedAssignments:"); AssignmentTargets customDeconstructionAndConversion = Get(0); AssignmentTargets customDeconstructionAndConversion2 = Get(1); GetSource().Deconstruct(out MyInt? x, out MyInt y); MyInt myInt2 = customDeconstructionAndConversion2.My = y; MyInt? myInt4 = customDeconstructionAndConversion.NMy = x; } public void Property_NoDeconstruction_SwappedInits() { Console.WriteLine("Property_NoDeconstruction_SwappedInits:"); AssignmentTargets customDeconstructionAndConversion = Get(1); (Get(0).NMy, customDeconstructionAndConversion.My) = GetSource(); } public void Property_IntToUIntConversion() { Console.WriteLine("Property_IntToUIntConversion:"); AssignmentTargets t0 = Get(0); AssignmentTargets t1 = Get(1); int a; uint b; GetSource().Deconstruct(out a, out b); t0.UIntProperty = (uint)a; t1.IntProperty = (int)b; } public void NoDeconstruction_NotUsingConver() { Console.WriteLine("NoDeconstruction_NotUsingConver:"); AssignmentTargets t0 = Get(0); int a; uint b; GetSource().Deconstruct(out a, out b); long c = a; t0.IntProperty = a; t0.UIntProperty = b; Console.WriteLine(c); } public void NoDeconstruction_NotUsingConver_Tuple() { Console.WriteLine("NoDeconstruction_NotUsingConver_Tuple:"); AssignmentTargets t0 = Get(0); var t = GetTuple(); long c = t.Item1; t0.IntProperty = t.Item1; t0.UIntProperty = t.Item2; Console.WriteLine(c); } public void NullReferenceException_Field_Deconstruction(out int a) { try { AssignmentTargets t0 = null; (t0.IntField, a) = GetSource(); } catch (Exception ex) { a = 0; Console.WriteLine(ex.GetType().FullName); } } public void NullReferenceException_RefLocalReferencesField_Deconstruction(out int a) { try { AssignmentTargets t0 = null; ref int i = ref t0.IntField; (i, a) = GetSource(); } catch (Exception ex) { a = 0; Console.WriteLine(ex.GetType().FullName); } } public void NullReferenceException_RefLocalReferencesArrayElement_Deconstruction(out int a, int[] arr) { try { ref int i = ref arr[0]; (i, a) = GetSource(); } catch (Exception ex) { a = 0; Console.WriteLine(ex.GetType().FullName); } } public void DeconstructTupleSameVar((string, string) tuple) { Console.WriteLine("DeconstructTupleSameVar:"); string a; a = tuple.Item1; a = tuple.Item2; Console.WriteLine(a); } public void DeconstructTupleListForEachSameVar(List<(string, string)> tuples) { Console.WriteLine("DeconstructTupleListForEachSameVar:"); foreach (var tuple in tuples) { string a; a = tuple.Item1; a = tuple.Item2; Console.WriteLine(a); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/DynamicTests.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class DynamicTests { delegate void RefAction(ref T arg); static void Main(string[] args) { PrintResult((ref dynamic x) => x = x + 2.0, 5.0); PrintResult((ref dynamic x) => x = x + 2.0, 5); PrintResult((ref dynamic x) => x = x + 2, 5.0); PrintResult((ref dynamic x) => x = x + 2, 5); PrintResult((ref dynamic x) => x = x - 2.0, 5.0); PrintResult((ref dynamic x) => x = x - 2.0, 5); PrintResult((ref dynamic x) => x = x - 2, 5.0); PrintResult((ref dynamic x) => x = x - 2, 5); PrintResult((ref dynamic x) => x = x * 2.0, 5.0); PrintResult((ref dynamic x) => x = x * 2.0, 5); PrintResult((ref dynamic x) => x = x * 2, 5.0); PrintResult((ref dynamic x) => x = x * 2, 5); PrintResult((ref dynamic x) => x = x / 2.0, 5.0); PrintResult((ref dynamic x) => x = x / 2.0, 5); PrintResult((ref dynamic x) => x = x / 2, 5.0); PrintResult((ref dynamic x) => x = x / 2, 5); } private static void PrintResult(RefAction p, dynamic arg) { p(ref arg); Console.WriteLine(arg); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/ExpressionTrees.cs ================================================ // Copyright (c) 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 // 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.Linq.Expressions; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class ExpressionTrees { static void Main() { Test(); var twice = GetExpression(Expression.Constant(2)).Compile(); Console.WriteLine(twice(21)); } static void Test() { int i = 0; Expression> expression = () => i; i = 1; Console.WriteLine(expression.Compile()()); } static Expression> GetExpression(Expression factor) { ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x"); return Expression.Lambda>(Expression.Multiply(parameterExpression, factor), parameterExpression); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class FloatingPointArithmetic { public static int Main(string[] args) { Issue999(); Issue1656(); Issue1794(); return 0; } static void Issue999() { for (float i = -10f; i <= 10f; i += 0.01f) Console.WriteLine("{1:R}: {0:R}", M(i), i); } static float M(float v) { return 0.99f * v + 0.01f; } static void Issue1656() { double primary = 'B'; CxAssert((++primary) == 'C'); CxAssert((--primary) == 'B'); CxAssert((primary++) == 'B'); CxAssert((primary--) == 'C'); } static void Issue1794() { Console.WriteLine("CastUnsignedToFloat:"); Console.WriteLine(CastUnsignedToFloat(9007199791611905).ToString("r")); Console.WriteLine("CastUnsignedToDouble:"); Console.WriteLine(CastUnsignedToDouble(9007199791611905).ToString("r")); Console.WriteLine("CastUnsignedToFloatViaDouble:"); Console.WriteLine(CastUnsignedToFloatViaDouble(9007199791611905).ToString("r")); Console.WriteLine("CastSignedToFloat:"); Console.WriteLine(CastSignedToFloat(9007199791611905).ToString("r")); Console.WriteLine("ImplicitCastSignedToFloat:"); Console.WriteLine(ImplicitCastSignedToFloat(9007199791611905).ToString("r")); Console.WriteLine("CastSignedToDouble:"); Console.WriteLine(CastSignedToDouble(9007199791611905).ToString("r")); Console.WriteLine("CastSignedToFloatViaDouble:"); Console.WriteLine(CastSignedToFloatViaDouble(9007199791611905).ToString("r")); } static float CastUnsignedToFloat(ulong val) { return (float)val; } static double CastUnsignedToDouble(ulong val) { return (double)val; } static float CastUnsignedToFloatViaDouble(ulong val) { // The double-rounding can increase the rounding error return (float)(double)val; } static float CastSignedToFloat(long val) { return (float)val; } static double CastSignedToDouble(long val) { return (double)val; } static float CastSignedToFloatViaDouble(long val) { // The double-rounding can increase the rounding error return (float)(double)val; } static float ImplicitCastSignedToFloat(long val) { return val; } static void CxAssert(bool v) { if (!v) { throw new InvalidOperationException(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Generics.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { /// /// Description of Generics. /// public class Generics { public static void Main() { Console.WriteLine(TestGenericReturn()); Console.WriteLine(TestGenericReturn()); TestGenericParam(); TestGenericParam(); } public static T TestGenericReturn() { return default(T); } public static void TestGenericParam() { Console.WriteLine(typeof(T)); } public static void TestGenericParam() { Console.WriteLine(typeof(T1) + " " + typeof(T2)); } } class GenericClass { public void M(out GenericClass self) { self = this; } } public abstract class BaseClass { protected abstract void Method1(T test); } public class DerivedClass : BaseClass { protected override void Method1(T test) { } private void Method2() { this.Method1(0); } } #region Issue 958 - Invalid cast in generic class for abstract base call internal abstract class BaseClass { protected abstract void Method1(); } internal class DerivedClass : BaseClass { protected override void Method1() { } private void Method2() { this.Method1(); } } #endregion } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/HelloWorld.cs ================================================ // Copyright (c) 2010-2013 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class HelloWorld { public static int Main(string[] args) { Console.WriteLine("Hello World!"); return 0; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/InitializerTests.cs ================================================ // Copyright (c) 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 // 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.Globalization; using System.Linq; using System.Threading; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class InitializerTests { public static int Main() { int[,] test = new int[2, 3]; test[0, 0] = 0; test[0, 1] = 1; test[0, 2] = 2; int result = test.Length + test[0, 0] + test[0, 2]; Console.WriteLine(result); return 0; } private enum MyEnum { a, b } private enum MyEnum2 { c, d } private class Data { public List FieldList = new List(); public InitializerTests.MyEnum a { get; set; } public InitializerTests.MyEnum b { get; set; } public List PropertyList { get; set; } public InitializerTests.Data MoreData { get; set; } public InitializerTests.StructData NestedStruct { get; set; } public InitializerTests.Data this[int i] { get { return null; } set { } } public InitializerTests.Data this[int i, string j] { get { return null; } set { } } } private struct StructData { public int Field; public int Property { get; set; } public InitializerTests.Data MoreData { get; set; } public StructData(int initialValue) { this = default(InitializerTests.StructData); this.Field = initialValue; this.Property = initialValue; } } // Helper methods used to ensure initializers used within expressions work correctly private static void X(object a, object b) { } private static object Y() { return null; } public static void CollectionInitializerList() { InitializerTests.X(InitializerTests.Y(), new List { 1, 2, 3 }); } public static object RecursiveCollectionInitializer() { List list = new List(); list.Add(list); return list; } public static void CollectionInitializerDictionary() { InitializerTests.X(InitializerTests.Y(), new Dictionary { { "First", 1 }, { "Second", 2 }, { "Third", 3 } }); } public static void CollectionInitializerDictionaryWithEnumTypes() { InitializerTests.X(InitializerTests.Y(), new Dictionary { { InitializerTests.MyEnum.a, InitializerTests.MyEnum2.c }, { InitializerTests.MyEnum.b, InitializerTests.MyEnum2.d } }); } public static void NotACollectionInitializer() { List list = new List(); list.Add(1); list.Add(2); list.Add(3); InitializerTests.X(InitializerTests.Y(), list); } public static void ObjectInitializer() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.a }); } public static void NotAnObjectInitializer() { InitializerTests.Data data = new InitializerTests.Data(); data.a = InitializerTests.MyEnum.a; InitializerTests.X(InitializerTests.Y(), data); } public static void ObjectInitializerAssignCollectionToField() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.a, FieldList = new List { InitializerTests.MyEnum2.c, InitializerTests.MyEnum2.d } }); } public static void ObjectInitializerAddToCollectionInField() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.a, FieldList = { InitializerTests.MyEnum2.c, InitializerTests.MyEnum2.d } }); } public static void ObjectInitializerAssignCollectionToProperty() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.a, PropertyList = new List { InitializerTests.MyEnum2.c, InitializerTests.MyEnum2.d } }); } public static void ObjectInitializerAddToCollectionInProperty() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.a, PropertyList = { InitializerTests.MyEnum2.c, InitializerTests.MyEnum2.d } }); } public static void ObjectInitializerWithInitializationOfNestedObjects() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { MoreData = { a = InitializerTests.MyEnum.a, MoreData = { a = InitializerTests.MyEnum.b } } }); } static int GetInt() { return 1; } static string GetString() { return "Test"; } #if CS60 public static void SimpleDictInitializer() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { MoreData = { a = InitializerTests.MyEnum.a, [2] = (Data)null } }); } public static void MixedObjectAndDictInitializer() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { MoreData = { a = InitializerTests.MyEnum.a, [GetInt()] = { a = InitializerTests.MyEnum.b, FieldList = { MyEnum2.c }, [GetInt(), GetString()] = new Data(), [2] = (Data)null } } }); } #endif public static void ObjectInitializerWithInitializationOfDeeplyNestedObjects() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { a = InitializerTests.MyEnum.b, MoreData = { a = InitializerTests.MyEnum.a, MoreData = { MoreData = { MoreData = { MoreData = { MoreData = { MoreData = { a = MyEnum.b } } } } } } } }); } public static void CollectionInitializerInsideObjectInitializers() { InitializerTests.Data castPattern = new InitializerTests.Data { MoreData = new InitializerTests.Data { a = InitializerTests.MyEnum.a, b = InitializerTests.MyEnum.b, PropertyList = { InitializerTests.MyEnum2.c } } }; } public static void StructInitializer_DefaultConstructor() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData { Field = 1, Property = 2 }); } public static void StructInitializer_ExplicitConstructor() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData(0) { Field = 1, Property = 2 }); } public static void StructInitializerWithInitializationOfNestedObjects() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData { MoreData = { a = InitializerTests.MyEnum.a, FieldList = { InitializerTests.MyEnum2.c, InitializerTests.MyEnum2.d } } }); } public static void StructInitializerWithinObjectInitializer() { InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data { NestedStruct = new InitializerTests.StructData(2) { Field = 1, Property = 2 } }); } public static void Bug270_NestedInitialisers() { NumberFormatInfo[] numberFormats = null; Thread t = new Thread(Bug270_NestedInitialisers) { Priority = ThreadPriority.BelowNormal, CurrentCulture = new CultureInfo(0) { DateTimeFormat = new DateTimeFormatInfo { ShortDatePattern = "ddmmyy" }, NumberFormat = (from format in numberFormats where format.CurrencySymbol == "$" select format).First() } }; } #if CS60 class Issue2622a { public class C { public ServiceHost M() { return new ServiceHost(typeof(EWSService), null) { Description = { Endpoints = { [0] = { Behaviors = { new EwsWebHttpBehavior() } } } } }; } } class EWSService { } public class ServiceHost { public ServiceHost(Type type, object x) { } public Descr Description { get; } } public class Descr { public List Endpoints { get; } } public class EP { public List Behaviors { get; } } public abstract class Beh { } public class EwsWebHttpBehavior : Beh { } } #endif class Issue855 { class Data { public object Obj; } class Items { public void SetItem(int i, object item) { } } object Item(string s, Data d) { return new object(); } void Test() { Items items = null; int num = 0; for (int i = 0; i < 2; i++) { if (num < 10) items.SetItem(num, Item(string.Empty, new Data { Obj = null })); } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Jmp.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly 'Jmp' { .ver 0:0:0:0 } .module Jmp.exe .corflags 0x00000001 // ILOnly .class private auto ansi abstract sealed beforefieldinit Program extends [mscorlib]System.Object { .method public hidebysig static void Main (string[] args) cil managed { .maxstack 8 .entrypoint ldstr "Method1(100) = {0}" ldc.i4 100 call int32 Program::Method1(int32) box int32 call void [mscorlib]System.Console::WriteLine(string, object) ret } // end of method Main .method public static int32 Method1(int32 val) { ldarg.0 ldc.i4.1 add starg.s 0 jmp int32 Program::Method2(int32) } .method public static int32 Method2(int32 val) { ldarg.0 ldc.i4.5 mul ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs ================================================ // This test case is taken from https://blogs.msdn.microsoft.com/lukeh/2007/10/01/taking-linq-to-objects-to-extremes-a-fully-linqified-raytracer/ using System; using System.Collections.Generic; using System.Linq; namespace RayTracer { public class RayTracer { static void Main() { const int width = 50; const int height = 50; RayTracer rayTracer = new RayTracer(width, height, (int x, int y, Color color) => { Console.Write("{0},{1}:{2};", x, y, color); }); rayTracer.Render(rayTracer.DefaultScene); } private int screenWidth; private int screenHeight; private const int MaxDepth = 5; public Action setPixel; public RayTracer(int screenWidth, int screenHeight, Action setPixel) { this.screenWidth = screenWidth; this.screenHeight = screenHeight; this.setPixel = setPixel; } private class Wrap { public readonly Func, T> It; public Wrap(Func, T> it) { It = it; } } public static Func Y(Func, Func> f) { Func>, Func> g = wx => f(wx.It(wx)); return g(new Wrap>(wx => f(y => wx.It(wx)(y)))); } class TraceRayArgs { public readonly Ray Ray; public readonly Scene Scene; public readonly int Depth; public TraceRayArgs(Ray ray, Scene scene, int depth) { Ray = ray; Scene = scene; Depth = depth; } } internal void Render(Scene scene) { var pixelsQuery = from y in Enumerable.Range(0, screenHeight) let recenterY = -(y - (screenHeight / 2.0)) / (2.0 * screenHeight) select from x in Enumerable.Range(0, screenWidth) let recenterX = (x - (screenWidth / 2.0)) / (2.0 * screenWidth) let point = Vector.Norm(Vector.Plus(scene.Camera.Forward, Vector.Plus(Vector.Times(recenterX, scene.Camera.Right), Vector.Times(recenterY, scene.Camera.Up)))) let ray = new Ray() { Start = scene.Camera.Pos, Dir = point } let computeTraceRay = (Func, Func>) (f => traceRayArgs => (from isect in from thing in traceRayArgs.Scene.Things select thing.Intersect(traceRayArgs.Ray) where isect != null orderby isect.Dist let d = isect.Ray.Dir let pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start) let normal = isect.Thing.Normal(pos) let reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal)) let naturalColors = from light in traceRayArgs.Scene.Lights let ldis = Vector.Minus(light.Pos, pos) let livec = Vector.Norm(ldis) let testRay = new Ray() { Start = pos, Dir = livec } let testIsects = from inter in from thing in traceRayArgs.Scene.Things select thing.Intersect(testRay) where inter != null orderby inter.Dist select inter let testIsect = testIsects.FirstOrDefault() let neatIsect = testIsect == null ? 0 : testIsect.Dist let isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0)) where !isInShadow let illum = Vector.Dot(livec, normal) let lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0) let specular = Vector.Dot(livec, Vector.Norm(reflectDir)) let scolor = specular > 0 ? Color.Times(Math.Pow(specular, isect.Thing.Surface.Roughness), light.Color) : Color.Make(0, 0, 0) select Color.Plus(Color.Times(isect.Thing.Surface.Diffuse(pos), lcolor), Color.Times(isect.Thing.Surface.Specular(pos), scolor)) let reflectPos = Vector.Plus(pos, Vector.Times(.001, reflectDir)) let reflectColor = traceRayArgs.Depth >= MaxDepth ? Color.Make(.5, .5, .5) : Color.Times(isect.Thing.Surface.Reflect(reflectPos), f(new TraceRayArgs(new Ray() { Start = reflectPos, Dir = reflectDir }, traceRayArgs.Scene, traceRayArgs.Depth + 1))) select naturalColors.Aggregate(reflectColor, (color, natColor) => Color.Plus(color, natColor)) ).DefaultIfEmpty(Color.Background).First()) let traceRay = Y(computeTraceRay) select new { X = x, Y = y, Color = traceRay(new TraceRayArgs(ray, scene, 0)) }; foreach (var row in pixelsQuery) foreach (var pixel in row) setPixel(pixel.X, pixel.Y, pixel.Color); } internal readonly Scene DefaultScene = new Scene() { Things = new SceneObject[] { new Plane() { Norm = Vector.Make(0,1,0), Offset = 0, Surface = Surfaces.CheckerBoard }, new Sphere() { Center = Vector.Make(0,1,0), Radius = 1, Surface = Surfaces.Shiny }, new Sphere() { Center = Vector.Make(-1,.5,1.5), Radius = .5, Surface = Surfaces.Shiny }}, Lights = new Light[] { new Light() { Pos = Vector.Make(-2,2.5,0), Color = Color.Make(.49,.07,.07) }, new Light() { Pos = Vector.Make(1.5,2.5,1.5), Color = Color.Make(.07,.07,.49) }, new Light() { Pos = Vector.Make(1.5,2.5,-1.5), Color = Color.Make(.07,.49,.071) }, new Light() { Pos = Vector.Make(0,3.5,0), Color = Color.Make(.21,.21,.35) }}, Camera = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, .5, 0)) }; } static class Surfaces { // Only works with X-Z plane. public static readonly Surface CheckerBoard = new Surface() { Diffuse = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) ? Color.Make(1, 1, 1) : Color.Make(0, 0, 0), Specular = pos => Color.Make(1, 1, 1), Reflect = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) ? .1 : .7, Roughness = 150 }; public static readonly Surface Shiny = new Surface() { Diffuse = pos => Color.Make(1, 1, 1), Specular = pos => Color.Make(.5, .5, .5), Reflect = pos => .6, Roughness = 50 }; } class Vector { public readonly double X; public readonly double Y; public readonly double Z; public Vector(double x, double y, double z) { X = x; Y = y; Z = z; } public static Vector Make(double x, double y, double z) { return new Vector(x, y, z); } public static Vector Times(double n, Vector v) { return new Vector(v.X * n, v.Y * n, v.Z * n); } public static Vector Minus(Vector v1, Vector v2) { return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); } public static Vector Plus(Vector v1, Vector v2) { return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); } public static double Dot(Vector v1, Vector v2) { return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z); } public static double Mag(Vector v) { return Math.Sqrt(Dot(v, v)); } public static Vector Norm(Vector v) { double mag = Mag(v); double div = mag == 0 ? double.PositiveInfinity : 1 / mag; return Times(div, v); } public static Vector Cross(Vector v1, Vector v2) { return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)), ((v1.Z * v2.X) - (v1.X * v2.Z)), ((v1.X * v2.Y) - (v1.Y * v2.X))); } public static bool Equals(Vector v1, Vector v2) { return (v1.X == v2.X) && (v1.Y == v2.Y) && (v1.Z == v2.Z); } } public class Color { public double R; public double G; public double B; public Color(double r, double g, double b) { R = r; G = g; B = b; } public static Color Make(double r, double g, double b) { return new Color(r, g, b); } public static Color Times(double n, Color v) { return new Color(n * v.R, n * v.G, n * v.B); } public static Color Times(Color v1, Color v2) { return new Color(v1.R * v2.R, v1.G * v2.G, v1.B * v2.B); } public static Color Plus(Color v1, Color v2) { return new Color(v1.R + v2.R, v1.G + v2.G, v1.B + v2.B); } public static Color Minus(Color v1, Color v2) { return new Color(v1.R - v2.R, v1.G - v2.G, v1.B - v2.B); } public static readonly Color Background = Make(0, 0, 0); public static readonly Color DefaultColor = Make(0, 0, 0); private double Legalize(double d) { return d > 1 ? 1 : d; } public override string ToString() { return string.Format("[{0},{1},{2}]", R, G, B); } } class Ray { public Vector Start; public Vector Dir; } class ISect { public SceneObject Thing; public Ray Ray; public double Dist; } class Surface { public Func Diffuse; public Func Specular; public Func Reflect; public double Roughness; } class Camera { public Vector Pos; public Vector Forward; public Vector Up; public Vector Right; public static Camera Create(Vector pos, Vector lookAt) { Vector forward = Vector.Norm(Vector.Minus(lookAt, pos)); Vector down = new Vector(0, -1, 0); Vector right = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, down))); Vector up = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, right))); return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right }; } } class Light { public Vector Pos; public Color Color; } abstract class SceneObject { public Surface Surface; public abstract ISect Intersect(Ray ray); public abstract Vector Normal(Vector pos); } class Sphere : SceneObject { public Vector Center; public double Radius; public override ISect Intersect(Ray ray) { Vector eo = Vector.Minus(Center, ray.Start); double v = Vector.Dot(eo, ray.Dir); double dist; if (v < 0) { dist = 0; } else { double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2)); dist = disc < 0 ? 0 : v - Math.Sqrt(disc); } if (dist == 0) return null; return new ISect() { Thing = this, Ray = ray, Dist = dist }; } public override Vector Normal(Vector pos) { return Vector.Norm(Vector.Minus(pos, Center)); } } class Plane : SceneObject { public Vector Norm; public double Offset; public override ISect Intersect(Ray ray) { double denom = Vector.Dot(Norm, ray.Dir); if (denom > 0) return null; return new ISect() { Thing = this, Ray = ray, Dist = (Vector.Dot(Norm, ray.Start) + Offset) / (-denom) }; } public override Vector Normal(Vector pos) { return Norm; } } class Scene { public SceneObject[] Things; public Light[] Lights; public Camera Camera; public IEnumerable Intersect(Ray r) { return from thing in Things select thing.Intersect(r); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs ================================================ // Copyright (c) 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 // 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; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class Loops { public class CustomClassEnumeratorWithIDisposable : IDisposable { bool next = true; public T Current { get { return default(T); } } public void Dispose() { Console.WriteLine("CustomClassEnumeratorWithIDisposable.Dispose()"); } public bool MoveNext() { if (next) { next = false; return true; } return next; } public CustomClassEnumeratorWithIDisposable GetEnumerator() { return this; } } static void Operation(ref int item) { item++; } static T CallWithSideEffect() { Console.WriteLine("CallWithSideEffect"); return default(T); } static void Main() { ForWithMultipleVariables(); DoubleForEachWithSameVariable(new[] { "a", "b", "c" }); ForeachExceptForNameCollision(new[] { 42, 43, 44, 45 }); ForeachExceptForContinuedUse(new[] { 42, 43, 44, 45 }); NonGenericForeachWithReturnFallbackTest(new object[] { "a", 42, "b", 43 }); NonGenericForeachWithReturn(new object[] { "a", 42, "b", 43 }); ForeachWithReturn(new[] { 42, 43, 44, 45 }); ForeachWithRefUsage(new List { 1, 2, 3, 4, 5 }); Console.WriteLine(FirstOrDefault(new List { 1, 2, 3, 4, 5 })); Console.WriteLine(NoForeachDueToMultipleCurrentAccess(new List { 1, 2, 3, 4, 5 })); Console.WriteLine(NoForeachCallWithSideEffect(new CustomClassEnumeratorWithIDisposable())); LoopWithGotoRepeat(); Console.WriteLine("LoopFollowedByIf: {0}", LoopFollowedByIf()); NoForeachDueToVariableAssignment(); } public static void ForWithMultipleVariables() { int x, y; Console.WriteLine("before for"); for (x = y = 0; x < 10; x++) { y++; Console.WriteLine("x = " + x + ", y = " + y); } Console.WriteLine("after for"); } public static void DoubleForEachWithSameVariable(IEnumerable enumerable) { Console.WriteLine("DoubleForEachWithSameVariable:"); foreach (string current in enumerable) { Console.WriteLine(current.ToLower()); } Console.WriteLine("after first loop"); foreach (string current in enumerable) { Console.WriteLine(current.ToUpper()); } Console.WriteLine("after second loop"); } public static void ForeachExceptForNameCollision(IEnumerable inputs) { Console.WriteLine("ForeachWithNameCollision:"); int current; using (IEnumerator enumerator = inputs.GetEnumerator()) { while (enumerator.MoveNext()) { current = enumerator.Current; Console.WriteLine(current); } } current = 1; Console.WriteLine(current); } public static void ForeachExceptForContinuedUse(IEnumerable inputs) { Console.WriteLine("ForeachExceptForContinuedUse"); int num = 0; using (IEnumerator enumerator = inputs.GetEnumerator()) { while (enumerator.MoveNext()) { num = enumerator.Current; Console.WriteLine(num); } } Console.WriteLine("Last: " + num); } public static void NonGenericForeachWithReturnFallbackTest(IEnumerable e) { Console.WriteLine("NonGenericForeachWithReturnFallback:"); IEnumerator enumerator = e.GetEnumerator(); try { Console.WriteLine("MoveNext"); if (enumerator.MoveNext()) { object current = enumerator.Current; Console.WriteLine("current: " + current); } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } Console.WriteLine("After finally!"); } public static object NonGenericForeachWithReturn(IEnumerable enumerable) { Console.WriteLine("NonGenericForeachWithReturn:"); foreach (var obj in enumerable) { Console.WriteLine("return: " + obj); return obj; } Console.WriteLine("return: null"); return null; } public static int? ForeachWithReturn(IEnumerable enumerable) { Console.WriteLine("ForeachWithReturn:"); foreach (var obj in enumerable) { Console.WriteLine("return: " + obj); return obj; } Console.WriteLine("return: null"); return null; } public static void ForeachWithRefUsage(List items) { Console.WriteLine("ForeachWithRefUsage:"); foreach (var item in items) { var itemToChange = item; Console.WriteLine("item: " + item); Operation(ref itemToChange); Console.WriteLine("item: " + itemToChange); } } public static T FirstOrDefault(IEnumerable items) { T result = default(T); foreach (T item in items) { result = item; break; } return result; } public static T NoForeachDueToMultipleCurrentAccess(IEnumerable items) { Console.WriteLine("NoForeachDueToMultipleCurrentAccess:"); T result = default(T); using (IEnumerator enumerator = items.GetEnumerator()) { while (enumerator.MoveNext()) { result = enumerator.Current; Console.WriteLine("result: " + result); } return enumerator.Current; } } public static T NoForeachCallWithSideEffect(CustomClassEnumeratorWithIDisposable items) { Console.WriteLine("NoForeachCallWithSideEffect:"); using (CustomClassEnumeratorWithIDisposable enumerator = items.GetEnumerator()) { while (enumerator.MoveNext()) { T result = enumerator.Current; } return CallWithSideEffect(); } } static bool GetBool(string text) { return false; } // https://github.com/icsharpcode/ILSpy/issues/915 static void LoopWithGotoRepeat() { Console.WriteLine("LoopWithGotoRepeat:"); try { REPEAT: Console.WriteLine("after repeat label"); while (GetBool("Loop condition")) { if (GetBool("if1")) { if (GetBool("if3")) { goto REPEAT; } break; } } Console.WriteLine("after loop"); } finally { Console.WriteLine("finally"); } Console.WriteLine("after finally"); } private static int LoopFollowedByIf() { int num = 0; while (num == 0) { num++; } if (num == 0) { return -1; } return num; } static void Issue1392ForWithNestedSwitchPlusGoto() { for (int i = 0; i < 100; i++) { again: switch (i) { case 10: Console.WriteLine("10"); break; case 25: Console.WriteLine("25"); break; case 50: Console.WriteLine("50"); goto again; } } } private static void NoForeachDueToVariableAssignment() { try { int[] array = new int[] { 1, 2, 3 }; for (int i = 0; i < array.Length; i++) { Console.WriteLine(array[i]); array = null; } } catch (Exception ex) { Console.WriteLine(ex.GetType() + ": " + ex.Message); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/MemberLookup.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class MemberLookup { static readonly Action delegateConstruction = (new Child1() as Base1).TestAction; public static int Main() { Console.WriteLine((new Child1() as Base1).Field); Child1.Test(); delegateConstruction(); new Child2b().CallTestMethod(); return 0; } class Base1 { public int Field = 1; protected virtual void TestMethod() { Property = 5; Console.WriteLine("Base1.TestMethod()"); Console.WriteLine(Property); } public void TestAction() { Console.WriteLine("Base1.TestAction()"); } public int Property { get; set; } public virtual int VirtProp { get { return 3; } } } class Child1 : Base1 { Child1 child; new public int Field = 2; public static void Test() { var o = new Child1(); o.child = new Child1(); o.TestMethod(); Console.WriteLine(((Base1)o).Property); Console.WriteLine(o.Property); Console.WriteLine(((Base1)o).VirtProp); Console.WriteLine(o.VirtProp); } protected override void TestMethod() { Property = 10; base.TestMethod(); if (child != null) child.TestMethod(); Console.WriteLine("Child1.TestMethod()"); Console.WriteLine("Property = " + Property + " " + base.Property); Console.WriteLine("Field = " + Field); Console.WriteLine("base.Field = " + base.Field); } new public void TestAction() { Console.WriteLine("Child1.TestAction()"); } new public int Property { get; set; } public override int VirtProp { get { return base.VirtProp * 2; } } } class Child2 : Base1 { public void CallTestMethod() { Console.WriteLine("Child2 calling this.TestMethod():"); this.TestMethod(); Console.WriteLine("Child2 calling base.TestMethod():"); base.TestMethod(); } } class Child2b : Child2 { protected override void TestMethod() { Console.WriteLine("Child2b.TestMethod"); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/MiniJSON.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { // taken from https://github.com/Jackyjjc/MiniJSON.cs // Copyright (c) 2013 Calvin Rien. // Licensed under the MIT LICENSE. public class MiniJSONTest { public static void Main(string[] args) { var jsonString = "{ \"array\": [1.44,2,3], " + "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + "\"int\": 65536, " + "\"float\": 3.1415926, " + "\"bool\": true, " + "\"null\": null }"; var dict = Json.Deserialize(jsonString) as Dictionary; Console.WriteLine("deserialized: " + dict.GetType()); Console.WriteLine("dict['array'][0]: " + ((List)dict["array"])[0]); Console.WriteLine("dict['string']: " + (string)dict["string"]); Console.WriteLine("dict['float']: " + (double)dict["float"]); // floats come out as doubles Console.WriteLine("dict['int']: " + (long)dict["int"]); // ints come out as longs Console.WriteLine("dict['unicode']: " + (string)dict["unicode"]); var str = Json.Serialize(dict); Console.WriteLine("serialized: " + str); } } public static class Json { /// /// Parses the string json into a value /// /// A JSON string. /// An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false public static object Deserialize(string json) { // save the string for debug information if (json == null) { return null; } return Parser.Parse(json); } sealed class Parser : IDisposable { const string WORD_BREAK = "{}[],:\""; public static bool IsWordBreak(char c) { return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; } const string HEX_DIGIT = "0123456789ABCDEFabcdef"; public static bool IsHexDigit(char c) { return HEX_DIGIT.IndexOf(c) != -1; } enum TOKEN { NONE, CURLY_OPEN, CURLY_CLOSE, SQUARED_OPEN, SQUARED_CLOSE, COLON, COMMA, STRING, NUMBER, TRUE, FALSE, NULL }; StringReader json; Parser(string jsonString) { json = new StringReader(jsonString); } public static object Parse(string jsonString) { using (var instance = new Parser(jsonString)) { return instance.ParseValue(); } } public void Dispose() { json.Dispose(); json = null; } Dictionary ParseObject() { Dictionary table = new Dictionary(); // ditch opening brace json.Read(); // { while (true) { switch (NextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.CURLY_CLOSE: return table; case TOKEN.STRING: // name string name = ParseString(); if (name == null) { return null; } // : if (NextToken != TOKEN.COLON) { return null; } // ditch the colon json.Read(); // value TOKEN valueToken = NextToken; object value = ParseByToken(valueToken); if (value == null && valueToken != TOKEN.NULL) return null; table[name] = value; break; default: return null; } } } List ParseArray() { List array = new List(); // ditch opening bracket json.Read(); // [ var parsing = true; while (parsing) { TOKEN nextToken = NextToken; switch (nextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.SQUARED_CLOSE: parsing = false; break; default: object value = ParseByToken(nextToken); if (value == null && nextToken != TOKEN.NULL) return null; array.Add(value); break; } } return array; } object ParseValue() { TOKEN nextToken = NextToken; return ParseByToken(nextToken); } object ParseByToken(TOKEN token) { switch (token) { case TOKEN.STRING: return ParseString(); case TOKEN.NUMBER: return ParseNumber(); case TOKEN.CURLY_OPEN: return ParseObject(); case TOKEN.SQUARED_OPEN: return ParseArray(); case TOKEN.TRUE: return true; case TOKEN.FALSE: return false; case TOKEN.NULL: return null; default: return null; } } string ParseString() { StringBuilder s = new StringBuilder(); char c; // ditch opening quote json.Read(); bool parsing = true; while (parsing) { if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': parsing = false; break; case '\\': if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': case '\\': case '/': s.Append(c); break; case 'b': s.Append('\b'); break; case 'f': s.Append('\f'); break; case 'n': s.Append('\n'); break; case 'r': s.Append('\r'); break; case 't': s.Append('\t'); break; case 'u': var hex = new char[4]; for (int i = 0; i < 4; i++) { hex[i] = NextChar; if (!IsHexDigit(hex[i])) return null; } s.Append((char)Convert.ToInt32(new string(hex), 16)); break; } break; default: s.Append(c); break; } } return s.ToString(); } object ParseNumber() { string number = NextWord; if (number.IndexOf('.') == -1 && number.IndexOf('E') == -1 && number.IndexOf('e') == -1) { long parsedInt; Int64.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedInt); return parsedInt; } double parsedDouble; Double.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble); return parsedDouble; } void EatWhitespace() { while (Char.IsWhiteSpace(PeekChar)) { json.Read(); if (json.Peek() == -1) { break; } } } char PeekChar { get { return Convert.ToChar(json.Peek()); } } char NextChar { get { return Convert.ToChar(json.Read()); } } string NextWord { get { StringBuilder word = new StringBuilder(); while (!IsWordBreak(PeekChar)) { word.Append(NextChar); if (json.Peek() == -1) { break; } } return word.ToString(); } } TOKEN NextToken { get { EatWhitespace(); if (json.Peek() == -1) { return TOKEN.NONE; } switch (PeekChar) { case '{': return TOKEN.CURLY_OPEN; case '}': json.Read(); return TOKEN.CURLY_CLOSE; case '[': return TOKEN.SQUARED_OPEN; case ']': json.Read(); return TOKEN.SQUARED_CLOSE; case ',': json.Read(); return TOKEN.COMMA; case '"': return TOKEN.STRING; case ':': return TOKEN.COLON; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN.NUMBER; } switch (NextWord) { case "false": return TOKEN.FALSE; case "true": return TOKEN.TRUE; case "null": return TOKEN.NULL; } return TOKEN.NONE; } } } /// /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string /// /// A Dictionary<string, object> / List<object> /// A JSON encoded string, or null if object 'json' is not serializable public static string Serialize(object obj) { return Serializer.Serialize(obj); } sealed class Serializer { StringBuilder builder; Serializer() { builder = new StringBuilder(); } public static string Serialize(object obj) { var instance = new Serializer(); instance.SerializeValue(obj); return instance.builder.ToString(); } void SerializeValue(object value) { IList asList; IDictionary asDict; string asStr; if (value == null) { builder.Append("null"); } else if ((asStr = value as string) != null) { SerializeString(asStr); } else if (value is bool) { builder.Append((bool)value ? "true" : "false"); } else if ((asList = value as IList) != null) { SerializeArray(asList); } else if ((asDict = value as IDictionary) != null) { SerializeObject(asDict); } else if (value is char) { SerializeString(new string((char)value, 1)); } else { SerializeOther(value); } } void SerializeObject(IDictionary obj) { bool first = true; builder.Append('{'); foreach (object e in obj.Keys) { if (!first) { builder.Append(','); } SerializeString(e.ToString()); builder.Append(':'); SerializeValue(obj[e]); first = false; } builder.Append('}'); } void SerializeArray(IList anArray) { builder.Append('['); bool first = true; for (int i = 0; i < anArray.Count; i++) { object obj = anArray[i]; if (!first) { builder.Append(','); } SerializeValue(obj); first = false; } builder.Append(']'); } void SerializeString(string str) { builder.Append('\"'); char[] charArray = str.ToCharArray(); for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; switch (c) { case '"': builder.Append("\\\""); break; case '\\': builder.Append("\\\\"); break; case '\b': builder.Append("\\b"); break; case '\f': builder.Append("\\f"); break; case '\n': builder.Append("\\n"); break; case '\r': builder.Append("\\r"); break; case '\t': builder.Append("\\t"); break; default: int codepoint = Convert.ToInt32(c); if ((codepoint >= 32) && (codepoint <= 126)) { builder.Append(c); } else { builder.Append("\\u"); builder.Append(codepoint.ToString("x4")); } break; } } builder.Append('\"'); } void SerializeOther(object value) { // NOTE: decimals lose precision during serialization. // They always have, I'm just letting you know. // Previously floats and doubles lost precision too. if (value is float) { builder.Append(((float)value).ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } else if (value is int || value is uint || value is long || value is sbyte || value is byte || value is short || value is ushort || value is ulong) { builder.Append(value); } else if (value is double || value is decimal) { builder.Append(Convert.ToDouble(value).ToString("R", System.Globalization.CultureInfo.InvariantCulture)); } else { SerializeString(value.ToString()); } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/NonGenericConstrainedCallVirt.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly _ { .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class public auto ansi beforefieldinit NonGenericConstrainedCallVirt extends [mscorlib]System.Object { // Methods .method public hidebysig static void Main () cil managed { // Method begins at RVA 0x2050 // Code size 16 (0x10) .entrypoint .maxstack 8 IL_0000: ldstr "A" IL_0005: newobj instance void C::.ctor(string) IL_000a: call void NonGenericConstrainedCallVirt::Foo(class C) IL_000f: ret } // end of method NonGenericConstrainedCallVirt::Main .method private hidebysig static void Foo ( class C arg ) cil managed { // Method begins at RVA 0x2064 // Code size 25 (0x19) .maxstack 8 IL_0000: ldarga arg IL_0004: ldarga arg IL_0008: call int32 NonGenericConstrainedCallVirt::Bar(class C&) IL_000d: constrained. C IL_0013: callvirt instance void C::Baz(int32) IL_0018: ret } // end of method NonGenericConstrainedCallVirt::Foo .method private hidebysig static int32 Bar ( class C& o ) cil managed { // Method begins at RVA 0x2080 // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldstr "B" IL_0006: newobj instance void C::.ctor(string) IL_000b: stind.ref IL_000c: ldc.i4.0 IL_000d: ret } // end of method NonGenericConstrainedCallVirt::Bar .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2090 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NonGenericConstrainedCallVirt::.ctor } // end of class NonGenericConstrainedCallVirt .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { // Fields .field private initonly string 'k__BackingField' .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( string n ) cil managed { // Method begins at RVA 0x2098 // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld string C::'k__BackingField' IL_000d: ret } // end of method C::.ctor .method public hidebysig instance void Baz ( int32 arg ) cil managed { // Method begins at RVA 0x20a8 // Code size 12 (0xc) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance string C::get_Name() IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: ret } // end of method C::Baz .method public hidebysig specialname instance string get_Name () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x20b8 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string C::'k__BackingField' IL_0006: ret } // end of method C::get_Name // Properties .property instance string Name() { .get instance string C::get_Name() } } // end of class C ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class NullPropagation { static void Main() { new NullPropagation().TestNotCoalescing(); } class MyClass { public string Text; } void TestNotCoalescing() { Console.WriteLine("TestNotCoalescing:"); Console.WriteLine(NotCoalescing(null)); Console.WriteLine(NotCoalescing(new MyClass())); } string NotCoalescing(MyClass c) { return c != null ? c.Text : "Hello"; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullableTests.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class NullableTests { static void Main() { AvoidLifting(); BitNot(); FieldAccessOrderOfEvaluation(null); FieldAccessOrderOfEvaluationWithStruct(null); ArrayAccessOrderOfEvaluation(); } struct SomeStruct { public int IntField; } static void AvoidLifting() { Console.WriteLine("MayThrow:"); Console.WriteLine(MayThrow(10, 2, 3)); Console.WriteLine(MayThrow(10, 0, null)); Console.WriteLine("NotUsingAllInputs:"); Console.WriteLine(NotUsingAllInputs(5, 3)); Console.WriteLine(NotUsingAllInputs(5, null)); Console.WriteLine("UsingUntestedValue:"); Console.WriteLine(UsingUntestedValue(5, 3)); Console.WriteLine(UsingUntestedValue(5, null)); } static int? MayThrow(int? a, int? b, int? c) { // cannot be lifted without changing the exception behavior return a.HasValue & b.HasValue & c.HasValue ? a.GetValueOrDefault() / b.GetValueOrDefault() + c.GetValueOrDefault() : default(int?); } static int? NotUsingAllInputs(int? a, int? b) { // cannot be lifted because the value differs if b == null return a.HasValue & b.HasValue ? a.GetValueOrDefault() + a.GetValueOrDefault() : default(int?); } static int? UsingUntestedValue(int? a, int? b) { // cannot be lifted because the value differs if b == null return a.HasValue ? a.GetValueOrDefault() + b.GetValueOrDefault() : default(int?); } static void BitNot() { UInt32? value = 0; Assert(~value == UInt32.MaxValue); UInt64? value2 = 0; Assert(~value2 == UInt64.MaxValue); UInt16? value3 = 0; Assert((UInt16)~value3 == (UInt16)UInt16.MaxValue); UInt32 value4 = 0; Assert(~value4 == UInt32.MaxValue); UInt64 value5 = 0; Assert(~value5 == UInt64.MaxValue); UInt16 value6 = 0; Assert((UInt16)~value6 == UInt16.MaxValue); } static void Assert(bool b) { if (!b) throw new InvalidOperationException(); } static T GetValue() { Console.WriteLine("GetValue"); return default(T); } int intField; static void FieldAccessOrderOfEvaluation(NullableTests c) { Console.WriteLine("GetInt, then NRE:"); try { c.intField = GetValue(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("NRE before GetInt:"); try { #if CS70 ref int i = ref c.intField; i = GetValue(); #endif } catch (Exception ex) { Console.WriteLine(ex.Message); } } SomeStruct structField; static void FieldAccessOrderOfEvaluationWithStruct(NullableTests c) { Console.WriteLine("GetInt, then NRE (with struct):"); try { c.structField.IntField = GetValue(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("NRE before GetInt (with struct):"); try { #if CS70 ref SomeStruct s = ref c.structField; s.IntField = GetValue(); #endif } catch (Exception ex) { Console.WriteLine(ex.Message); } } static T[] GetArray() { Console.WriteLine("GetArray"); return null; } static int GetIndex() { Console.WriteLine("GetIndex"); return 0; } static void ArrayAccessOrderOfEvaluation() { Console.WriteLine("GetArray direct:"); try { GetArray()[GetIndex()] = GetValue(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("GetArray with ref:"); try { #if CS70 ref int elem = ref GetArray()[GetIndex()]; elem = GetValue(); #endif } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("GetArray direct with value-type:"); try { // This line is mis-compiled by legacy csc: // with the legacy compiler the NRE is thrown before the GetValue call; // with Roslyn the NRE is thrown after the GetValue call. GetArray()[GetIndex()] = GetValue(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { static class OverloadResolution { static void Main() { CallOverloadedMethod(); TestBoxing(); TestIssue180(); TestExtensionMethod(); TestParamsMethod(); Generics(); ConstructorTest(); TestIndexer(); Issue1281(); Issue1747(); CallAmbiguousOutParam(); CallWithInParam(); CallWithRefReadOnlyParam(); #if CS90 NativeIntTests(new IntPtr(1), 2); #endif Issue2444.M2(); Issue2741.B.Test(new Issue2741.C()); ExtensionMethodDemo.Issue2165.Test(); } #region ConstructorTest static void ConstructorTest() { new CtorTestObj(1); new CtorTestObj((short)2); new CtorTestObj(3, null); new CtorTestObj(4, null, null); } class CtorTestObj { public CtorTestObj(int i) { Console.WriteLine("CtorTestObj(int = " + i + ")"); } public CtorTestObj(short s) { Console.WriteLine("CtorTestObj(short = " + s + ")"); } public CtorTestObj(int i, object item1, object item2) : this(i, new object[] { item1, item2 }) { Console.WriteLine("CtorTestObj(int = " + i + ", item1 = " + item1 + ", item2 = " + item2); } public CtorTestObj(int i, params object[] items) { Console.WriteLine("CtorTestObj(int = " + i + ", items = " + (items == null ? "null" : items.Length.ToString()) + ")"); } } #endregion #region params with nulls static void TestParamsMethod() { TestCall(1, null, (NullReferenceException)null); TestCall(2, null, (AccessViolationException)null); TestCall(3, null); TestCall(3, null, null, null); } static void TestCall(int v, Type p1, NullReferenceException p2) { Console.WriteLine("TestCall without params"); } static void TestCall(int v, params AccessViolationException[] p2) { Console.WriteLine("TestCall with params: " + (p2 == null ? "null" : p2.Length.ToString())); } static void Issue1281() { var arg = new object[0]; TestCallIssue1281(arg); TestCallIssue1281((object)arg); TestCallIssue1281(new[] { arg }); } static void TestCallIssue1281(params object[] args) { Console.Write("TestCallIssue1281: count = " + args.Length + ": "); foreach (var arg in args) { Console.Write(arg); Console.Write(", "); } Console.WriteLine(); } #endregion #region Simple Overloaded Method static void CallOverloadedMethod() { OverloadedMethod("(string)"); OverloadedMethod((object)"(object)"); OverloadedMethod(5); OverloadedMethod((object)5); OverloadedMethod(5L); OverloadedMethod((object)null); OverloadedMethod((string)null); OverloadedMethod((int?)null); } static void OverloadedMethod(object a) { Console.WriteLine("OverloadedMethod(object={0}, object.GetType()={1})", a, a != null ? a.GetType().Name : "null"); } static void OverloadedMethod(int? a) { Console.WriteLine("OverloadedMethod(int?={0})", a); } static void OverloadedMethod(string a) { Console.WriteLine("OverloadedMethod(string={0})", a); } #endregion #region Boxing static void TestBoxing() { Print(1); Print((ushort)1); Print(null); } static void Print(object obj) { if (obj == null) Console.WriteLine("null"); else Console.WriteLine("{0}: {1}", obj.GetType().Name, obj); } #endregion #region #180 static void TestIssue180() { Issue180(null); Issue180(new object[1]); Issue180((object)new object[1]); } static void Issue180(object obj) { Console.WriteLine("#180: object"); } static void Issue180(params object[] objs) { Console.WriteLine("#180: params object[]"); } #endregion #region Extension Method static void TestExtensionMethod() { new object().ExtensionMethod(); ExtensionMethod(null); // issue #167 } public static void ExtensionMethod(this object obj) { Console.WriteLine("ExtensionMethod(obj)"); } #endregion #region Generics static void Generics() { GenericsTest(null); GenericsTest((object)null); } static void GenericsTest(string x) where T : struct { Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(string: " + x + ");"); } static void GenericsTest(object x) where T : struct { Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(object: " + x + ");"); } #endregion #region NullableValueTypes private static void Issue1747() { Console.WriteLine("Issue1747:"); M1747(null); M1747(true); M1747(false); M1747((bool?)true); M1747((bool?)false); Console.WriteLine("Issue1747, non-constant:"); bool b = Get(); M1747(b); M1747((bool?)b); } private static void M1747(bool b) { Console.WriteLine("bool=" + b); } private static void M1747(bool? b) { Console.WriteLine("bool?=" + b); } static T Get() { return default(T); } #endregion #region IndexerTests static void TestIndexer() { var obj = new IndexerTests(); Console.WriteLine(obj[(object)5]); obj[(object)5] = null; Console.WriteLine(obj[5]); obj[5] = null; } #endregion #region Out Parameter static void AmbiguousOutParam(out string a) { a = null; Console.WriteLine("AmbiguousOutParam(out string)"); } static void AmbiguousOutParam(out int b) { b = 1; Console.WriteLine("AmbiguousOutParam(out int)"); } static void CallAmbiguousOutParam() { Console.WriteLine("CallAmbiguousOutParam:"); string a; int b; AmbiguousOutParam(out a); AmbiguousOutParam(out b); } #endregion #region Ref readonly Parameter static void CallWithRefReadOnlyParam() { #if CS120 #pragma warning disable CS9193 Console.WriteLine("OverloadSetWithRefReadOnlyParam:"); OverloadSetWithRefReadOnlyParam(1); OverloadSetWithRefReadOnlyParam(2L); int i = 3; OverloadSetWithRefReadOnlyParam(in i); OverloadSetWithRefReadOnlyParam((long)4); Console.WriteLine("OverloadSetWithRefReadOnlyParam2:"); OverloadSetWithRefReadOnlyParam2(1); OverloadSetWithRefReadOnlyParam2((object)1); Console.WriteLine("OverloadSetWithRefReadOnlyParam3:"); OverloadSetWithRefReadOnlyParam3(1); OverloadSetWithRefReadOnlyParam3(2); OverloadSetWithRefReadOnlyParam3((object)3); Console.WriteLine("RefReadOnlyVsRegularParam:"); RefReadOnlyVsRegularParam(1); i = 2; RefReadOnlyVsRegularParam(in i); #endif } #if CS120 static void OverloadSetWithRefReadOnlyParam(ref readonly int i) { Console.WriteLine("ref readonly int " + i); } static void OverloadSetWithRefReadOnlyParam(long l) { Console.WriteLine("long " + l); } static void OverloadSetWithRefReadOnlyParam2(ref readonly long i) { Console.WriteLine("ref readonly long " + i); } static void OverloadSetWithRefReadOnlyParam2(object o) { Console.WriteLine("object " + o); } static void OverloadSetWithRefReadOnlyParam3(ref readonly int i) { Console.WriteLine("ref readonly int " + i); } static void OverloadSetWithRefReadOnlyParam3(T a) { Console.WriteLine("T " + a); } static void RefReadOnlyVsRegularParam(ref readonly int i) { Console.WriteLine("ref readonly int " + i); } static void RefReadOnlyVsRegularParam(int i) { Console.WriteLine("int " + i); } #endif #endregion #region In Parameter static void CallWithInParam() { #if CS72 Console.WriteLine("OverloadSetWithInParam:"); OverloadSetWithInParam(1); OverloadSetWithInParam(2L); int i = 3; OverloadSetWithInParam(in i); OverloadSetWithInParam((long)4); Console.WriteLine("OverloadSetWithInParam2:"); OverloadSetWithInParam2(1); OverloadSetWithInParam2((object)1); Console.WriteLine("OverloadSetWithInParam3:"); OverloadSetWithInParam3(1); OverloadSetWithInParam3(2); OverloadSetWithInParam3((object)3); Console.WriteLine("InVsRegularParam:"); InVsRegularParam(1); i = 2; InVsRegularParam(in i); #endif } #if CS72 static void OverloadSetWithInParam(in int i) { Console.WriteLine("in int " + i); } static void OverloadSetWithInParam(long l) { Console.WriteLine("long " + l); } static void OverloadSetWithInParam2(in long i) { Console.WriteLine("in long " + i); } static void OverloadSetWithInParam2(object o) { Console.WriteLine("object " + o); } static void OverloadSetWithInParam3(in int i) { Console.WriteLine("in int " + i); } static void OverloadSetWithInParam3(T a) { Console.WriteLine("T " + a); } static void InVsRegularParam(in int i) { Console.WriteLine("in int " + i); } static void InVsRegularParam(int i) { Console.WriteLine("int " + i); } #endif #endregion #if CS90 static void NativeIntTests(IntPtr i1, nint i2) { Console.WriteLine("NativeIntTests(i1):"); ObjectOrLong((object)i1); ObjectOrLong((long)i1); Console.WriteLine("NativeIntTests(i2):"); ObjectOrLong((object)i2); ObjectOrLong((long)i2); Console.WriteLine("NativeIntTests(new IntPtr):"); ObjectOrLong((object)new IntPtr(3)); ObjectOrLong((long)new IntPtr(3)); Console.WriteLine("NativeIntTests(IntPtr.Zero):"); ObjectOrLong((object)IntPtr.Zero); ObjectOrLong((long)IntPtr.Zero); } static void ObjectOrLong(object o) { Console.WriteLine("object " + o); } static void ObjectOrLong(long l) { Console.WriteLine("long " + l); } #endif #region #2444 public struct Issue2444 { public class X { } public class Y { } public static implicit operator Issue2444(X x) { Console.WriteLine("#2444: op_Implicit(X)"); return new Issue2444(); } public static implicit operator Issue2444(Y y) { Console.WriteLine("#2444: op_Implicit(Y)"); return new Issue2444(); } public static void M1(Issue2444 z) { Console.WriteLine(string.Format("#2444: M1({0})", z)); } public static void M2() { Console.WriteLine("#2444: before M1"); M1((X)null); Console.WriteLine("#2444: after M1"); } } public class Issue2741 { public class B { private void M() { Console.WriteLine("B::M"); } protected void M2() { Console.WriteLine("B::M2"); } protected void M3() { Console.WriteLine("B::M3"); } protected void M4() { Console.WriteLine("B::M4"); } public static void Test(C c) { ((B)c).M(); ((B)c).M2(); c.Test(); } } public class C : B { public void M() { Console.WriteLine("C::M"); } public new void M2() { Console.WriteLine("C::M2"); } public new void M3() { Console.WriteLine("C::M3"); } public void Test() { M3(); base.M3(); M4(); } } } #endregion } class IndexerTests { public object this[object key] { get { Console.WriteLine("IndexerTests.get_Item(object key)"); return new object(); } set { Console.WriteLine("IndexerTests.set_Item(object key, object value)"); } } public object this[int key] { get { Console.WriteLine("IndexerTests.get_Item(int key)"); return new object(); } set { Console.WriteLine("IndexerTests.set_Item(int key, object value)"); } } } } namespace ExtensionMethodDemo { // First extension class with an out int parameter public static class StringExtensions { public static bool TryParseCustom(this string input, out int result) { return int.TryParse(input, out result); } } // Second extension class with an out double parameter public static class StringDoubleExtensions { public static bool TryParseCustom(this string input, out double result) { return double.TryParse(input, out result); } } class Issue2165 { public static void Test() { string value1 = "123"; string value2 = "123.45"; #if CS70 // Use the int version with extension method syntax if (value1.TryParseCustom(out int intResult)) { Console.WriteLine("Parsed int: " + intResult); } // Use the double version with extension method syntax if (value2.TryParseCustom(out double doubleResult)) { Console.WriteLine("Parsed double: " + doubleResult); } #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/PropertiesAndEvents.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class PropertiesAndEvents { public static int Main(string[] args) { Index i = new Index(); i.AutoProp = "Name"; Console.WriteLine("AutoProp set!"); i[0] = 5; i[1] = 2; Console.WriteLine("{0} {1}", i[0], i[5]); Console.WriteLine("PI² = {0}", i.PISquare); return 0; } } class Index { int thisValue; public int this[int i] { get { Console.WriteLine("get_this({0})", i); return i * i; } set { Console.WriteLine("set_this({0}, {1})", i, value); thisValue = value; } } public string AutoProp { get; set; } public double PISquare { get { Console.WriteLine("get_PISquare"); return Math.Pow(Math.PI, 2); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Readme.txt ================================================ The files in this folder are correctness tests for the decompiler. The NUnit class running these tests is ../../CorrectnessTestRunner.cs. We: * compile/assemble a test case (call the result "executable 1") * decompile "executable 1" to C# ("decompiled.cs") * compile decompiled.cs, resulting in "executable 2" * run both executable and compare their output (exit code, stdout, stderr) We repeat the steps above with a few different compiler options (/o+ or not; /debug or not). The tests pass if the code compiles without error and produces the same output. The tests do not care at all if the resulting code is pretty, or if any high-level constructs like closures were detected. ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTests.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly StackTests { .hash algorithm 0x00008004 .ver 1:0:4059:39717 } .module StackTests.exe .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000003 // ILONLY 32BITREQUIRED .class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object { .method public hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 8 ldc.i4.0 call string StackTests.Program::Test1(bool cond) call void [mscorlib]System.Console::WriteLine(string) // false ldc.i4.1 call string StackTests.Program::Test1(bool cond) call void [mscorlib]System.Console::WriteLine(string) // true ldc.i4.0 ldc.i4.0 ldc.i4.0 call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) call void [mscorlib]System.Console::WriteLine(int32) // 11 ldc.i4.0 ldc.i4.1 ldc.i4.0 call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) call void [mscorlib]System.Console::WriteLine(int32) // 21 ldc.i4.1 ldc.i4.1 ldc.i4.1 call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) call void [mscorlib]System.Console::WriteLine(int32) // 32 ldc.i4.2 ldc.i4.1 ldc.i4.0 call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) call void [mscorlib]System.Console::WriteLine(int32) // 23 ret } .method public hidebysig static string Test1(bool cond) cil managed { ldarg.0 brtrue TRUE FALSE: ldstr "false" br EXIT TRUE: ldstr "true" EXIT: ret } .method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed { ldarg.0 switch (ENTRY1, ENTRY2, ENTRY3) ldc.i4.0 ret ENTRY1: ldc.i4.1 br BRANCH1 ENTRY2: ldc.i4.2 br BRANCH1 ENTRY3: ldc.i4.3 br BRANCH2 BRANCH1: ldarg.1 brtrue BRANCH2 EXIT1: ldc.i4 10 add ret BRANCH2: ldarg.2 brtrue.s EXIT3 EXIT2: ldc.i4 20 add ret EXIT3: ldc.i4 30 add ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Program::.ctor } // end of class StackTests.Program // ============================================================= ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly 'StackTypes' { .ver 0:0:0:0 } .module StackTypes.exe .corflags 0x00000001 // ILOnly .class private auto ansi abstract sealed beforefieldinit Program extends [mscorlib]System.Object { .method public hidebysig static void Main (string[] args) cil managed { .maxstack 8 .entrypoint call void Program::InlineAssignByte() call void Program::Int32OrNativeTests() call void Program::ByRefInstanceCallWithTypeMismatchTests() ret } // end of method Main .method public static void InlineAssignByte() { .locals init ( int8 local ) ldstr "InlineAssignByte: WriteLine(local = {0})" ldc.i4 300 dup br pointless // this pointless branch is a workaround for https://github.com/dotnet/coreclr/issues/14784 // it doesn't have any effect on ILSpy as TransformAssignment runs after control-flow reconstruction pointless: // This assignment cannot be turned into a C# inline assignment, because doing so would truncate to 8 bits. stloc.0 box int32 call void [mscorlib]System.Console::WriteLine(string, object) ldstr "InlineAssignByte: local is {0}" ldloc.0 box int32 call void [mscorlib]System.Console::WriteLine(string, object) ret } .method public static void Int32OrNativeTests() { ldstr "Int32OrNative(0x7fffffff, false) = {0}" ldc.i4 0x7fffffff ldc.i4 0 call native int Program::Int32OrNative(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNative(0x7fffffff, true) = {0}" ldc.i4 0x7fffffff ldc.i4 1 call native int Program::Int32OrNative(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNative(-1, false) = {0}" ldc.i4.m1 ldc.i4 0 call native int Program::Int32OrNative(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNative(-1, true) = {0}" ldc.i4.m1 ldc.i4 1 call native int Program::Int32OrNative(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeReordered(0x7fffffff, false) = {0}" ldc.i4 0x7fffffff ldc.i4 0 call native int Program::Int32OrNativeReordered(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeReordered(0x7fffffff, true) = {0}" ldc.i4 0x7fffffff ldc.i4 1 call native int Program::Int32OrNativeReordered(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeReordered(-1, false) = {0}" ldc.i4.m1 ldc.i4 0 call native int Program::Int32OrNativeReordered(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeReordered(-1, true) = {0}" ldc.i4.m1 ldc.i4 1 call native int Program::Int32OrNativeReordered(int32, bool) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeLoopStyle(0x7fffffff):" call void [mscorlib]System.Console::WriteLine(string) ldc.i4 0x7fffffff call void Program::Int32OrNativeLoopStyle(int32) ldstr "Int32OrNativeLoopStyle(-1):" call void [mscorlib]System.Console::WriteLine(string) ldc.i4.m1 call void Program::Int32OrNativeLoopStyle(int32) ldstr "Int32OrNativeDeadCode(0x7fffffff) = {0}" ldc.i4 0x7fffffff call native int Program::Int32OrNativeDeadCode(int32) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Int32OrNativeDeadCode(-1) = {0}" ldc.i4.m1 call native int Program::Int32OrNativeDeadCode(int32) box native int call void [mscorlib]System.Console::WriteLine(string, object) ldc.i4 0x7fffffff call void Program::RunInt32OrNativeMultiUse(int32) ldc.i4.m1 call void Program::RunInt32OrNativeMultiUse(int32) ret } .method public static native int Int32OrNative(int32 val, bool use_native) { ldarg.1 brtrue use_native_int use_i4: ldarg.0 br after_if use_native_int: ldarg.0 conv.u br after_if after_if: ldc.i4.1 add ret } .method public static native int Int32OrNativeReordered(int32 val, bool use_native) { // The spec is ambiguous whether the addition will be in 32-bits or native size. // The microsoft runtime picks native size. ldarg.1 brtrue use_native_int use_i4: ldarg.0 br after_if after_if: ldc.i4.1 add ret use_native_int: ldarg.0 conv.u br after_if } .method public static void Int32OrNativeLoopStyle(int32 val) { // The spec is ambiguous whether the addition will be in 32-bits or native size. // The microsoft runtime picks native size. .locals init ( int32 i ) ldarg.0 loop: ldc.i4.1 add call void Program::Print(native int) ldloc.0 brtrue end ldc.i4.1 stloc.0 ldarg.0 conv.u br loop end: ret } .method public static native int Int32OrNativeDeadCode(int32 val) { // The spec is ambiguous whether the addition will be in 32-bits or native size. // The microsoft runtime picks 32-bits. use_i4: ldarg.0 br after_if after_if: ldc.i4.1 add ret use_native_int: // dead code ldarg.0 conv.u br after_if } .method public static void RunInt32OrNativeMultiUse(int32 val) { ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: false) = {1}" ldarg val box int32 ldarg val ldc.i4 0 // push_i ldc.i4 0 // use2 call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2) box native int call void [mscorlib]System.Console::WriteLine(string, object, object) ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: true) = {1}" ldarg val box int32 ldarg val ldc.i4 0 // push_i ldc.i4 1 // use2 call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2) box native int call void [mscorlib]System.Console::WriteLine(string, object, object) ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: false) = {1}" ldarg val box int32 ldarg val ldc.i4 1 // push_i ldc.i4 0 // use2 call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2) box native int call void [mscorlib]System.Console::WriteLine(string, object, object) ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: true) = {1}" ldarg val box int32 ldarg val ldc.i4 1 // push_i ldc.i4 1 // use2 call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2) box native int call void [mscorlib]System.Console::WriteLine(string, object, object) ret } .method public static native int Int32OrNativeMultiUse(int32 val, bool push_i, bool use2) { ldarg.1 brtrue push_i br push_i4 push_i4: ldarg.0 ldarg.2 brtrue use2 br use1 push_i: ldarg.0 conv.u br use1 use1: ldc.i4.1 add ret use2: ldc.i4.2 add ret } .method public static void RefOrNull(uint8& r, bool use_null) { // Not quite valid IL due to ref<->I mismatch. // https://github.com/icsharpcode/ILSpy/issues/981 ldarg.1 brtrue push_null ldarg.0 br done push_null: ldc.i4.0 conv.u done: call void Program::UseRef(uint8&) ret } .method public static void UseRef(uint8& r) { ret } .method public static void Print(native int val) { ldarg.0 box native int call void [mscorlib]System.Console::WriteLine(object) ret } .method public static void ByRefInstanceCallWithTypeMismatchTests() { ldstr "ByRefInstanceCallWithTypeMismatch(0) = {0}" ldc.i4.0 call string Program::ByRefInstanceCallWithTypeMismatch(int32) call void [mscorlib]System.Console::WriteLine(string, object) ldstr "ByRefInstanceCallWithTypeMismatch(1) = {0}" ldc.i4.1 call string Program::ByRefInstanceCallWithTypeMismatch(int32) call void [mscorlib]System.Console::WriteLine(string, object) ldstr "Issue1333() = {0}" call string Program::Issue1333() call void [mscorlib]System.Console::WriteLine(string, object) ret } .method public hidebysig static string ByRefInstanceCallWithTypeMismatch(int32 val) cil managed { ldarga val constrained. [mscorlib]System.Boolean callvirt instance string [mscorlib]System.Object::ToString() ret } .method public hidebysig static string Issue1333() cil managed { .locals (bool) ldc.i4.0 stloc.0 ldloca.s 0 constrained. [mscorlib]System.Boolean callvirt instance string [mscorlib]System.Object::ToString() ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs ================================================ using System; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class StringConcat { private class C { int i; public C(int i) { Console.WriteLine(" new C(" + i + ")"); this.i = i; } public override string ToString() { Console.WriteLine(" C(" + i + ").ToString()"); return (i++).ToString(); } } private struct S { int i; public S(int i) { Console.WriteLine(" new C(" + i + ")"); this.i = i; } public override string ToString() { Console.WriteLine(" S(" + i + ").ToString()"); return (i++).ToString(); } } static string Space() { Console.WriteLine(" Space()"); return " "; } static void TestClass() { Console.WriteLine("string + C:"); Console.WriteLine(Space() + new C(1)); Console.WriteLine("C + string:"); Console.WriteLine(new C(2) + Space()); Console.WriteLine("C + string + C:"); Console.WriteLine(new C(3) + Space() + new C(4)); Console.WriteLine("string + C + C:"); Console.WriteLine(Space() + new C(5) + new C(6)); Console.WriteLine("string.Concat(C, string, C):"); Console.WriteLine(string.Concat(new C(10), Space(), new C(11))); Console.WriteLine("string.Concat(string.Concat(C, string), C):"); Console.WriteLine(string.Concat(string.Concat(new C(15), Space()), new C(16))); Console.WriteLine("string.Concat(C, string.Concat(string, C)):"); Console.WriteLine(string.Concat(new C(20), string.Concat(Space(), new C(21)))); Console.WriteLine("string.Concat(C, string) + C:"); Console.WriteLine(string.Concat(new C(30), Space()) + new C(31)); } static void TestStruct() { Console.WriteLine("string + S:"); Console.WriteLine(Space() + new S(1)); Console.WriteLine("S + string:"); Console.WriteLine(new S(2) + Space()); Console.WriteLine("S + string + S:"); Console.WriteLine(new S(3) + Space() + new S(4)); Console.WriteLine("string + S + S:"); Console.WriteLine(Space() + new S(5) + new S(6)); Console.WriteLine("string.Concat(S, string, S):"); Console.WriteLine(string.Concat(new S(10), Space(), new S(11))); Console.WriteLine("string.Concat(string.Concat(S, string), S):"); Console.WriteLine(string.Concat(string.Concat(new S(15), Space()), new S(16))); Console.WriteLine("string.Concat(S, string.Concat(string, S)):"); Console.WriteLine(string.Concat(new S(20), string.Concat(Space(), new S(21)))); Console.WriteLine("string.Concat(S, string) + S:"); Console.WriteLine(string.Concat(new S(30), Space()) + new S(31)); } static void TestStructMutation() { Console.WriteLine("TestStructMutation:"); S s = new S(0); Console.WriteLine(Space() + s); Console.WriteLine(Space() + s.ToString()); Console.WriteLine(s); } static void TestCharPlusChar(string a) { Console.WriteLine("TestCharPlusChar:"); Console.WriteLine(a[0] + a[1]); Console.WriteLine(a[0].ToString() + a[1].ToString()); } #if NET60 && ROSLYN2 static void TestManualDefaultStringInterpolationHandler() { Console.WriteLine("TestManualDefaultStringInterpolationHandler:"); C c = new C(42); DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(0, 1); defaultInterpolatedStringHandler.AppendFormatted(c); M2(Space(), defaultInterpolatedStringHandler.ToStringAndClear()); } static void M2(object x, string y) { } #endif static void Main() { TestClass(); TestStruct(); TestStructMutation(); TestCharPlusChar("ab"); #if NET60 && ROSLYN2 TestManualDefaultStringInterpolationHandler(); #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public static class Switch { public static void Main() { TestCase(SparseIntegerSwitch, -100, 1, 2, 3, 4); TestCase(ShortSwitchOverString, "First case", "Else"); TestCase(ShortSwitchOverString2, "First case", "Second case", "Third case", "Else"); TestCase(ShortSwitchOverStringNoExplicitDefault, "First case", "Second case", "Third case", "Else"); TestCase(SwitchOverString1, "First case", "Second case", "2nd case", "Third case", "Fourth case", "Fifth case", "Sixth case", null, "default", "else"); Console.WriteLine(SwitchOverString2()); Console.WriteLine(SwitchOverBool(true)); Console.WriteLine(SwitchOverBool(false)); SwitchInLoop(0); SwitchWithGoto(1); SwitchWithGoto(2); SwitchWithGoto(3); SwitchWithGoto(4); } static void TestCase(Func target, params T[] args) { foreach (var arg in args) { Console.WriteLine(target(arg)); } } public static string SparseIntegerSwitch(int i) { Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: return "-10 mln"; case -100: return "-hundred"; case -1: return "-1"; case 0: return "0"; case 1: return "1"; case 2: return "2"; case 4: return "4"; case 100: return "hundred"; case 10000: return "ten thousand"; case 10001: return "ten thousand and one"; case int.MaxValue: return "int.MaxValue"; default: return "something else"; } } public static string ShortSwitchOverString(string text) { Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text"; default: return "Default"; } } public static string ShortSwitchOverString2(string text) { Console.WriteLine("ShortSwitchOverString2: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; default: return "Default"; } } public static string ShortSwitchOverStringNoExplicitDefault(string text) { Console.WriteLine("ShortSwitchOverStringNoExplicitDefault: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; } return "Default"; } public static string SwitchOverString1(string text) { Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case null: return null; default: return "Default"; } } public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } public static string SwitchOverBool(bool b) { Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; case false: return bool.FalseString; default: #pragma warning disable CS0162 return null; #pragma warning restore CS0162 } } public static void SwitchInLoop(int i) { Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: Console.WriteLine("one"); break; case 2: Console.WriteLine("two"); break; case 3: Console.WriteLine("three"); continue; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); Console.WriteLine("more code"); return; } i++; } } public static void SwitchWithGoto(int i) { Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: Console.WriteLine("one"); goto default; case 2: Console.WriteLine("two"); goto case 3; case 3: Console.WriteLine("three"); break; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); break; } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs ================================================ // Copyright (c) 2017 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 // 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.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class TrickyTypes { static void Main() { InterestingConstants(); TruncatedComp(); StringConcat(); LinqNullableMin(); LinqNullableMin(1, 2, 3); UnboxingToWrongType(); } static void Print(T val) { Console.Write(typeof(T).Name + ": "); if (val == null) Console.WriteLine("null"); else Console.WriteLine(val); } static void InterestingConstants() { long val1 = 2147483648L; uint val2 = 2147483648u; Console.WriteLine("InterestingConstants:"); Print(val1); Print(2147483648L); Print(val2); Print(2147483648u); } static void TruncatedComp() { Console.WriteLine("TruncatedComp1(1):"); TruncatedComp1(1); Console.WriteLine("TruncatedComp1(-1):"); TruncatedComp1(-1); Console.WriteLine("TruncatedComp1(0x100000001):"); TruncatedComp1(0x100000001); Console.WriteLine("TruncatedComp1(long.MinValue):"); TruncatedComp1(long.MinValue); Console.WriteLine("TruncatedComp2(1):"); TruncatedComp2(1, 1); Console.WriteLine("TruncatedComp2(-1):"); TruncatedComp2(-1, -1); Console.WriteLine("TruncatedComp2(0x100000001):"); TruncatedComp2(0x100000001, 1); Console.WriteLine("TruncatedComp2(long.MinValue):"); TruncatedComp2(long.MinValue, int.MinValue); } static void TruncatedComp1(long val) { Print((int)val == val); Print(val == (int)val); Print(val < (int)val); Print((int)val >= val); } static void TruncatedComp2(long val1, int val2) { Print(val1 == val2); Print((int)val1 == val2); Print(val1 < val2); Print((int)val1 < val2); Print(val1 <= val2); Print((int)val1 <= val2); } static void StringConcat() { // Some string.Concat()-cases that cannot be replaced using operator+ Print(string.Concat("String concat:")); Print(string.Concat(1, 2)); Print(string.Concat(1, 2, "str")); } static void LinqNullableMin(params int[] arr) { Print(string.Format("LinqNullableMin {0}:", arr.Length)); Print(arr.Min(v => (int?)v)); } static void UnboxingToWrongType() { Console.WriteLine("UnboxingToWrongType:"); object o = 3.50; try { Print((long)o); } catch (Exception ex) { Console.WriteLine(ex.GetType().Name + ": " + ex.Message); } try { Print(o as long?); } catch (Exception ex) { Console.WriteLine(ex.GetType().Name + ": " + ex.Message); } try { Print((long)(o as long?)); } catch (Exception ex) { Console.WriteLine(ex.GetType().Name + ": " + ex.Message); } try { Print((long)(o is long ? o : null)); } catch (Exception ex) { Console.WriteLine(ex.GetType().Name + ": " + ex.Message); } #if CS90 if (o is not 0L) { Console.WriteLine("Not 0L"); } #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/UndocumentedExpressions.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class UndocumentedExpressions { static void Main(string[] args) { MakeTypedRef("abc"); VarArgs(1, __arglist()); VarArgs(__arglist(1)); VarArgs(1, __arglist("abc", 2, true)); VarArgs(1, __arglist((object)"abc", 2, true)); VarArgs(1, __arglist((short)1)); VarArgs(1, __arglist(ConsoleColor.Red)); } public static void VarArgs(int normalArg, __arglist) { ArgIterator argIterator = new ArgIterator(__arglist); Console.WriteLine("Called with {0} arguments", argIterator.GetRemainingCount()); int pos = 0; while (argIterator.GetRemainingCount() > 0) { TypedReference tr = argIterator.GetNextArg(); object val; try { val = __refvalue(tr, object); } catch (Exception ex) { val = ex.GetType().Name; } Console.WriteLine("{0} : {1} = {2}", pos++, __reftype(tr).Name, val); } } public static void VarArgs(__arglist) { Console.WriteLine("The other varargs overload"); } public static void MakeTypedRef(object o) { TypedReference tr = __makeref(o); UndocumentedExpressions.AcceptTypedRef(tr); } private static void AcceptTypedRef(TypedReference tr) { Console.WriteLine("Value is: " + __refvalue(tr, object).ToString()); Console.WriteLine("Type is: " + __reftype(tr).Name); __refvalue(tr, object) = 1; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Uninit.vb ================================================ Imports System Module UninitTest Sub Main() Dim i = 0 Dim result As Integer = -5 Dim num As Integer Do If num > result Then num += 5 result += 5 End If result += 1 i += 1 If i > 10 Then Exit Do End If Loop Console.WriteLine(result) End Sub End Module ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/UnsafeCode.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class UnsafeCode { private struct SimpleStruct { public int X; public double Y; } static void Main() { // TODO: test behavior, or convert this into a pretty-test // (but for now, it's already valuable knowing whether the decompiled code can be re-compiled) } public unsafe int MultipleExitsOutOfFixedBlock(int[] arr) { fixed (int* ptr = &arr[0]) { if (*ptr < 0) return *ptr; if (*ptr == 21) return 42; if (*ptr == 42) goto outside; } return 1; outside: Console.WriteLine("outside"); return 2; } public unsafe void FixMultipleStrings(string text) { fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text) { *ptr = 'c'; *userName = 'd'; *ptr2 = 'e'; } } public unsafe byte* PointerArithmetic2(long* p, int y, int x) { return (byte*)((short*)p + (y * x)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { class Using { class PrintOnDispose : IDisposable { private string v; public PrintOnDispose(string v) { this.v = v; } public void Dispose() { Console.WriteLine(this.v); } } static void Main() { SimpleUsingNullStatement(); NoUsingDueToAssignment(); NoUsingDueToAssignment2(); NoUsingDueToByRefCall(); NoUsingDueToContinuedDisposableUse(); ContinuedObjectUse(); VariableAlreadyUsedBefore(); UsingObject(); } /// /// Special case: Roslyn eliminates the try-finally altogether. /// public static void SimpleUsingNullStatement() { Console.WriteLine("before using"); // Mono has a compiler bug and introduces an assembly reference to [gmcs] here... #if !MCS using (null) { Console.WriteLine("using (null)"); } #endif Console.WriteLine("after using"); } public static void NoUsingDueToAssignment() { PrintOnDispose printOnDispose = new PrintOnDispose("Wrong"); try { printOnDispose = new PrintOnDispose("Correct"); } finally { printOnDispose.Dispose(); } } public static void NoUsingDueToAssignment2() { PrintOnDispose printOnDispose = new PrintOnDispose("NoUsing(): Wrong"); try { printOnDispose = new PrintOnDispose("NoUsing(): Correct"); } finally { IDisposable disposable = (object)printOnDispose as IDisposable; if (disposable != null) { disposable.Dispose(); } } } static void Clear(ref T t) { t = default(T); } public static void NoUsingDueToByRefCall() { PrintOnDispose printOnDispose = new PrintOnDispose("NoUsingDueToByRefCall(): Wrong"); try { Console.WriteLine("NoUsingDueToByRefCall"); Clear(ref printOnDispose); } finally { IDisposable disposable = (object)printOnDispose as IDisposable; if (disposable != null) { disposable.Dispose(); } } } public static void NoUsingDueToContinuedDisposableUse() { var obj = new System.IO.StringWriter(); IDisposable disposable; try { obj.WriteLine("NoUsingDueToContinuedDisposableUse"); } finally { disposable = (object)obj as IDisposable; if (disposable != null) { disposable.Dispose(); } } Console.WriteLine(disposable); } public static void ContinuedObjectUse() { var obj = new System.IO.StringWriter(); try { obj.WriteLine("ContinuedObjectUse"); } finally { IDisposable disposable = (object)obj as IDisposable; if (disposable != null) { disposable.Dispose(); } } Console.WriteLine(obj); } public static void VariableAlreadyUsedBefore() { System.IO.StringWriter obj = new System.IO.StringWriter(); obj.Write("VariableAlreadyUsedBefore - 1"); Console.WriteLine(obj); obj = new System.IO.StringWriter(); try { obj.WriteLine("VariableAlreadyUsedBefore - 2"); } finally { IDisposable disposable = (object)obj as IDisposable; if (disposable != null) { disposable.Dispose(); } } } public static void UsingObject() { object obj = new object(); try { Console.WriteLine("UsingObject: {0}", obj); } finally { IDisposable disposable = obj as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs ================================================ using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public struct MutValueType : IDisposable { public int val; public void Increment() { Console.WriteLine("Inc() called on {0}", val); val = val + 1; } public void Dispose() { Console.WriteLine("MutValueType disposed on {0}", val); val = val + 1; } public override string ToString() { return "MutValueType.ToString() " + (++val); } } public struct GenericValueType { T data; int num; public GenericValueType(T data) { this.data = data; this.num = 1; } public void Call(ref GenericValueType v) { num++; Console.WriteLine("Call #{0}: {1} with v=#{2}", num, data, v.num); } } public struct ValueTypeWithReadOnlyMember { public readonly int Member; public ValueTypeWithReadOnlyMember(int member) { this.Member = member; } } public class ValueTypeCall { public static void Main() { MutValueType m = new MutValueType(); RefParameter(ref m); ValueParameter(m); Field(); Box(); BoxToStringCalls(); Using(); var gvt = new GenericValueType("Test"); gvt.Call(ref gvt); new ValueTypeCall().InstanceFieldTests(); ForEach(); #if CS73 DisposeMultipleTimes(ref m, in m); ToStringGeneric(ref m, in m); #endif } static void RefParameter(ref MutValueType m) { m.Increment(); m.Increment(); } static void ValueParameter(MutValueType m) { m.Increment(); m.Increment(); } static readonly MutValueType ReadonlyField = new MutValueType { val = 100 }; static MutValueType MutableField = new MutValueType { val = 200 }; static void Field() { ReadonlyField.Increment(); ReadonlyField.Increment(); MutableField.Increment(); MutableField.Increment(); // Ensure that 'v' isn't incorrectly removed // as a compiler-generated temporary MutValueType v = MutableField; v.Increment(); Console.WriteLine("Final value in MutableField: " + MutableField.val); // Read-only field copies cannot be inlined for static methods: MutValueType localCopy = ReadonlyField; RefParameter(ref localCopy); } static void Box() { Console.WriteLine("Box"); object o = new MutValueType { val = 300 }; ((MutValueType)o).Increment(); ((MutValueType)o).Increment(); MutValueType unboxed1 = (MutValueType)o; unboxed1.Increment(); unboxed1.Increment(); ((MutValueType)o).Increment(); MutValueType unboxed2 = (MutValueType)o; unboxed2.val = 100; ((MutValueType)o).Dispose(); } static void BoxToStringCalls() { Console.WriteLine("BoxToStringCalls:"); MutValueType m = new MutValueType { val = 400 }; Console.WriteLine(m.ToString()); Console.WriteLine(((object)m).ToString()); Console.WriteLine(m.ToString()); } MutValueType instanceField; ValueTypeWithReadOnlyMember mutableInstanceFieldWithReadOnlyMember; void InstanceFieldTests() { this.instanceField.val = 42; Console.WriteLine(this.instanceField.val); mutableInstanceFieldWithReadOnlyMember = new ValueTypeWithReadOnlyMember(45); Console.WriteLine(this.mutableInstanceFieldWithReadOnlyMember.Member); } static void Using() { Using1(); Using2(); Using3(); } static void Using1() { Console.WriteLine("Using:"); using (var x = new MutValueType()) { x.Increment(); } } static void Using2() { Console.WriteLine("Not using:"); var y = new MutValueType(); try { y.Increment(); } finally { MutValueType x = y; x.Dispose(); } } static void Using3() { Console.WriteLine("Using with variable declared outside:"); MutValueType z; using (z = new MutValueType()) { z.Increment(); } } static void ForEach() { var list = new List { new MutValueType { val = 10 }, new MutValueType { val = 20 }, new MutValueType { val = 30 }, }; ForEach1(list); var array = new MutValueType[] { new MutValueType { val = 100 }, new MutValueType { val = 200 }, new MutValueType { val = 300 }, }; ForEachArray1(array); } static void ForEach1(List list) { Console.WriteLine("ForEach1:"); foreach (var val in list) { val.Increment(); val.Increment(); } Console.WriteLine("after: " + list[0].val); } static void ForEachArray1(MutValueType[] list) { Console.WriteLine("ForEachArray1:"); foreach (var val in list) { val.Increment(); val.Increment(); } Console.WriteLine("after: " + list[0].val); } #if CS73 static void DisposeMultipleTimes(ref T mutRef, in T immutableRef) where T : struct, IDisposable { Console.WriteLine("DisposeMultipleTimes:"); mutRef.Dispose(); mutRef.Dispose(); T copyFromMut = mutRef; copyFromMut.Dispose(); immutableRef.Dispose(); immutableRef.Dispose(); T copyFromImmutable = immutableRef; copyFromImmutable.Dispose(); mutRef.Dispose(); immutableRef.Dispose(); } static void ToStringGeneric(ref T mutRef, in T immutableRef) where T : struct { Console.WriteLine("ToStringGeneric:"); Console.WriteLine(mutRef.ToString()); Console.WriteLine(mutRef.ToString()); T copyFromMut = mutRef; Console.WriteLine(copyFromMut.ToString()); Console.WriteLine(immutableRef.ToString()); Console.WriteLine(immutableRef.ToString()); T copyFromImmutable = immutableRef; Console.WriteLine(copyFromImmutable.ToString()); Console.WriteLine(mutRef.ToString()); Console.WriteLine(immutableRef.ToString()); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Correctness/YieldReturn.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public class YieldReturnTest { static void Main() { Print("SimpleYieldReturn", SimpleYieldReturn().GetEnumerator()); Print("SimpleYieldReturnEnumerator", SimpleYieldReturnEnumerator()); Print("YieldReturnParameters", new YieldReturnTest { fieldOnThis = 1 }.YieldReturnParameters(2).GetEnumerator()); Print("YieldReturnParametersEnumerator", new YieldReturnTest { fieldOnThis = 1 }.YieldReturnParametersEnumerator(2)); Print("YieldReturnInLoop", YieldReturnInLoop().GetEnumerator()); Print("YieldReturnWithTryFinally", YieldReturnWithTryFinally().GetEnumerator()); Print("YieldReturnInLock1", YieldReturnInLock1(new object()).GetEnumerator()); Print("YieldReturnInLock2", YieldReturnInLock2(new object()).GetEnumerator()); Print("YieldReturnWithNestedTryFinally(false)", YieldReturnWithNestedTryFinally(false).GetEnumerator()); Print("YieldReturnWithNestedTryFinally(true)", YieldReturnWithNestedTryFinally(true).GetEnumerator()); Print("YieldReturnWithTwoNonNestedFinallyBlocks", YieldReturnWithTwoNonNestedFinallyBlocks(SimpleYieldReturn()).GetEnumerator()); // TODO: check anon methods Print("GetEvenNumbers", GetEvenNumbers(3).GetEnumerator()); Print("YieldChars", YieldChars.GetEnumerator()); Print("ExceptionHandling", ExceptionHandling().GetEnumerator()); Print("YieldBreakInCatch", YieldBreakInCatch().GetEnumerator()); Print("YieldBreakInCatchInTryFinally", YieldBreakInCatchInTryFinally().GetEnumerator()); Print("YieldBreakInTryCatchInTryFinally", YieldBreakInTryCatchInTryFinally().GetEnumerator()); Print("YieldBreakInTryFinallyInTryFinally(false)", YieldBreakInTryFinallyInTryFinally(false).GetEnumerator()); Print("YieldBreakInTryFinallyInTryFinally(true)", YieldBreakInTryFinallyInTryFinally(true).GetEnumerator()); try { Print("UnconditionalThrowInTryFinally()", UnconditionalThrowInTryFinally().GetEnumerator()); } catch (Exception ex) { Console.WriteLine(ex.Message); } Print("NestedTryFinallyStartingOnSamePosition", NestedTryFinallyStartingOnSamePosition().GetEnumerator()); Print("TryFinallyWithTwoExitPoints(false)", TryFinallyWithTwoExitPoints(false).GetEnumerator()); Print("TryFinallyWithTwoExitPoints(true)", TryFinallyWithTwoExitPoints(true).GetEnumerator()); #if !LEGACY_CSC Print("YieldBreakInNestedTryFinally()", YieldBreakInNestedTryFinally().GetEnumerator()); Print("TryFinallyWithTwoExitPointsInNestedTry(false)", TryFinallyWithTwoExitPointsInNestedTry(false).GetEnumerator()); Print("TryFinallyWithTwoExitPointsInNestedTry(true)", TryFinallyWithTwoExitPointsInNestedTry(true).GetEnumerator()); Print("TryFinallyWithTwoExitPointsInNestedCatch(false)", TryFinallyWithTwoExitPointsInNestedCatch(false).GetEnumerator()); Print("TryFinallyWithTwoExitPointsInNestedCatch(true)", TryFinallyWithTwoExitPointsInNestedCatch(true).GetEnumerator()); #endif Print("GenericYield()", GenericYield().GetEnumerator()); StructWithYieldReturn.Run(); } internal static void Print(string name, IEnumerator enumerator) { Console.WriteLine(name + ": Test start"); while (enumerator.MoveNext()) { Console.WriteLine(name + ": " + enumerator.Current); } } int fieldOnThis; public static IEnumerable YieldChars { get { yield return 'a'; yield return 'b'; yield return 'c'; } } public static IEnumerable SimpleYieldReturn() { yield return "A"; yield return "B"; yield return "C"; } public static IEnumerator SimpleYieldReturnEnumerator() { yield return "A"; yield return "B"; yield return "C"; } public IEnumerable YieldReturnParameters(int p) { yield return p; yield return fieldOnThis; } public IEnumerator YieldReturnParametersEnumerator(int p) { yield return p; yield return fieldOnThis; } public static IEnumerable YieldReturnInLoop() { for (int i = 0; i < 100; i++) { yield return i; } } public static IEnumerable YieldReturnWithTryFinally() { yield return 0; try { yield return 1; } finally { Console.WriteLine("Finally!"); } yield return 2; } public static IEnumerable YieldReturnInLock1(object o) { lock (o) { yield return 1; } } public static IEnumerable YieldReturnInLock2(object o) { lock (o) { yield return 1; o = null; yield return 2; } } public static IEnumerable YieldReturnWithNestedTryFinally(bool breakInMiddle) { Console.WriteLine("Start of method - 1"); yield return "Start of method"; Console.WriteLine("Start of method - 2"); try { Console.WriteLine("Within outer try - 1"); yield return "Within outer try"; Console.WriteLine("Within outer try - 2"); try { Console.WriteLine("Within inner try - 1"); yield return "Within inner try"; Console.WriteLine("Within inner try - 2"); if (breakInMiddle) { Console.WriteLine("Breaking..."); yield break; } Console.WriteLine("End of inner try - 1"); yield return "End of inner try"; Console.WriteLine("End of inner try - 2"); } finally { Console.WriteLine("Inner Finally"); } Console.WriteLine("End of outer try - 1"); yield return "End of outer try"; Console.WriteLine("End of outer try - 2"); } finally { Console.WriteLine("Outer Finally"); } Console.WriteLine("End of method - 1"); yield return "End of method"; Console.WriteLine("End of method - 2"); } public static IEnumerable YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable input) { // outer try-finally block foreach (string line in input) { // nested try-finally block try { yield return line; } finally { Console.WriteLine("Processed " + line); } } yield return "A"; yield return "B"; yield return "C"; yield return "D"; yield return "E"; yield return "F"; // outer try-finally block foreach (string line in input) { yield return line.ToUpper(); } } public static IEnumerable> YieldReturnWithAnonymousMethods1(IEnumerable input) { foreach (string line in input) { yield return () => line; } } public static IEnumerable> YieldReturnWithAnonymousMethods2(IEnumerable input) { foreach (string line in input) { string copy = line; yield return () => copy; } } public static IEnumerable GetEvenNumbers(int n) { for (int i = 0; i < n; i++) { if (i % 2 == 0) { yield return i; } } } public static IEnumerable ExceptionHandling() { yield return 'a'; try { Console.WriteLine("1 - try"); } catch (Exception) { Console.WriteLine("1 - catch"); } yield return 'b'; try { try { Console.WriteLine("2 - try"); } finally { Console.WriteLine("2 - finally"); } yield return 'c'; } finally { Console.WriteLine("outer finally"); } } public static IEnumerable YieldBreakInCatch() { yield return 0; try { Console.WriteLine("In Try"); } catch { // yield return is not allowed in catch, but yield break is yield break; } yield return 1; } public static IEnumerable YieldBreakInCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); } catch { // yield return is not allowed in catch, but yield break is // Note that pre-roslyn, this code triggers a compiler bug: // If the finally block throws an exception, it ends up getting // called a second time. yield break; } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } catch { Console.WriteLine("Catch"); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryFinallyInTryFinally(bool b) { try { yield return 0; try { Console.WriteLine("In Try"); if (b) { // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } } finally { Console.WriteLine("Inner Finally"); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakOnly() { yield break; } public static IEnumerable UnconditionalThrowInTryFinally() { // Here, MoveNext() doesn't call the finally methods at all // (only indirectly via Dispose()) try { yield return 0; throw new NotImplementedException(); } finally { Console.WriteLine("Finally"); } } public static IEnumerable NestedTryFinallyStartingOnSamePosition() { // The first user IL instruction is already in 2 nested try blocks. try { try { yield return 0; } finally { Console.WriteLine("Inner Finally"); } } finally { Console.WriteLine("Outer Finally"); } } public static IEnumerable TryFinallyWithTwoExitPoints(bool b) { // Uses goto for multiple non-exceptional exits out of try-finally. try { if (b) { yield return 1; goto exit1; } else { yield return 2; goto exit2; } } finally { Console.WriteLine("Finally"); } exit1: Console.WriteLine("Exit1"); yield break; exit2: Console.WriteLine("Exit2"); } #if !LEGACY_CSC public static IEnumerable YieldBreakInNestedTryFinally() { try { yield return 1; try { // Compiler bug: pre-Roslyn, the finally blocks will execute in the wrong order yield break; } finally { Console.WriteLine("Inner Finally"); } } finally { Console.WriteLine("Outer Finally"); } } // Legacy csc has a compiler bug with this type of code: // If the goto statements triggers a finally block, and the finally block throws an exception, // that exception gets caught by the catch block. public static IEnumerable TryFinallyWithTwoExitPointsInNestedTry(bool b) { try { yield return 1; try { if (b) goto exit1; else goto exit2; } catch { Console.WriteLine("Catch"); } } finally { Console.WriteLine("Finally"); } exit1: Console.WriteLine("Exit1"); yield break; exit2: Console.WriteLine("Exit2"); } public static IEnumerable TryFinallyWithTwoExitPointsInNestedCatch(bool b) { try { yield return 1; try { Console.WriteLine("Nested Try"); } catch { if (b) goto exit1; else goto exit2; } } finally { Console.WriteLine("Finally"); } exit1: Console.WriteLine("Exit1"); yield break; exit2: Console.WriteLine("Exit2"); } #endif public static IEnumerable LocalInFinally(T a) where T : IDisposable { yield return 1; try { yield return 2; } finally { T val = a; val.Dispose(); val.Dispose(); } yield return 3; } public static IEnumerable GenericYield() where T : new() { T val = new T(); for (int i = 0; i < 3; i++) { yield return val; } } } struct StructWithYieldReturn { public static void Run() { var s = new StructWithYieldReturn { val = 2 }; var count = s.Count(); YieldReturnTest.Print("StructWithYieldReturn", count.GetEnumerator()); YieldReturnTest.Print("StructWithYieldReturn (again)", count.GetEnumerator()); } int val; public IEnumerable Count() { yield return val++; yield return val++; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/.gitignore ================================================ *.result.il *.dll ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/GenericConstraints.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly GenericConstraints { .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module GenericConstraints.dll .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .class private auto ansi '' { } // end of class .class public auto ansi beforefieldinit TestType`1 extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method TestType::.ctor } // end of class TestType ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/InterfaceImplAttributes.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly InterfaceImplAttributes { .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module InterfaceImplAttributes.dll .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .class private auto ansi '' { } // end of class .class public auto ansi beforefieldinit TestType extends [mscorlib]System.Object implements ITestInterfaceA { .interfaceimpl type ITestInterfaceA .custom instance void TestAttributeA::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method TestType::.ctor } // end of class TestType .class interface public auto ansi abstract ITestInterfaceA { } // end of class ITestInterfaceA .class public auto ansi beforefieldinit TestAttributeA extends [mscorlib]System.Attribute { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2058 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method TestAttributeA::.ctor } // end of class TestAttributeA ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SecurityDeclarations.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly SecurityDeclarations { .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module SecurityDeclarations.dll // MVID: {761F919A-2373-48EB-9282-9DAB26913D43} .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .class private auto ansi '' { } // end of class .class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct extends [mscorlib]System.ValueType { .pack 0 .size 1 } // end of class SecurityDeclarations.TestStruct .class private auto ansi beforefieldinit SecurityDeclarations.SimpleType extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method SimpleType::.ctor } // end of class SecurityDeclarations.SimpleType .class private auto ansi sealed SecurityDeclarations.TestEnum extends [mscorlib]System.Enum { // Fields .field public specialname rtspecialname int32 value__ .field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0) .field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1) .field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2) } // end of class SecurityDeclarations.TestEnum .class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest extends [mscorlib]System.Security.Permissions.SecurityAttribute { // Fields .field private string[] _testStringArray .field private int32[] _testInt32Array .field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray .field private class [mscorlib]System.Type[] _testTypeArray .field public int32 TestInt32 .field public class [mscorlib]System.Type TestType .field public valuetype SecurityDeclarations.TestEnum TestEnumType .field public object TestBoxed .field public object TestBoxed2 .field public string TestString .field public object TestBoxedString .field public object TestBoxedArray .field public object TestBoxedType // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( valuetype [mscorlib]System.Security.Permissions.SecurityAction action ) cil managed { // Method begins at RVA 0x2059 // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction) IL_0007: nop IL_0008: nop IL_0009: ret } // end of method SecurityAttrTest::.ctor .method public hidebysig virtual instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed { // Method begins at RVA 0x2064 // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method SecurityAttrTest::CreatePermission .method public hidebysig specialname instance string[] get_TestStringArray () cil managed { // Method begins at RVA 0x206c // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0006: ret } // end of method SecurityAttrTest::get_TestStringArray .method public hidebysig specialname instance void set_TestStringArray ( string[] 'value' ) cil managed { // Method begins at RVA 0x2074 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0007: ret } // end of method SecurityAttrTest::set_TestStringArray .method public hidebysig specialname instance int32[] get_TestInt32Array () cil managed { // Method begins at RVA 0x207d // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0006: ret } // end of method SecurityAttrTest::get_TestInt32Array .method public hidebysig specialname instance void set_TestInt32Array ( int32[] 'value' ) cil managed { // Method begins at RVA 0x2085 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0007: ret } // end of method SecurityAttrTest::set_TestInt32Array .method public hidebysig specialname instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed { // Method begins at RVA 0x208e // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0006: ret } // end of method SecurityAttrTest::get_TestEnumArray .method public hidebysig specialname instance void set_TestEnumArray ( valuetype SecurityDeclarations.TestEnum[] 'value' ) cil managed { // Method begins at RVA 0x2096 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0007: ret } // end of method SecurityAttrTest::set_TestEnumArray .method public hidebysig specialname instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed { // Method begins at RVA 0x209f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0006: ret } // end of method SecurityAttrTest::get_TestTypeArray .method public hidebysig specialname instance void set_TestTypeArray ( class [mscorlib]System.Type[] 'value' ) cil managed { // Method begins at RVA 0x20a7 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0007: ret } // end of method SecurityAttrTest::set_TestTypeArray // Properties .property instance string[] TestStringArray() { .get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[]) } .property instance int32[] TestInt32Array() { .get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[]) } .property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray() { .get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[]) } .property instance class [mscorlib]System.Type[] TestTypeArray() { .get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[]) } } // end of class SecurityDeclarations.SecurityAttrTest .class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field string TestString = string('Hello World!') field object TestBoxedString = object(string('Boxed String')) property string[] TestStringArray = string[2]('a' 'b') field object TestBoxedArray = object(string[2]('c' 'd')) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestStringTypes::.ctor } // end of class SecurityDeclarations.TestStringTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes extends [mscorlib]System.Object { .permissionset demand = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field type TestType = type(SecurityDeclarations.SimpleType) field object TestBoxed = object(type(SecurityDeclarations.TestEnum)) property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType) field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestTypeTypes::.ctor } // end of class SecurityDeclarations.TestTypeTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes extends [mscorlib]System.Object { .permissionset inheritcheck = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field enum SecurityDeclarations.TestEnum TestEnumType = int32(0) field object TestBoxed = object(int32(1)) property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2) field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3)))) field object TestBoxedArray = object(int32[3](0 1 2)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestEnumTypes::.ctor } // end of class SecurityDeclarations.TestEnumTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types extends [mscorlib]System.Object { .permissionset permitonly = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field int32 TestInt32 = int32(5) field object TestBoxed = object(int32(10)) property int32[] TestInt32Array = int32[3](1 2 3) field object TestBoxedArray = object(int32[3](4 5 6)) field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestInt32Types::.ctor } // end of class SecurityDeclarations.TestInt32Types .class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6)))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NestedArrays::.ctor } // end of class SecurityDeclarations.NestedArrays ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.expected.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly SecurityDeclarations { .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module SecurityDeclarations.dll .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .class private auto ansi '' { } // end of class .class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6)))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20d4 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NestedArrays::.ctor } // end of class SecurityDeclarations.NestedArrays .class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest extends [mscorlib]System.Security.Permissions.SecurityAttribute { // Fields .field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray .field private int32[] _testInt32Array .field private string[] _testStringArray .field private class [mscorlib]System.Type[] _testTypeArray .field public object TestBoxed .field public object TestBoxed2 .field public object TestBoxedArray .field public object TestBoxedString .field public object TestBoxedType .field public valuetype SecurityDeclarations.TestEnum TestEnumType .field public int32 TestInt32 .field public string TestString .field public class [mscorlib]System.Type TestType // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( valuetype [mscorlib]System.Security.Permissions.SecurityAction action ) cil managed { // Method begins at RVA 0x2059 // Header size: 1 // Code size: 10 (0xa) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction) IL_0007: nop IL_0008: nop IL_0009: ret } // end of method SecurityAttrTest::.ctor .method public hidebysig virtual instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed { // Method begins at RVA 0x2064 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method SecurityAttrTest::CreatePermission .method public hidebysig specialname instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed { // Method begins at RVA 0x208e // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0006: ret } // end of method SecurityAttrTest::get_TestEnumArray .method public hidebysig specialname instance int32[] get_TestInt32Array () cil managed { // Method begins at RVA 0x207d // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0006: ret } // end of method SecurityAttrTest::get_TestInt32Array .method public hidebysig specialname instance string[] get_TestStringArray () cil managed { // Method begins at RVA 0x206c // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0006: ret } // end of method SecurityAttrTest::get_TestStringArray .method public hidebysig specialname instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0006: ret } // end of method SecurityAttrTest::get_TestTypeArray .method public hidebysig specialname instance void set_TestEnumArray ( valuetype SecurityDeclarations.TestEnum[] 'value' ) cil managed { // Method begins at RVA 0x2096 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0007: ret } // end of method SecurityAttrTest::set_TestEnumArray .method public hidebysig specialname instance void set_TestInt32Array ( int32[] 'value' ) cil managed { // Method begins at RVA 0x2085 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0007: ret } // end of method SecurityAttrTest::set_TestInt32Array .method public hidebysig specialname instance void set_TestStringArray ( string[] 'value' ) cil managed { // Method begins at RVA 0x2074 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0007: ret } // end of method SecurityAttrTest::set_TestStringArray .method public hidebysig specialname instance void set_TestTypeArray ( class [mscorlib]System.Type[] 'value' ) cil managed { // Method begins at RVA 0x20a7 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0007: ret } // end of method SecurityAttrTest::set_TestTypeArray // Properties .property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray() { .get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[]) } .property instance int32[] TestInt32Array() { .get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[]) } .property instance string[] TestStringArray() { .get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[]) } .property instance class [mscorlib]System.Type[] TestTypeArray() { .get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[]) } } // end of class SecurityDeclarations.SecurityAttrTest .class private auto ansi beforefieldinit SecurityDeclarations.SimpleType extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method SimpleType::.ctor } // end of class SecurityDeclarations.SimpleType .class private auto ansi sealed SecurityDeclarations.TestEnum extends [mscorlib]System.Enum { // Fields .field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0) .field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1) .field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2) .field public specialname rtspecialname int32 value__ } // end of class SecurityDeclarations.TestEnum .class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes extends [mscorlib]System.Object { .permissionset inheritcheck = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field enum SecurityDeclarations.TestEnum TestEnumType = int32(0) field object TestBoxed = object(int32(1)) property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2) field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3)))) field object TestBoxedArray = object(int32[3](0 1 2)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20c2 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestEnumTypes::.ctor } // end of class SecurityDeclarations.TestEnumTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types extends [mscorlib]System.Object { .permissionset permitonly = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field int32 TestInt32 = int32(5) field object TestBoxed = object(int32(10)) property int32[] TestInt32Array = int32[3](1 2 3) field object TestBoxedArray = object(int32[3](4 5 6)) field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20cb // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestInt32Types::.ctor } // end of class SecurityDeclarations.TestInt32Types .class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field string TestString = string('Hello World!') field object TestBoxedString = object(string('Boxed String')) property string[] TestStringArray = string[2]('a' 'b') field object TestBoxedArray = object(string[2]('c' 'd')) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20b0 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestStringTypes::.ctor } // end of class SecurityDeclarations.TestStringTypes .class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct extends [mscorlib]System.ValueType { .pack 0 .size 1 } // end of class SecurityDeclarations.TestStruct .class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes extends [mscorlib]System.Object { .permissionset demand = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field type TestType = type(SecurityDeclarations.SimpleType) field object TestBoxed = object(type(SecurityDeclarations.TestEnum)) property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType) field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20b9 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestTypeTypes::.ctor } // end of class SecurityDeclarations.TestTypeTypes ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SortMembers.il ================================================ .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly SecurityDeclarations { .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module SecurityDeclarations.dll // MVID: {761F919A-2373-48EB-9282-9DAB26913D43} .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .class private auto ansi '' { } // end of class .class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct extends [mscorlib]System.ValueType { .pack 0 .size 1 } // end of class SecurityDeclarations.TestStruct .class private auto ansi beforefieldinit SecurityDeclarations.SimpleType extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method SimpleType::.ctor } // end of class SecurityDeclarations.SimpleType .class private auto ansi sealed SecurityDeclarations.TestEnum extends [mscorlib]System.Enum { // Fields .field public specialname rtspecialname int32 value__ .field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0) .field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1) .field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2) } // end of class SecurityDeclarations.TestEnum .class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest extends [mscorlib]System.Security.Permissions.SecurityAttribute { // Fields .field private string[] _testStringArray .field private int32[] _testInt32Array .field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray .field private class [mscorlib]System.Type[] _testTypeArray .field public int32 TestInt32 .field public class [mscorlib]System.Type TestType .field public valuetype SecurityDeclarations.TestEnum TestEnumType .field public object TestBoxed .field public object TestBoxed2 .field public string TestString .field public object TestBoxedString .field public object TestBoxedArray .field public object TestBoxedType // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( valuetype [mscorlib]System.Security.Permissions.SecurityAction action ) cil managed { // Method begins at RVA 0x2059 // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction) IL_0007: nop IL_0008: nop IL_0009: ret } // end of method SecurityAttrTest::.ctor .method public hidebysig virtual instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed { // Method begins at RVA 0x2064 // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method SecurityAttrTest::CreatePermission .method public hidebysig specialname instance string[] get_TestStringArray () cil managed { // Method begins at RVA 0x206c // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0006: ret } // end of method SecurityAttrTest::get_TestStringArray .method public hidebysig specialname instance void set_TestStringArray ( string[] 'value' ) cil managed { // Method begins at RVA 0x2074 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray IL_0007: ret } // end of method SecurityAttrTest::set_TestStringArray .method public hidebysig specialname instance int32[] get_TestInt32Array () cil managed { // Method begins at RVA 0x207d // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0006: ret } // end of method SecurityAttrTest::get_TestInt32Array .method public hidebysig specialname instance void set_TestInt32Array ( int32[] 'value' ) cil managed { // Method begins at RVA 0x2085 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array IL_0007: ret } // end of method SecurityAttrTest::set_TestInt32Array .method public hidebysig specialname instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed { // Method begins at RVA 0x208e // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0006: ret } // end of method SecurityAttrTest::get_TestEnumArray .method public hidebysig specialname instance void set_TestEnumArray ( valuetype SecurityDeclarations.TestEnum[] 'value' ) cil managed { // Method begins at RVA 0x2096 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray IL_0007: ret } // end of method SecurityAttrTest::set_TestEnumArray .method public hidebysig specialname instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed { // Method begins at RVA 0x209f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0006: ret } // end of method SecurityAttrTest::get_TestTypeArray .method public hidebysig specialname instance void set_TestTypeArray ( class [mscorlib]System.Type[] 'value' ) cil managed { // Method begins at RVA 0x20a7 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray IL_0007: ret } // end of method SecurityAttrTest::set_TestTypeArray // Properties .property instance string[] TestStringArray() { .get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[]) } .property instance int32[] TestInt32Array() { .get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[]) } .property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray() { .get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[]) } .property instance class [mscorlib]System.Type[] TestTypeArray() { .get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray() .set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[]) } } // end of class SecurityDeclarations.SecurityAttrTest .class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field string TestString = string('Hello World!') field object TestBoxedString = object(string('Boxed String')) property string[] TestStringArray = string[2]('a' 'b') field object TestBoxedArray = object(string[2]('c' 'd')) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestStringTypes::.ctor } // end of class SecurityDeclarations.TestStringTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes extends [mscorlib]System.Object { .permissionset demand = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field type TestType = type(SecurityDeclarations.SimpleType) field object TestBoxed = object(type(SecurityDeclarations.TestEnum)) property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType) field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestTypeTypes::.ctor } // end of class SecurityDeclarations.TestTypeTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes extends [mscorlib]System.Object { .permissionset inheritcheck = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field enum SecurityDeclarations.TestEnum TestEnumType = int32(0) field object TestBoxed = object(int32(1)) property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2) field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3)))) field object TestBoxedArray = object(int32[3](0 1 2)) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestEnumTypes::.ctor } // end of class SecurityDeclarations.TestEnumTypes .class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types extends [mscorlib]System.Object { .permissionset permitonly = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field int32 TestInt32 = int32(5) field object TestBoxed = object(int32(10)) property int32[] TestInt32Array = int32[3](1 2 3) field object TestBoxedArray = object(int32[3](4 5 6)) field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestInt32Types::.ctor } // end of class SecurityDeclarations.TestInt32Types .class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays extends [mscorlib]System.Object { .permissionset assert = { class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = { field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6)))) } } // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2050 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NestedArrays::.ctor } // end of class SecurityDeclarations.NestedArrays ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/.gitignore ================================================ /*.dll ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Debug.cs ================================================ // Copyright (c) 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 // 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; using System.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Switch { public class SetProperty { public readonly PropertyInfo Property; private int _set; public int Set { get { return _set; } set { _set = value; } } public SetProperty(PropertyInfo property) { Property = property; } } public enum State { False, True, Null } public static string SparseIntegerSwitch(int i) { Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: return "-10 mln"; case -100: return "-hundred"; case -1: return "-1"; case 0: return "0"; case 1: return "1"; case 2: return "2"; case 4: return "4"; case 100: return "hundred"; case 10000: return "ten thousand"; case 10001: return "ten thousand and one"; case int.MaxValue: return "int.MaxValue"; default: return "something else"; } } public static void SwitchOverInt(int i) { switch (i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static string ShortSwitchOverString(string text) { Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; default: return "Default"; } } public static string ShortSwitchOverStringWithNullCase(string text) { Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case null: return "null"; default: return "Default"; } } public static string SwitchOverString1(string text) { Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case null: return null; default: return "Default"; } } public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } public static string TwoDifferentSwitchBlocksInTryFinally() { try { Console.WriteLine("TwoDifferentSwitchBlocks:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } finally { Console.WriteLine("Second switch:"); switch (Console.ReadLine()) { case "12": Console.WriteLine("Te43234xt1"); break; case "13": Console.WriteLine("Te223443xt2"); break; case "14": Console.WriteLine("Te234xt3"); break; case "15": Console.WriteLine("Tex243t4"); break; case "16": Console.WriteLine("Tex243t5"); break; case "17": Console.WriteLine("Text2346"); break; case "18": Console.WriteLine("Text234234"); break; case "19 case": Console.WriteLine("Text8234"); break; case "20 case": Console.WriteLine("Text923423"); break; case "21 case": Console.WriteLine("Text10"); break; case "22 case": Console.WriteLine("Text1134123"); break; default: Console.WriteLine("Defa234234ult"); break; } } } public static string SwitchOverBool(bool b) { Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; case false: return bool.FalseString; default: return null; } } public static void SwitchInLoop(int i) { Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: Console.WriteLine("one"); break; case 2: Console.WriteLine("two"); break; //case 3: // Console.WriteLine("three"); // continue; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); Console.WriteLine("more code"); return; } i++; } } public static void SwitchWithGoto(int i) { Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: Console.WriteLine("one"); goto default; case 2: Console.WriteLine("two"); goto case 3; case 3: Console.WriteLine("three"); break; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); break; } Console.WriteLine("End of method"); } private static SetProperty[] GetProperties() { return new SetProperty[0]; } public static void SwitchOnStringInForLoop() { ArrayList arrayList = new ArrayList(); ArrayList arrayList2 = new ArrayList(); SetProperty[] properties = GetProperties(); for (int i = 0; i < properties.Length; i++) { Console.WriteLine("In for-loop"); SetProperty setProperty = properties[i]; switch (setProperty.Property.Name) { case "Name1": setProperty.Set = 1; arrayList.Add(setProperty); break; case "Name2": setProperty.Set = 2; arrayList.Add(setProperty); break; case "Name3": setProperty.Set = 3; arrayList.Add(setProperty); break; case "Name4": setProperty.Set = 4; arrayList.Add(setProperty); break; case "Name5": case "Name6": arrayList.Add(setProperty); break; default: arrayList2.Add(setProperty); break; } } } public static void SwitchWithComplexCondition(string[] args) { switch ((args.Length == 0) ? "dummy" : args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } public static void SwitchWithArray(string[] args) { switch (args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Debug.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v1.1.4322 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:5000:0 } .assembly CS1xSwitch_Debug { // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool, // bool) = ( 01 00 00 01 00 00 ) .hash algorithm 0x00008004 .ver 0:0:0:0 } .module CS1xSwitch_Debug.dll // MVID: {3797A9DC-06AE-42B7-994E-BDC70F8FF69B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x07560000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch extends [mscorlib]System.Object { .class auto ansi nested public beforefieldinit SetProperty extends [mscorlib]System.Object { .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property .field private int32 _set .method public hidebysig specialname instance int32 get_Set() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (int32 V_0) IL_0000: ldarg.0 IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::_set IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method SetProperty::get_Set .method public hidebysig specialname instance void set_Set(int32 'value') cil managed { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::_set IL_0007: ret } // end of method SetProperty::set_Set .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property IL_000d: ret } // end of method SetProperty::.ctor .property instance int32 Set() { .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) } // end of property SetProperty::Set } // end of class SetProperty .class auto ansi sealed nested public State extends [mscorlib]System.Enum { .field public specialname rtspecialname int32 value__ .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State False = int32(0x00000000) .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State True = int32(0x00000001) .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State Null = int32(0x00000002) } // end of class State .method public hidebysig static string SparseIntegerSwitch(int32 i) cil managed { // Code size 207 (0xcf) .maxstack 2 .locals init (string V_0, int32 V_1) IL_0000: ldstr "SparseIntegerSwitch: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ldarg.0 IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: ldc.i4.4 IL_0019: bgt.s IL_004a IL_001b: ldloc.1 IL_001c: ldc.i4 0xff676980 IL_0021: beq.s IL_006d IL_0023: ldloc.1 IL_0024: ldc.i4.s -100 IL_0026: beq.s IL_0075 IL_0028: ldloc.1 IL_0029: ldc.i4.m1 IL_002a: sub IL_002b: switch ( IL_007d, IL_0085, IL_008d, IL_0095, IL_00c5, IL_009d) IL_0048: br.s IL_00c5 IL_004a: ldloc.1 IL_004b: ldc.i4.s 100 IL_004d: beq.s IL_00a5 IL_004f: ldloc.1 IL_0050: ldc.i4 0x2710 IL_0055: sub IL_0056: switch ( IL_00ad, IL_00b5) IL_0063: ldloc.1 IL_0064: ldc.i4 0x7fffffff IL_0069: beq.s IL_00bd IL_006b: br.s IL_00c5 IL_006d: ldstr "-10 mln" IL_0072: stloc.0 IL_0073: br.s IL_00cd IL_0075: ldstr "-hundred" IL_007a: stloc.0 IL_007b: br.s IL_00cd IL_007d: ldstr "-1" IL_0082: stloc.0 IL_0083: br.s IL_00cd IL_0085: ldstr "0" IL_008a: stloc.0 IL_008b: br.s IL_00cd IL_008d: ldstr "1" IL_0092: stloc.0 IL_0093: br.s IL_00cd IL_0095: ldstr "2" IL_009a: stloc.0 IL_009b: br.s IL_00cd IL_009d: ldstr "4" IL_00a2: stloc.0 IL_00a3: br.s IL_00cd IL_00a5: ldstr "hundred" IL_00aa: stloc.0 IL_00ab: br.s IL_00cd IL_00ad: ldstr "ten thousand" IL_00b2: stloc.0 IL_00b3: br.s IL_00cd IL_00b5: ldstr "ten thousand and one" IL_00ba: stloc.0 IL_00bb: br.s IL_00cd IL_00bd: ldstr "int.MaxValue" IL_00c2: stloc.0 IL_00c3: br.s IL_00cd IL_00c5: ldstr "something else" IL_00ca: stloc.0 IL_00cb: br.s IL_00cd IL_00cd: ldloc.0 IL_00ce: ret } // end of method Switch::SparseIntegerSwitch .method public hidebysig static void SwitchOverInt(int32 i) cil managed { // Code size 136 (0x88) .maxstack 2 .locals init (int32 V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.s 10 IL_0005: bgt.s IL_0016 IL_0007: ldloc.0 IL_0008: ldc.i4.0 IL_0009: beq.s IL_0033 IL_000b: ldloc.0 IL_000c: ldc.i4.5 IL_000d: beq.s IL_003f IL_000f: ldloc.0 IL_0010: ldc.i4.s 10 IL_0012: beq.s IL_004b IL_0014: br.s IL_0087 IL_0016: ldloc.0 IL_0017: ldc.i4.s 20 IL_0019: bgt.s IL_0027 IL_001b: ldloc.0 IL_001c: ldc.i4.s 15 IL_001e: beq.s IL_0057 IL_0020: ldloc.0 IL_0021: ldc.i4.s 20 IL_0023: beq.s IL_0063 IL_0025: br.s IL_0087 IL_0027: ldloc.0 IL_0028: ldc.i4.s 25 IL_002a: beq.s IL_006f IL_002c: ldloc.0 IL_002d: ldc.i4.s 30 IL_002f: beq.s IL_007b IL_0031: br.s IL_0087 IL_0033: ldstr "zero" IL_0038: call void [mscorlib]System.Console::WriteLine(string) IL_003d: br.s IL_0087 IL_003f: ldstr "five" IL_0044: call void [mscorlib]System.Console::WriteLine(string) IL_0049: br.s IL_0087 IL_004b: ldstr "ten" IL_0050: call void [mscorlib]System.Console::WriteLine(string) IL_0055: br.s IL_0087 IL_0057: ldstr "fifteen" IL_005c: call void [mscorlib]System.Console::WriteLine(string) IL_0061: br.s IL_0087 IL_0063: ldstr "twenty" IL_0068: call void [mscorlib]System.Console::WriteLine(string) IL_006d: br.s IL_0087 IL_006f: ldstr "twenty-five" IL_0074: call void [mscorlib]System.Console::WriteLine(string) IL_0079: br.s IL_0087 IL_007b: ldstr "thirty" IL_0080: call void [mscorlib]System.Console::WriteLine(string) IL_0085: br.s IL_0087 IL_0087: ret } // end of method Switch::SwitchOverInt .method public hidebysig static string ShortSwitchOverString(string text) cil managed { // Code size 105 (0x69) .maxstack 3 .locals init (string V_0, string V_1) IL_0000: ldstr "ShortSwitchOverString: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: ldstr "Third case" IL_001f: leave.s IL_0021 IL_0021: ldarg.0 IL_0022: dup IL_0023: stloc.1 IL_0024: brfalse.s IL_005f IL_0026: ldloc.1 IL_0027: call string [mscorlib]System.String::IsInterned(string) IL_002c: stloc.1 IL_002d: ldloc.1 IL_002e: ldstr "First case" IL_0033: beq.s IL_0047 IL_0035: ldloc.1 IL_0036: ldstr "Second case" IL_003b: beq.s IL_004f IL_003d: ldloc.1 IL_003e: ldstr "Third case" IL_0043: beq.s IL_0057 IL_0045: br.s IL_005f IL_0047: ldstr "Text1" IL_004c: stloc.0 IL_004d: br.s IL_0067 IL_004f: ldstr "Text2" IL_0054: stloc.0 IL_0055: br.s IL_0067 IL_0057: ldstr "Text3" IL_005c: stloc.0 IL_005d: br.s IL_0067 IL_005f: ldstr "Default" IL_0064: stloc.0 IL_0065: br.s IL_0067 IL_0067: ldloc.0 IL_0068: ret } // end of method Switch::ShortSwitchOverString .method public hidebysig static string ShortSwitchOverStringWithNullCase(string text) cil managed { // Code size 92 (0x5c) .maxstack 2 .locals init (string V_0, string V_1) IL_0000: ldstr "ShortSwitchOverStringWithNullCase: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: leave.s IL_001c IL_001c: ldarg.0 IL_001d: dup IL_001e: stloc.1 IL_001f: brfalse.s IL_004a IL_0021: ldloc.1 IL_0022: call string [mscorlib]System.String::IsInterned(string) IL_0027: stloc.1 IL_0028: ldloc.1 IL_0029: ldstr "First case" IL_002e: beq.s IL_003a IL_0030: ldloc.1 IL_0031: ldstr "Second case" IL_0036: beq.s IL_0042 IL_0038: br.s IL_0052 IL_003a: ldstr "Text1" IL_003f: stloc.0 IL_0040: br.s IL_005a IL_0042: ldstr "Text2" IL_0047: stloc.0 IL_0048: br.s IL_005a IL_004a: ldstr "null" IL_004f: stloc.0 IL_0050: br.s IL_005a IL_0052: ldstr "Default" IL_0057: stloc.0 IL_0058: br.s IL_005a IL_005a: ldloc.0 IL_005b: ret } // end of method Switch::ShortSwitchOverStringWithNullCase .method public hidebysig static string SwitchOverString1(string text) cil managed { // Code size 185 (0xb9) .maxstack 7 .locals init (string V_0, string V_1) IL_0000: ldstr "SwitchOverString1: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: ldstr "2nd case" IL_001f: ldstr "Third case" IL_0024: ldstr "Fourth case" IL_0029: ldstr "Fifth case" IL_002e: ldstr "Sixth case" IL_0033: leave.s IL_0035 IL_0035: ldarg.0 IL_0036: dup IL_0037: stloc.1 IL_0038: brfalse.s IL_00ab IL_003a: ldloc.1 IL_003b: call string [mscorlib]System.String::IsInterned(string) IL_0040: stloc.1 IL_0041: ldloc.1 IL_0042: ldstr "First case" IL_0047: beq.s IL_007b IL_0049: ldloc.1 IL_004a: ldstr "Second case" IL_004f: beq.s IL_0083 IL_0051: ldloc.1 IL_0052: ldstr "2nd case" IL_0057: beq.s IL_0083 IL_0059: ldloc.1 IL_005a: ldstr "Third case" IL_005f: beq.s IL_008b IL_0061: ldloc.1 IL_0062: ldstr "Fourth case" IL_0067: beq.s IL_0093 IL_0069: ldloc.1 IL_006a: ldstr "Fifth case" IL_006f: beq.s IL_009b IL_0071: ldloc.1 IL_0072: ldstr "Sixth case" IL_0077: beq.s IL_00a3 IL_0079: br.s IL_00af IL_007b: ldstr "Text1" IL_0080: stloc.0 IL_0081: br.s IL_00b7 IL_0083: ldstr "Text2" IL_0088: stloc.0 IL_0089: br.s IL_00b7 IL_008b: ldstr "Text3" IL_0090: stloc.0 IL_0091: br.s IL_00b7 IL_0093: ldstr "Text4" IL_0098: stloc.0 IL_0099: br.s IL_00b7 IL_009b: ldstr "Text5" IL_00a0: stloc.0 IL_00a1: br.s IL_00b7 IL_00a3: ldstr "Text6" IL_00a8: stloc.0 IL_00a9: br.s IL_00b7 IL_00ab: ldnull IL_00ac: stloc.0 IL_00ad: br.s IL_00b7 IL_00af: ldstr "Default" IL_00b4: stloc.0 IL_00b5: br.s IL_00b7 IL_00b7: ldloc.0 IL_00b8: ret } // end of method Switch::SwitchOverString1 .method public hidebysig static string SwitchOverString2() cil managed { // Code size 418 (0x1a2) .maxstack 4 .locals init (string V_0, object V_1) IL_0000: volatile. IL_0002: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_0007: brtrue IL_00dc IL_000c: ldc.i4.s 24 IL_000e: ldc.r4 0.5 IL_0013: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_0018: dup IL_0019: ldstr "First case" IL_001e: ldc.i4.0 IL_001f: box [mscorlib]System.Int32 IL_0024: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0029: dup IL_002a: ldstr "Second case" IL_002f: ldc.i4.1 IL_0030: box [mscorlib]System.Int32 IL_0035: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_003a: dup IL_003b: ldstr "Third case" IL_0040: ldc.i4.2 IL_0041: box [mscorlib]System.Int32 IL_0046: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_004b: dup IL_004c: ldstr "Fourth case" IL_0051: ldc.i4.3 IL_0052: box [mscorlib]System.Int32 IL_0057: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_005c: dup IL_005d: ldstr "Fifth case" IL_0062: ldc.i4.4 IL_0063: box [mscorlib]System.Int32 IL_0068: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_006d: dup IL_006e: ldstr "Sixth case" IL_0073: ldc.i4.5 IL_0074: box [mscorlib]System.Int32 IL_0079: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_007e: dup IL_007f: ldstr "Seventh case" IL_0084: ldc.i4.6 IL_0085: box [mscorlib]System.Int32 IL_008a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_008f: dup IL_0090: ldstr "Eighth case" IL_0095: ldc.i4.7 IL_0096: box [mscorlib]System.Int32 IL_009b: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00a0: dup IL_00a1: ldstr "Ninth case" IL_00a6: ldc.i4.8 IL_00a7: box [mscorlib]System.Int32 IL_00ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00b1: dup IL_00b2: ldstr "Tenth case" IL_00b7: ldc.i4.s 9 IL_00b9: box [mscorlib]System.Int32 IL_00be: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00c3: dup IL_00c4: ldstr "Eleventh case" IL_00c9: ldc.i4.s 10 IL_00cb: box [mscorlib]System.Int32 IL_00d0: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00d5: volatile. IL_00d7: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_00dc: ldstr "SwitchOverString2:" IL_00e1: call void [mscorlib]System.Console::WriteLine(string) IL_00e6: call string [mscorlib]System.Environment::get_UserName() IL_00eb: dup IL_00ec: stloc.1 IL_00ed: brfalse IL_0198 IL_00f2: volatile. IL_00f4: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_00f9: ldloc.1 IL_00fa: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_00ff: dup IL_0100: stloc.1 IL_0101: brfalse IL_0198 IL_0106: ldloc.1 IL_0107: unbox [mscorlib]System.Int32 IL_010c: ldind.i4 IL_010d: switch ( IL_0140, IL_0148, IL_0150, IL_0158, IL_0160, IL_0168, IL_0170, IL_0178, IL_0180, IL_0188, IL_0190) IL_013e: br.s IL_0198 IL_0140: ldstr "Text1" IL_0145: stloc.0 IL_0146: br.s IL_01a0 IL_0148: ldstr "Text2" IL_014d: stloc.0 IL_014e: br.s IL_01a0 IL_0150: ldstr "Text3" IL_0155: stloc.0 IL_0156: br.s IL_01a0 IL_0158: ldstr "Text4" IL_015d: stloc.0 IL_015e: br.s IL_01a0 IL_0160: ldstr "Text5" IL_0165: stloc.0 IL_0166: br.s IL_01a0 IL_0168: ldstr "Text6" IL_016d: stloc.0 IL_016e: br.s IL_01a0 IL_0170: ldstr "Text7" IL_0175: stloc.0 IL_0176: br.s IL_01a0 IL_0178: ldstr "Text8" IL_017d: stloc.0 IL_017e: br.s IL_01a0 IL_0180: ldstr "Text9" IL_0185: stloc.0 IL_0186: br.s IL_01a0 IL_0188: ldstr "Text10" IL_018d: stloc.0 IL_018e: br.s IL_01a0 IL_0190: ldstr "Text11" IL_0195: stloc.0 IL_0196: br.s IL_01a0 IL_0198: ldstr "Default" IL_019d: stloc.0 IL_019e: br.s IL_01a0 IL_01a0: ldloc.0 IL_01a1: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string TwoDifferentSwitchBlocksInTryFinally() cil managed { // Code size 925 (0x39d) .maxstack 4 .locals init (string V_0, object V_1) IL_0000: volatile. IL_0002: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_0007: brtrue IL_01b8 IL_000c: ldc.i4.s 24 IL_000e: ldc.r4 0.5 IL_0013: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_0018: dup IL_0019: ldstr "12" IL_001e: ldc.i4.0 IL_001f: box [mscorlib]System.Int32 IL_0024: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0029: dup IL_002a: ldstr "13" IL_002f: ldc.i4.1 IL_0030: box [mscorlib]System.Int32 IL_0035: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_003a: dup IL_003b: ldstr "14" IL_0040: ldc.i4.2 IL_0041: box [mscorlib]System.Int32 IL_0046: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_004b: dup IL_004c: ldstr "15" IL_0051: ldc.i4.3 IL_0052: box [mscorlib]System.Int32 IL_0057: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_005c: dup IL_005d: ldstr "16" IL_0062: ldc.i4.4 IL_0063: box [mscorlib]System.Int32 IL_0068: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_006d: dup IL_006e: ldstr "17" IL_0073: ldc.i4.5 IL_0074: box [mscorlib]System.Int32 IL_0079: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_007e: dup IL_007f: ldstr "18" IL_0084: ldc.i4.6 IL_0085: box [mscorlib]System.Int32 IL_008a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_008f: dup IL_0090: ldstr "19 case" IL_0095: ldc.i4.7 IL_0096: box [mscorlib]System.Int32 IL_009b: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00a0: dup IL_00a1: ldstr "20 case" IL_00a6: ldc.i4.8 IL_00a7: box [mscorlib]System.Int32 IL_00ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00b1: dup IL_00b2: ldstr "21 case" IL_00b7: ldc.i4.s 9 IL_00b9: box [mscorlib]System.Int32 IL_00be: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00c3: dup IL_00c4: ldstr "22 case" IL_00c9: ldc.i4.s 10 IL_00cb: box [mscorlib]System.Int32 IL_00d0: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00d5: volatile. IL_00d7: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_00dc: volatile. IL_00de: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' IL_00e3: brtrue IL_01b8 IL_00e8: ldc.i4.s 24 IL_00ea: ldc.r4 0.5 IL_00ef: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_00f4: dup IL_00f5: ldstr "First case" IL_00fa: ldc.i4.0 IL_00fb: box [mscorlib]System.Int32 IL_0100: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0105: dup IL_0106: ldstr "Second case" IL_010b: ldc.i4.1 IL_010c: box [mscorlib]System.Int32 IL_0111: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0116: dup IL_0117: ldstr "Third case" IL_011c: ldc.i4.2 IL_011d: box [mscorlib]System.Int32 IL_0122: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0127: dup IL_0128: ldstr "Fourth case" IL_012d: ldc.i4.3 IL_012e: box [mscorlib]System.Int32 IL_0133: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0138: dup IL_0139: ldstr "Fifth case" IL_013e: ldc.i4.4 IL_013f: box [mscorlib]System.Int32 IL_0144: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0149: dup IL_014a: ldstr "Sixth case" IL_014f: ldc.i4.5 IL_0150: box [mscorlib]System.Int32 IL_0155: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_015a: dup IL_015b: ldstr "Seventh case" IL_0160: ldc.i4.6 IL_0161: box [mscorlib]System.Int32 IL_0166: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_016b: dup IL_016c: ldstr "Eighth case" IL_0171: ldc.i4.7 IL_0172: box [mscorlib]System.Int32 IL_0177: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_017c: dup IL_017d: ldstr "Ninth case" IL_0182: ldc.i4.8 IL_0183: box [mscorlib]System.Int32 IL_0188: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_018d: dup IL_018e: ldstr "Tenth case" IL_0193: ldc.i4.s 9 IL_0195: box [mscorlib]System.Int32 IL_019a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_019f: dup IL_01a0: ldstr "Eleventh case" IL_01a5: ldc.i4.s 10 IL_01a7: box [mscorlib]System.Int32 IL_01ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_01b1: volatile. IL_01b3: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' .try { IL_01b8: ldstr "TwoDifferentSwitchBlocks:" IL_01bd: call void [mscorlib]System.Console::WriteLine(string) IL_01c2: call string [mscorlib]System.Environment::get_UserName() IL_01c7: dup IL_01c8: stloc.1 IL_01c9: brfalse IL_0295 IL_01ce: volatile. IL_01d0: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' IL_01d5: ldloc.1 IL_01d6: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_01db: dup IL_01dc: stloc.1 IL_01dd: brfalse IL_0295 IL_01e2: ldloc.1 IL_01e3: unbox [mscorlib]System.Int32 IL_01e8: ldind.i4 IL_01e9: switch ( IL_021c, IL_0227, IL_0232, IL_023d, IL_0248, IL_0253, IL_025e, IL_0269, IL_0274, IL_027f, IL_028a) IL_021a: br.s IL_0295 IL_021c: ldstr "Text1" IL_0221: stloc.0 IL_0222: leave IL_039b IL_0227: ldstr "Text2" IL_022c: stloc.0 IL_022d: leave IL_039b IL_0232: ldstr "Text3" IL_0237: stloc.0 IL_0238: leave IL_039b IL_023d: ldstr "Text4" IL_0242: stloc.0 IL_0243: leave IL_039b IL_0248: ldstr "Text5" IL_024d: stloc.0 IL_024e: leave IL_039b IL_0253: ldstr "Text6" IL_0258: stloc.0 IL_0259: leave IL_039b IL_025e: ldstr "Text7" IL_0263: stloc.0 IL_0264: leave IL_039b IL_0269: ldstr "Text8" IL_026e: stloc.0 IL_026f: leave IL_039b IL_0274: ldstr "Text9" IL_0279: stloc.0 IL_027a: leave IL_039b IL_027f: ldstr "Text10" IL_0284: stloc.0 IL_0285: leave IL_039b IL_028a: ldstr "Text11" IL_028f: stloc.0 IL_0290: leave IL_039b IL_0295: ldstr "Default" IL_029a: stloc.0 IL_029b: leave IL_039b } // end .try finally { IL_02a0: ldstr "Second switch:" IL_02a5: call void [mscorlib]System.Console::WriteLine(string) IL_02aa: call string [mscorlib]System.Console::ReadLine() IL_02af: dup IL_02b0: stloc.1 IL_02b1: brfalse IL_038e IL_02b6: volatile. IL_02b8: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_02bd: ldloc.1 IL_02be: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_02c3: dup IL_02c4: stloc.1 IL_02c5: brfalse IL_038e IL_02ca: ldloc.1 IL_02cb: unbox [mscorlib]System.Int32 IL_02d0: ldind.i4 IL_02d1: switch ( IL_0307, IL_0316, IL_0322, IL_032e, IL_033a, IL_0346, IL_0352, IL_035e, IL_036a, IL_0376, IL_0382) IL_0302: br IL_038e IL_0307: ldstr "Te43234xt1" IL_030c: call void [mscorlib]System.Console::WriteLine(string) IL_0311: br IL_039a IL_0316: ldstr "Te223443xt2" IL_031b: call void [mscorlib]System.Console::WriteLine(string) IL_0320: br.s IL_039a IL_0322: ldstr "Te234xt3" IL_0327: call void [mscorlib]System.Console::WriteLine(string) IL_032c: br.s IL_039a IL_032e: ldstr "Tex243t4" IL_0333: call void [mscorlib]System.Console::WriteLine(string) IL_0338: br.s IL_039a IL_033a: ldstr "Tex243t5" IL_033f: call void [mscorlib]System.Console::WriteLine(string) IL_0344: br.s IL_039a IL_0346: ldstr "Text2346" IL_034b: call void [mscorlib]System.Console::WriteLine(string) IL_0350: br.s IL_039a IL_0352: ldstr "Text234234" IL_0357: call void [mscorlib]System.Console::WriteLine(string) IL_035c: br.s IL_039a IL_035e: ldstr "Text8234" IL_0363: call void [mscorlib]System.Console::WriteLine(string) IL_0368: br.s IL_039a IL_036a: ldstr "Text923423" IL_036f: call void [mscorlib]System.Console::WriteLine(string) IL_0374: br.s IL_039a IL_0376: ldstr "Text10" IL_037b: call void [mscorlib]System.Console::WriteLine(string) IL_0380: br.s IL_039a IL_0382: ldstr "Text1134123" IL_0387: call void [mscorlib]System.Console::WriteLine(string) IL_038c: br.s IL_039a IL_038e: ldstr "Defa234234ult" IL_0393: call void [mscorlib]System.Console::WriteLine(string) IL_0398: br.s IL_039a IL_039a: endfinally } // end handler IL_039b: ldloc.0 IL_039c: ret } // end of method Switch::TwoDifferentSwitchBlocksInTryFinally .method public hidebysig static string SwitchOverBool(bool b) cil managed { // Code size 62 (0x3e) .maxstack 2 .locals init (string V_0, bool V_1) IL_0000: ldstr "SwitchOverBool: " IL_0005: ldarga.s b IL_0007: call instance string [mscorlib]System.Boolean::ToString() IL_000c: call string [mscorlib]System.String::Concat(string, string) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: ldarg.0 IL_0017: stloc.1 IL_0018: ldloc.1 IL_0019: switch ( IL_0030, IL_0028) IL_0026: br.s IL_0038 IL_0028: ldsfld string [mscorlib]System.Boolean::TrueString IL_002d: stloc.0 IL_002e: br.s IL_003c IL_0030: ldsfld string [mscorlib]System.Boolean::FalseString IL_0035: stloc.0 IL_0036: br.s IL_003c IL_0038: ldnull IL_0039: stloc.0 IL_003a: br.s IL_003c IL_003c: ldloc.0 IL_003d: ret } // end of method Switch::SwitchOverBool .method public hidebysig static void SwitchInLoop(int32 i) cil managed { // Code size 117 (0x75) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchInLoop: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: br.s IL_0072 IL_0017: ldarg.0 IL_0018: stloc.0 IL_0019: ldloc.0 IL_001a: ldc.i4.1 IL_001b: sub IL_001c: switch ( IL_0033, IL_003f, IL_0057, IL_004b) IL_0031: br.s IL_0057 IL_0033: ldstr "one" IL_0038: call void [mscorlib]System.Console::WriteLine(string) IL_003d: br.s IL_006d IL_003f: ldstr "two" IL_0044: call void [mscorlib]System.Console::WriteLine(string) IL_0049: br.s IL_006d IL_004b: ldstr "four" IL_0050: call void [mscorlib]System.Console::WriteLine(string) IL_0055: br.s IL_0074 IL_0057: ldstr "default" IL_005c: call void [mscorlib]System.Console::WriteLine(string) IL_0061: ldstr "more code" IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_006b: br.s IL_0074 IL_006d: ldarg.0 IL_006e: ldc.i4.1 IL_006f: add IL_0070: starg.s i IL_0072: br.s IL_0017 IL_0074: ret } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { // Code size 120 (0x78) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchWithGoto: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ldarg.0 IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: ldc.i4.1 IL_0019: sub IL_001a: switch ( IL_0031, IL_003d, IL_0049, IL_0055) IL_002f: br.s IL_0061 IL_0031: ldstr "one" IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: br.s IL_0061 IL_003d: ldstr "two" IL_0042: call void [mscorlib]System.Console::WriteLine(string) IL_0047: br.s IL_0049 IL_0049: ldstr "three" IL_004e: call void [mscorlib]System.Console::WriteLine(string) IL_0053: br.s IL_006d IL_0055: ldstr "four" IL_005a: call void [mscorlib]System.Console::WriteLine(string) IL_005f: br.s IL_0077 IL_0061: ldstr "default" IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_006b: br.s IL_006d IL_006d: ldstr "End of method" IL_0072: call void [mscorlib]System.Console::WriteLine(string) IL_0077: ret } // end of method Switch::SwitchWithGoto .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_0) IL_0000: ldc.i4.0 IL_0001: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method Switch::GetProperties .method public hidebysig static void SwitchOnStringInForLoop() cil managed { // Code size 269 (0x10d) .maxstack 6 .locals init (class [mscorlib]System.Collections.ArrayList V_0, class [mscorlib]System.Collections.ArrayList V_1, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, int32 V_3, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, string V_5) IL_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_0005: stloc.0 IL_0006: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_000b: stloc.1 IL_000c: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() IL_0011: stloc.2 IL_0012: ldc.i4.0 IL_0013: stloc.3 IL_0014: br IL_0103 IL_0019: ldstr "In for-loop" IL_001e: call void [mscorlib]System.Console::WriteLine(string) IL_0023: ldloc.2 IL_0024: ldloc.3 IL_0025: ldelem.ref IL_0026: stloc.s V_4 IL_0028: ldstr "Name1" IL_002d: ldstr "Name2" IL_0032: ldstr "Name3" IL_0037: ldstr "Name4" IL_003c: ldstr "Name5" IL_0041: ldstr "Name6" IL_0046: leave.s IL_0048 IL_0048: ldloc.s V_4 IL_004a: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property IL_004f: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() IL_0054: dup IL_0055: stloc.s V_5 IL_0057: brfalse IL_00f4 IL_005c: ldloc.s V_5 IL_005e: call string [mscorlib]System.String::IsInterned(string) IL_0063: stloc.s V_5 IL_0065: ldloc.s V_5 IL_0067: ldstr "Name1" IL_006c: beq.s IL_009d IL_006e: ldloc.s V_5 IL_0070: ldstr "Name2" IL_0075: beq.s IL_00b0 IL_0077: ldloc.s V_5 IL_0079: ldstr "Name3" IL_007e: beq.s IL_00c3 IL_0080: ldloc.s V_5 IL_0082: ldstr "Name4" IL_0087: beq.s IL_00d6 IL_0089: ldloc.s V_5 IL_008b: ldstr "Name5" IL_0090: beq.s IL_00e9 IL_0092: ldloc.s V_5 IL_0094: ldstr "Name6" IL_0099: beq.s IL_00e9 IL_009b: br.s IL_00f4 IL_009d: ldloc.s V_4 IL_009f: ldc.i4.1 IL_00a0: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00a5: ldloc.0 IL_00a6: ldloc.s V_4 IL_00a8: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00ad: pop IL_00ae: br.s IL_00ff IL_00b0: ldloc.s V_4 IL_00b2: ldc.i4.2 IL_00b3: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00b8: ldloc.0 IL_00b9: ldloc.s V_4 IL_00bb: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00c0: pop IL_00c1: br.s IL_00ff IL_00c3: ldloc.s V_4 IL_00c5: ldc.i4.3 IL_00c6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00cb: ldloc.0 IL_00cc: ldloc.s V_4 IL_00ce: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00d3: pop IL_00d4: br.s IL_00ff IL_00d6: ldloc.s V_4 IL_00d8: ldc.i4.4 IL_00d9: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00de: ldloc.0 IL_00df: ldloc.s V_4 IL_00e1: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00e6: pop IL_00e7: br.s IL_00ff IL_00e9: ldloc.0 IL_00ea: ldloc.s V_4 IL_00ec: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00f1: pop IL_00f2: br.s IL_00ff IL_00f4: ldloc.1 IL_00f5: ldloc.s V_4 IL_00f7: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00fc: pop IL_00fd: br.s IL_00ff IL_00ff: ldloc.3 IL_0100: ldc.i4.1 IL_0101: add IL_0102: stloc.3 IL_0103: ldloc.3 IL_0104: ldloc.2 IL_0105: ldlen IL_0106: conv.i4 IL_0107: blt IL_0019 IL_010c: ret } // end of method Switch::SwitchOnStringInForLoop .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed { // Code size 141 (0x8d) .maxstack 4 .locals init (string V_0) IL_0000: ldstr "a" IL_0005: ldstr "b" IL_000a: ldstr "c" IL_000f: ldstr "d" IL_0014: leave.s IL_0016 IL_0016: ldarg.0 IL_0017: ldlen IL_0018: conv.i4 IL_0019: brfalse.s IL_0020 IL_001b: ldarg.0 IL_001c: ldc.i4.0 IL_001d: ldelem.ref IL_001e: br.s IL_0025 IL_0020: ldstr "dummy" IL_0025: dup IL_0026: stloc.0 IL_0027: brfalse.s IL_0082 IL_0029: ldloc.0 IL_002a: call string [mscorlib]System.String::IsInterned(string) IL_002f: stloc.0 IL_0030: ldloc.0 IL_0031: ldstr "a" IL_0036: beq.s IL_0052 IL_0038: ldloc.0 IL_0039: ldstr "b" IL_003e: beq.s IL_005e IL_0040: ldloc.0 IL_0041: ldstr "c" IL_0046: beq.s IL_006a IL_0048: ldloc.0 IL_0049: ldstr "d" IL_004e: beq.s IL_0076 IL_0050: br.s IL_0082 IL_0052: ldstr "a" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: br.s IL_0082 IL_005e: ldstr "b" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: br.s IL_0082 IL_006a: ldstr "c" IL_006f: call void [mscorlib]System.Console::WriteLine(string) IL_0074: br.s IL_0082 IL_0076: ldstr "d" IL_007b: call void [mscorlib]System.Console::WriteLine(string) IL_0080: br.s IL_0082 IL_0082: ldstr "end" IL_0087: call void [mscorlib]System.Console::WriteLine(string) IL_008c: ret } // end of method Switch::SwitchWithComplexCondition .method public hidebysig static void SwitchWithArray(string[] args) cil managed { // Code size 129 (0x81) .maxstack 4 .locals init (string V_0) IL_0000: ldstr "a" IL_0005: ldstr "b" IL_000a: ldstr "c" IL_000f: ldstr "d" IL_0014: leave.s IL_0016 IL_0016: ldarg.0 IL_0017: ldc.i4.0 IL_0018: ldelem.ref IL_0019: dup IL_001a: stloc.0 IL_001b: brfalse.s IL_0076 IL_001d: ldloc.0 IL_001e: call string [mscorlib]System.String::IsInterned(string) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldstr "a" IL_002a: beq.s IL_0046 IL_002c: ldloc.0 IL_002d: ldstr "b" IL_0032: beq.s IL_0052 IL_0034: ldloc.0 IL_0035: ldstr "c" IL_003a: beq.s IL_005e IL_003c: ldloc.0 IL_003d: ldstr "d" IL_0042: beq.s IL_006a IL_0044: br.s IL_0076 IL_0046: ldstr "a" IL_004b: call void [mscorlib]System.Console::WriteLine(string) IL_0050: br.s IL_0076 IL_0052: ldstr "b" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: br.s IL_0076 IL_005e: ldstr "c" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: br.s IL_0076 IL_006a: ldstr "d" IL_006f: call void [mscorlib]System.Console::WriteLine(string) IL_0074: br.s IL_0076 IL_0076: ldstr "end" IL_007b: call void [mscorlib]System.Console::WriteLine(string) IL_0080: ret } // end of method Switch::SwitchWithArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Switch::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch .class private auto ansi '' extends [mscorlib]System.Object { .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000006-1' .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000007-2' } // end of class '' // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file C:\Users\Siegfried\Projects\ILSpy master\ICSharpCode.Decompiler.Tests\TestCases\ILPretty\CS1xSwitch_Debug.res ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Release.cs ================================================ // Copyright (c) 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 // 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; using System.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Switch { public class SetProperty { public readonly PropertyInfo Property; private int _set; public int Set { get { return _set; } set { _set = value; } } public SetProperty(PropertyInfo property) { Property = property; } } public enum State { False, True, Null } public static string SparseIntegerSwitch(int i) { Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: return "-10 mln"; case -100: return "-hundred"; case -1: return "-1"; case 0: return "0"; case 1: return "1"; case 2: return "2"; case 4: return "4"; case 100: return "hundred"; case 10000: return "ten thousand"; case 10001: return "ten thousand and one"; case int.MaxValue: return "int.MaxValue"; default: return "something else"; } } public static void SwitchOverInt(int i) { switch (i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static string ShortSwitchOverString(string text) { Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; default: return "Default"; } } public static string ShortSwitchOverStringWithNullCase(string text) { Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case null: return "null"; default: return "Default"; } } public static string SwitchOverString1(string text) { Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case null: return null; default: return "Default"; } } public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } public static string TwoDifferentSwitchBlocksInTryFinally() { try { Console.WriteLine("TwoDifferentSwitchBlocks:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } finally { Console.WriteLine("Second switch:"); switch (Console.ReadLine()) { case "12": Console.WriteLine("Te43234xt1"); break; case "13": Console.WriteLine("Te223443xt2"); break; case "14": Console.WriteLine("Te234xt3"); break; case "15": Console.WriteLine("Tex243t4"); break; case "16": Console.WriteLine("Tex243t5"); break; case "17": Console.WriteLine("Text2346"); break; case "18": Console.WriteLine("Text234234"); break; case "19 case": Console.WriteLine("Text8234"); break; case "20 case": Console.WriteLine("Text923423"); break; case "21 case": Console.WriteLine("Text10"); break; case "22 case": Console.WriteLine("Text1134123"); break; default: Console.WriteLine("Defa234234ult"); break; } } } public static string SwitchOverBool(bool b) { Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; case false: return bool.FalseString; default: return null; } } public static void SwitchInLoop(int i) { Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: Console.WriteLine("one"); break; case 2: Console.WriteLine("two"); break; //case 3: // Console.WriteLine("three"); // continue; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); Console.WriteLine("more code"); return; } i++; } } public static void SwitchWithGoto(int i) { Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: Console.WriteLine("one"); goto default; case 2: Console.WriteLine("two"); goto case 3; case 3: Console.WriteLine("three"); break; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); break; } Console.WriteLine("End of method"); } private static SetProperty[] GetProperties() { return new SetProperty[0]; } public static void SwitchOnStringInForLoop() { ArrayList arrayList = new ArrayList(); ArrayList arrayList2 = new ArrayList(); SetProperty[] properties = GetProperties(); for (int i = 0; i < properties.Length; i++) { Console.WriteLine("In for-loop"); SetProperty setProperty = properties[i]; switch (setProperty.Property.Name) { case "Name1": setProperty.Set = 1; arrayList.Add(setProperty); break; case "Name2": setProperty.Set = 2; arrayList.Add(setProperty); break; case "Name3": setProperty.Set = 3; arrayList.Add(setProperty); break; case "Name4": setProperty.Set = 4; arrayList.Add(setProperty); break; case "Name5": case "Name6": arrayList.Add(setProperty); break; default: arrayList2.Add(setProperty); break; } } } public static void SwitchWithComplexCondition(string[] args) { switch ((args.Length == 0) ? "dummy" : args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } public static void SwitchWithArray(string[] args) { switch (args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Release.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v1.1.4322 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 1:0:5000:0 } .assembly CS1xSwitch_Release { .hash algorithm 0x00008004 .ver 0:0:0:0 } .module CS1xSwitch_Release.dll // MVID: {E26201E5-5EC1-412E-BE6D-AE48AFAE6535} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x07560000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch extends [mscorlib]System.Object { .class auto ansi nested public beforefieldinit SetProperty extends [mscorlib]System.Object { .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property .field private int32 _set .method public hidebysig specialname instance int32 get_Set() cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::_set IL_0006: ret } // end of method SetProperty::get_Set .method public hidebysig specialname instance void set_Set(int32 'value') cil managed { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::_set IL_0007: ret } // end of method SetProperty::set_Set .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property IL_000d: ret } // end of method SetProperty::.ctor .property instance int32 Set() { .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) } // end of property SetProperty::Set } // end of class SetProperty .class auto ansi sealed nested public State extends [mscorlib]System.Enum { .field public specialname rtspecialname int32 value__ .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State False = int32(0x00000000) .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State True = int32(0x00000001) .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/State Null = int32(0x00000002) } // end of class State .method public hidebysig static string SparseIntegerSwitch(int32 i) cil managed { // Code size 181 (0xb5) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SparseIntegerSwitch: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ldarg.0 IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: ldc.i4.4 IL_0019: bgt.s IL_004a IL_001b: ldloc.0 IL_001c: ldc.i4 0xff676980 IL_0021: beq.s IL_006d IL_0023: ldloc.0 IL_0024: ldc.i4.s -100 IL_0026: beq.s IL_0073 IL_0028: ldloc.0 IL_0029: ldc.i4.m1 IL_002a: sub IL_002b: switch ( IL_0079, IL_007f, IL_0085, IL_008b, IL_00af, IL_0091) IL_0048: br.s IL_00af IL_004a: ldloc.0 IL_004b: ldc.i4.s 100 IL_004d: beq.s IL_0097 IL_004f: ldloc.0 IL_0050: ldc.i4 0x2710 IL_0055: sub IL_0056: switch ( IL_009d, IL_00a3) IL_0063: ldloc.0 IL_0064: ldc.i4 0x7fffffff IL_0069: beq.s IL_00a9 IL_006b: br.s IL_00af IL_006d: ldstr "-10 mln" IL_0072: ret IL_0073: ldstr "-hundred" IL_0078: ret IL_0079: ldstr "-1" IL_007e: ret IL_007f: ldstr "0" IL_0084: ret IL_0085: ldstr "1" IL_008a: ret IL_008b: ldstr "2" IL_0090: ret IL_0091: ldstr "4" IL_0096: ret IL_0097: ldstr "hundred" IL_009c: ret IL_009d: ldstr "ten thousand" IL_00a2: ret IL_00a3: ldstr "ten thousand and one" IL_00a8: ret IL_00a9: ldstr "int.MaxValue" IL_00ae: ret IL_00af: ldstr "something else" IL_00b4: ret } // end of method Switch::SparseIntegerSwitch .method public hidebysig static void SwitchOverInt(int32 i) cil managed { // Code size 125 (0x7d) .maxstack 2 .locals init (int32 V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.s 10 IL_0005: bgt.s IL_0015 IL_0007: ldloc.0 IL_0008: ldc.i4.0 IL_0009: beq.s IL_0030 IL_000b: ldloc.0 IL_000c: ldc.i4.5 IL_000d: beq.s IL_003b IL_000f: ldloc.0 IL_0010: ldc.i4.s 10 IL_0012: beq.s IL_0046 IL_0014: ret IL_0015: ldloc.0 IL_0016: ldc.i4.s 20 IL_0018: bgt.s IL_0025 IL_001a: ldloc.0 IL_001b: ldc.i4.s 15 IL_001d: beq.s IL_0051 IL_001f: ldloc.0 IL_0020: ldc.i4.s 20 IL_0022: beq.s IL_005c IL_0024: ret IL_0025: ldloc.0 IL_0026: ldc.i4.s 25 IL_0028: beq.s IL_0067 IL_002a: ldloc.0 IL_002b: ldc.i4.s 30 IL_002d: beq.s IL_0072 IL_002f: ret IL_0030: ldstr "zero" IL_0035: call void [mscorlib]System.Console::WriteLine(string) IL_003a: ret IL_003b: ldstr "five" IL_0040: call void [mscorlib]System.Console::WriteLine(string) IL_0045: ret IL_0046: ldstr "ten" IL_004b: call void [mscorlib]System.Console::WriteLine(string) IL_0050: ret IL_0051: ldstr "fifteen" IL_0056: call void [mscorlib]System.Console::WriteLine(string) IL_005b: ret IL_005c: ldstr "twenty" IL_0061: call void [mscorlib]System.Console::WriteLine(string) IL_0066: ret IL_0067: ldstr "twenty-five" IL_006c: call void [mscorlib]System.Console::WriteLine(string) IL_0071: ret IL_0072: ldstr "thirty" IL_0077: call void [mscorlib]System.Console::WriteLine(string) IL_007c: ret } // end of method Switch::SwitchOverInt .method public hidebysig static string ShortSwitchOverString(string text) cil managed { // Code size 95 (0x5f) .maxstack 3 .locals init (string V_0) IL_0000: ldstr "ShortSwitchOverString: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: ldstr "Third case" IL_001f: leave.s IL_0021 IL_0021: ldarg.0 IL_0022: dup IL_0023: stloc.0 IL_0024: brfalse.s IL_0059 IL_0026: ldloc.0 IL_0027: call string [mscorlib]System.String::IsInterned(string) IL_002c: stloc.0 IL_002d: ldloc.0 IL_002e: ldstr "First case" IL_0033: beq.s IL_0047 IL_0035: ldloc.0 IL_0036: ldstr "Second case" IL_003b: beq.s IL_004d IL_003d: ldloc.0 IL_003e: ldstr "Third case" IL_0043: beq.s IL_0053 IL_0045: br.s IL_0059 IL_0047: ldstr "Text1" IL_004c: ret IL_004d: ldstr "Text2" IL_0052: ret IL_0053: ldstr "Text3" IL_0058: ret IL_0059: ldstr "Default" IL_005e: ret } // end of method Switch::ShortSwitchOverString .method public hidebysig static string ShortSwitchOverStringWithNullCase(string text) cil managed { // Code size 82 (0x52) .maxstack 2 .locals init (string V_0) IL_0000: ldstr "ShortSwitchOverStringWithNullCase: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: leave.s IL_001c IL_001c: ldarg.0 IL_001d: dup IL_001e: stloc.0 IL_001f: brfalse.s IL_0046 IL_0021: ldloc.0 IL_0022: call string [mscorlib]System.String::IsInterned(string) IL_0027: stloc.0 IL_0028: ldloc.0 IL_0029: ldstr "First case" IL_002e: beq.s IL_003a IL_0030: ldloc.0 IL_0031: ldstr "Second case" IL_0036: beq.s IL_0040 IL_0038: br.s IL_004c IL_003a: ldstr "Text1" IL_003f: ret IL_0040: ldstr "Text2" IL_0045: ret IL_0046: ldstr "null" IL_004b: ret IL_004c: ldstr "Default" IL_0051: ret } // end of method Switch::ShortSwitchOverStringWithNullCase .method public hidebysig static string SwitchOverString1(string text) cil managed { // Code size 167 (0xa7) .maxstack 7 .locals init (string V_0) IL_0000: ldstr "SwitchOverString1: " IL_0005: ldarg.0 IL_0006: call string [mscorlib]System.String::Concat(string, string) IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: ldstr "First case" IL_0015: ldstr "Second case" IL_001a: ldstr "2nd case" IL_001f: ldstr "Third case" IL_0024: ldstr "Fourth case" IL_0029: ldstr "Fifth case" IL_002e: ldstr "Sixth case" IL_0033: leave.s IL_0035 IL_0035: ldarg.0 IL_0036: dup IL_0037: stloc.0 IL_0038: brfalse.s IL_009f IL_003a: ldloc.0 IL_003b: call string [mscorlib]System.String::IsInterned(string) IL_0040: stloc.0 IL_0041: ldloc.0 IL_0042: ldstr "First case" IL_0047: beq.s IL_007b IL_0049: ldloc.0 IL_004a: ldstr "Second case" IL_004f: beq.s IL_0081 IL_0051: ldloc.0 IL_0052: ldstr "2nd case" IL_0057: beq.s IL_0081 IL_0059: ldloc.0 IL_005a: ldstr "Third case" IL_005f: beq.s IL_0087 IL_0061: ldloc.0 IL_0062: ldstr "Fourth case" IL_0067: beq.s IL_008d IL_0069: ldloc.0 IL_006a: ldstr "Fifth case" IL_006f: beq.s IL_0093 IL_0071: ldloc.0 IL_0072: ldstr "Sixth case" IL_0077: beq.s IL_0099 IL_0079: br.s IL_00a1 IL_007b: ldstr "Text1" IL_0080: ret IL_0081: ldstr "Text2" IL_0086: ret IL_0087: ldstr "Text3" IL_008c: ret IL_008d: ldstr "Text4" IL_0092: ret IL_0093: ldstr "Text5" IL_0098: ret IL_0099: ldstr "Text6" IL_009e: ret IL_009f: ldnull IL_00a0: ret IL_00a1: ldstr "Default" IL_00a6: ret } // end of method Switch::SwitchOverString1 .method public hidebysig static string SwitchOverString2() cil managed { // Code size 389 (0x185) .maxstack 4 .locals init (object V_0) IL_0000: volatile. IL_0002: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_0007: brtrue IL_00dc IL_000c: ldc.i4.s 24 IL_000e: ldc.r4 0.5 IL_0013: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_0018: dup IL_0019: ldstr "First case" IL_001e: ldc.i4.0 IL_001f: box [mscorlib]System.Int32 IL_0024: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0029: dup IL_002a: ldstr "Second case" IL_002f: ldc.i4.1 IL_0030: box [mscorlib]System.Int32 IL_0035: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_003a: dup IL_003b: ldstr "Third case" IL_0040: ldc.i4.2 IL_0041: box [mscorlib]System.Int32 IL_0046: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_004b: dup IL_004c: ldstr "Fourth case" IL_0051: ldc.i4.3 IL_0052: box [mscorlib]System.Int32 IL_0057: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_005c: dup IL_005d: ldstr "Fifth case" IL_0062: ldc.i4.4 IL_0063: box [mscorlib]System.Int32 IL_0068: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_006d: dup IL_006e: ldstr "Sixth case" IL_0073: ldc.i4.5 IL_0074: box [mscorlib]System.Int32 IL_0079: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_007e: dup IL_007f: ldstr "Seventh case" IL_0084: ldc.i4.6 IL_0085: box [mscorlib]System.Int32 IL_008a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_008f: dup IL_0090: ldstr "Eighth case" IL_0095: ldc.i4.7 IL_0096: box [mscorlib]System.Int32 IL_009b: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00a0: dup IL_00a1: ldstr "Ninth case" IL_00a6: ldc.i4.8 IL_00a7: box [mscorlib]System.Int32 IL_00ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00b1: dup IL_00b2: ldstr "Tenth case" IL_00b7: ldc.i4.s 9 IL_00b9: box [mscorlib]System.Int32 IL_00be: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00c3: dup IL_00c4: ldstr "Eleventh case" IL_00c9: ldc.i4.s 10 IL_00cb: box [mscorlib]System.Int32 IL_00d0: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00d5: volatile. IL_00d7: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_00dc: ldstr "SwitchOverString2:" IL_00e1: call void [mscorlib]System.Console::WriteLine(string) IL_00e6: call string [mscorlib]System.Environment::get_UserName() IL_00eb: dup IL_00ec: stloc.0 IL_00ed: brfalse IL_017f IL_00f2: volatile. IL_00f4: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000006-1' IL_00f9: ldloc.0 IL_00fa: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_00ff: dup IL_0100: stloc.0 IL_0101: brfalse.s IL_017f IL_0103: ldloc.0 IL_0104: unbox [mscorlib]System.Int32 IL_0109: ldind.i4 IL_010a: switch ( IL_013d, IL_0143, IL_0149, IL_014f, IL_0155, IL_015b, IL_0161, IL_0167, IL_016d, IL_0173, IL_0179) IL_013b: br.s IL_017f IL_013d: ldstr "Text1" IL_0142: ret IL_0143: ldstr "Text2" IL_0148: ret IL_0149: ldstr "Text3" IL_014e: ret IL_014f: ldstr "Text4" IL_0154: ret IL_0155: ldstr "Text5" IL_015a: ret IL_015b: ldstr "Text6" IL_0160: ret IL_0161: ldstr "Text7" IL_0166: ret IL_0167: ldstr "Text8" IL_016c: ret IL_016d: ldstr "Text9" IL_0172: ret IL_0173: ldstr "Text10" IL_0178: ret IL_0179: ldstr "Text11" IL_017e: ret IL_017f: ldstr "Default" IL_0184: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string TwoDifferentSwitchBlocksInTryFinally() cil managed { // Code size 923 (0x39b) .maxstack 4 .locals init (string V_0, object V_1) IL_0000: volatile. IL_0002: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_0007: brtrue IL_01b8 IL_000c: ldc.i4.s 24 IL_000e: ldc.r4 0.5 IL_0013: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_0018: dup IL_0019: ldstr "12" IL_001e: ldc.i4.0 IL_001f: box [mscorlib]System.Int32 IL_0024: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0029: dup IL_002a: ldstr "13" IL_002f: ldc.i4.1 IL_0030: box [mscorlib]System.Int32 IL_0035: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_003a: dup IL_003b: ldstr "14" IL_0040: ldc.i4.2 IL_0041: box [mscorlib]System.Int32 IL_0046: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_004b: dup IL_004c: ldstr "15" IL_0051: ldc.i4.3 IL_0052: box [mscorlib]System.Int32 IL_0057: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_005c: dup IL_005d: ldstr "16" IL_0062: ldc.i4.4 IL_0063: box [mscorlib]System.Int32 IL_0068: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_006d: dup IL_006e: ldstr "17" IL_0073: ldc.i4.5 IL_0074: box [mscorlib]System.Int32 IL_0079: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_007e: dup IL_007f: ldstr "18" IL_0084: ldc.i4.6 IL_0085: box [mscorlib]System.Int32 IL_008a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_008f: dup IL_0090: ldstr "19 case" IL_0095: ldc.i4.7 IL_0096: box [mscorlib]System.Int32 IL_009b: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00a0: dup IL_00a1: ldstr "20 case" IL_00a6: ldc.i4.8 IL_00a7: box [mscorlib]System.Int32 IL_00ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00b1: dup IL_00b2: ldstr "21 case" IL_00b7: ldc.i4.s 9 IL_00b9: box [mscorlib]System.Int32 IL_00be: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00c3: dup IL_00c4: ldstr "22 case" IL_00c9: ldc.i4.s 10 IL_00cb: box [mscorlib]System.Int32 IL_00d0: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_00d5: volatile. IL_00d7: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_00dc: volatile. IL_00de: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' IL_00e3: brtrue IL_01b8 IL_00e8: ldc.i4.s 24 IL_00ea: ldc.r4 0.5 IL_00ef: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32) IL_00f4: dup IL_00f5: ldstr "First case" IL_00fa: ldc.i4.0 IL_00fb: box [mscorlib]System.Int32 IL_0100: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0105: dup IL_0106: ldstr "Second case" IL_010b: ldc.i4.1 IL_010c: box [mscorlib]System.Int32 IL_0111: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0116: dup IL_0117: ldstr "Third case" IL_011c: ldc.i4.2 IL_011d: box [mscorlib]System.Int32 IL_0122: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0127: dup IL_0128: ldstr "Fourth case" IL_012d: ldc.i4.3 IL_012e: box [mscorlib]System.Int32 IL_0133: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0138: dup IL_0139: ldstr "Fifth case" IL_013e: ldc.i4.4 IL_013f: box [mscorlib]System.Int32 IL_0144: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_0149: dup IL_014a: ldstr "Sixth case" IL_014f: ldc.i4.5 IL_0150: box [mscorlib]System.Int32 IL_0155: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_015a: dup IL_015b: ldstr "Seventh case" IL_0160: ldc.i4.6 IL_0161: box [mscorlib]System.Int32 IL_0166: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_016b: dup IL_016c: ldstr "Eighth case" IL_0171: ldc.i4.7 IL_0172: box [mscorlib]System.Int32 IL_0177: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_017c: dup IL_017d: ldstr "Ninth case" IL_0182: ldc.i4.8 IL_0183: box [mscorlib]System.Int32 IL_0188: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_018d: dup IL_018e: ldstr "Tenth case" IL_0193: ldc.i4.s 9 IL_0195: box [mscorlib]System.Int32 IL_019a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_019f: dup IL_01a0: ldstr "Eleventh case" IL_01a5: ldc.i4.s 10 IL_01a7: box [mscorlib]System.Int32 IL_01ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object) IL_01b1: volatile. IL_01b3: stsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' .try { IL_01b8: ldstr "TwoDifferentSwitchBlocks:" IL_01bd: call void [mscorlib]System.Console::WriteLine(string) IL_01c2: call string [mscorlib]System.Environment::get_UserName() IL_01c7: dup IL_01c8: stloc.1 IL_01c9: brfalse IL_0295 IL_01ce: volatile. IL_01d0: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-2' IL_01d5: ldloc.1 IL_01d6: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_01db: dup IL_01dc: stloc.1 IL_01dd: brfalse IL_0295 IL_01e2: ldloc.1 IL_01e3: unbox [mscorlib]System.Int32 IL_01e8: ldind.i4 IL_01e9: switch ( IL_021c, IL_0227, IL_0232, IL_023d, IL_0248, IL_0253, IL_025e, IL_0269, IL_0274, IL_027f, IL_028a) IL_021a: br.s IL_0295 IL_021c: ldstr "Text1" IL_0221: stloc.0 IL_0222: leave IL_0399 IL_0227: ldstr "Text2" IL_022c: stloc.0 IL_022d: leave IL_0399 IL_0232: ldstr "Text3" IL_0237: stloc.0 IL_0238: leave IL_0399 IL_023d: ldstr "Text4" IL_0242: stloc.0 IL_0243: leave IL_0399 IL_0248: ldstr "Text5" IL_024d: stloc.0 IL_024e: leave IL_0399 IL_0253: ldstr "Text6" IL_0258: stloc.0 IL_0259: leave IL_0399 IL_025e: ldstr "Text7" IL_0263: stloc.0 IL_0264: leave IL_0399 IL_0269: ldstr "Text8" IL_026e: stloc.0 IL_026f: leave IL_0399 IL_0274: ldstr "Text9" IL_0279: stloc.0 IL_027a: leave IL_0399 IL_027f: ldstr "Text10" IL_0284: stloc.0 IL_0285: leave IL_0399 IL_028a: ldstr "Text11" IL_028f: stloc.0 IL_0290: leave IL_0399 IL_0295: ldstr "Default" IL_029a: stloc.0 IL_029b: leave IL_0399 } // end .try finally { IL_02a0: ldstr "Second switch:" IL_02a5: call void [mscorlib]System.Console::WriteLine(string) IL_02aa: call string [mscorlib]System.Console::ReadLine() IL_02af: dup IL_02b0: stloc.1 IL_02b1: brfalse IL_038e IL_02b6: volatile. IL_02b8: ldsfld class [mscorlib]System.Collections.Hashtable ''::'$$method0x6000007-1' IL_02bd: ldloc.1 IL_02be: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object) IL_02c3: dup IL_02c4: stloc.1 IL_02c5: brfalse IL_038e IL_02ca: ldloc.1 IL_02cb: unbox [mscorlib]System.Int32 IL_02d0: ldind.i4 IL_02d1: switch ( IL_0307, IL_0316, IL_0322, IL_032e, IL_033a, IL_0346, IL_0352, IL_035e, IL_036a, IL_0376, IL_0382) IL_0302: br IL_038e IL_0307: ldstr "Te43234xt1" IL_030c: call void [mscorlib]System.Console::WriteLine(string) IL_0311: br IL_0398 IL_0316: ldstr "Te223443xt2" IL_031b: call void [mscorlib]System.Console::WriteLine(string) IL_0320: br.s IL_0398 IL_0322: ldstr "Te234xt3" IL_0327: call void [mscorlib]System.Console::WriteLine(string) IL_032c: br.s IL_0398 IL_032e: ldstr "Tex243t4" IL_0333: call void [mscorlib]System.Console::WriteLine(string) IL_0338: br.s IL_0398 IL_033a: ldstr "Tex243t5" IL_033f: call void [mscorlib]System.Console::WriteLine(string) IL_0344: br.s IL_0398 IL_0346: ldstr "Text2346" IL_034b: call void [mscorlib]System.Console::WriteLine(string) IL_0350: br.s IL_0398 IL_0352: ldstr "Text234234" IL_0357: call void [mscorlib]System.Console::WriteLine(string) IL_035c: br.s IL_0398 IL_035e: ldstr "Text8234" IL_0363: call void [mscorlib]System.Console::WriteLine(string) IL_0368: br.s IL_0398 IL_036a: ldstr "Text923423" IL_036f: call void [mscorlib]System.Console::WriteLine(string) IL_0374: br.s IL_0398 IL_0376: ldstr "Text10" IL_037b: call void [mscorlib]System.Console::WriteLine(string) IL_0380: br.s IL_0398 IL_0382: ldstr "Text1134123" IL_0387: call void [mscorlib]System.Console::WriteLine(string) IL_038c: br.s IL_0398 IL_038e: ldstr "Defa234234ult" IL_0393: call void [mscorlib]System.Console::WriteLine(string) IL_0398: endfinally } // end handler IL_0399: ldloc.0 IL_039a: ret } // end of method Switch::TwoDifferentSwitchBlocksInTryFinally .method public hidebysig static string SwitchOverBool(bool b) cil managed { // Code size 54 (0x36) .maxstack 2 .locals init (bool V_0) IL_0000: ldstr "SwitchOverBool: " IL_0005: ldarga.s b IL_0007: call instance string [mscorlib]System.Boolean::ToString() IL_000c: call string [mscorlib]System.String::Concat(string, string) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: ldarg.0 IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: switch ( IL_002e, IL_0028) IL_0026: br.s IL_0034 IL_0028: ldsfld string [mscorlib]System.Boolean::TrueString IL_002d: ret IL_002e: ldsfld string [mscorlib]System.Boolean::FalseString IL_0033: ret IL_0034: ldnull IL_0035: ret } // end of method Switch::SwitchOverBool .method public hidebysig static void SwitchInLoop(int32 i) cil managed { // Code size 112 (0x70) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchInLoop: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ldarg.0 IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: ldc.i4.1 IL_0019: sub IL_001a: switch ( IL_0031, IL_003d, IL_0054, IL_0049) IL_002f: br.s IL_0054 IL_0031: ldstr "one" IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: br.s IL_0069 IL_003d: ldstr "two" IL_0042: call void [mscorlib]System.Console::WriteLine(string) IL_0047: br.s IL_0069 IL_0049: ldstr "four" IL_004e: call void [mscorlib]System.Console::WriteLine(string) IL_0053: ret IL_0054: ldstr "default" IL_0059: call void [mscorlib]System.Console::WriteLine(string) IL_005e: ldstr "more code" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: ret IL_0069: ldarg.0 IL_006a: ldc.i4.1 IL_006b: add IL_006c: starg.s i IL_006e: br.s IL_0015 } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { // Code size 115 (0x73) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchWithGoto: " IL_0005: ldarg.0 IL_0006: box [mscorlib]System.Int32 IL_000b: call string [mscorlib]System.String::Concat(object, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ldarg.0 IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: ldc.i4.1 IL_0019: sub IL_001a: switch ( IL_0031, IL_003d, IL_0047, IL_0053) IL_002f: br.s IL_005e IL_0031: ldstr "one" IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: br.s IL_005e IL_003d: ldstr "two" IL_0042: call void [mscorlib]System.Console::WriteLine(string) IL_0047: ldstr "three" IL_004c: call void [mscorlib]System.Console::WriteLine(string) IL_0051: br.s IL_0068 IL_0053: ldstr "four" IL_0058: call void [mscorlib]System.Console::WriteLine(string) IL_005d: ret IL_005e: ldstr "default" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: ldstr "End of method" IL_006d: call void [mscorlib]System.Console::WriteLine(string) IL_0072: ret } // end of method Switch::SwitchWithGoto .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty IL_0006: ret } // end of method Switch::GetProperties .method public hidebysig static void SwitchOnStringInForLoop() cil managed { // Code size 267 (0x10b) .maxstack 6 .locals init (class [mscorlib]System.Collections.ArrayList V_0, class [mscorlib]System.Collections.ArrayList V_1, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, int32 V_3, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, string V_5) IL_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_0005: stloc.0 IL_0006: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_000b: stloc.1 IL_000c: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() IL_0011: stloc.2 IL_0012: ldc.i4.0 IL_0013: stloc.3 IL_0014: br IL_0101 IL_0019: ldstr "In for-loop" IL_001e: call void [mscorlib]System.Console::WriteLine(string) IL_0023: ldloc.2 IL_0024: ldloc.3 IL_0025: ldelem.ref IL_0026: stloc.s V_4 IL_0028: ldstr "Name1" IL_002d: ldstr "Name2" IL_0032: ldstr "Name3" IL_0037: ldstr "Name4" IL_003c: ldstr "Name5" IL_0041: ldstr "Name6" IL_0046: leave.s IL_0048 IL_0048: ldloc.s V_4 IL_004a: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property IL_004f: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() IL_0054: dup IL_0055: stloc.s V_5 IL_0057: brfalse IL_00f4 IL_005c: ldloc.s V_5 IL_005e: call string [mscorlib]System.String::IsInterned(string) IL_0063: stloc.s V_5 IL_0065: ldloc.s V_5 IL_0067: ldstr "Name1" IL_006c: beq.s IL_009d IL_006e: ldloc.s V_5 IL_0070: ldstr "Name2" IL_0075: beq.s IL_00b0 IL_0077: ldloc.s V_5 IL_0079: ldstr "Name3" IL_007e: beq.s IL_00c3 IL_0080: ldloc.s V_5 IL_0082: ldstr "Name4" IL_0087: beq.s IL_00d6 IL_0089: ldloc.s V_5 IL_008b: ldstr "Name5" IL_0090: beq.s IL_00e9 IL_0092: ldloc.s V_5 IL_0094: ldstr "Name6" IL_0099: beq.s IL_00e9 IL_009b: br.s IL_00f4 IL_009d: ldloc.s V_4 IL_009f: ldc.i4.1 IL_00a0: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00a5: ldloc.0 IL_00a6: ldloc.s V_4 IL_00a8: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00ad: pop IL_00ae: br.s IL_00fd IL_00b0: ldloc.s V_4 IL_00b2: ldc.i4.2 IL_00b3: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00b8: ldloc.0 IL_00b9: ldloc.s V_4 IL_00bb: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00c0: pop IL_00c1: br.s IL_00fd IL_00c3: ldloc.s V_4 IL_00c5: ldc.i4.3 IL_00c6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00cb: ldloc.0 IL_00cc: ldloc.s V_4 IL_00ce: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00d3: pop IL_00d4: br.s IL_00fd IL_00d6: ldloc.s V_4 IL_00d8: ldc.i4.4 IL_00d9: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) IL_00de: ldloc.0 IL_00df: ldloc.s V_4 IL_00e1: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00e6: pop IL_00e7: br.s IL_00fd IL_00e9: ldloc.0 IL_00ea: ldloc.s V_4 IL_00ec: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00f1: pop IL_00f2: br.s IL_00fd IL_00f4: ldloc.1 IL_00f5: ldloc.s V_4 IL_00f7: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_00fc: pop IL_00fd: ldloc.3 IL_00fe: ldc.i4.1 IL_00ff: add IL_0100: stloc.3 IL_0101: ldloc.3 IL_0102: ldloc.2 IL_0103: ldlen IL_0104: conv.i4 IL_0105: blt IL_0019 IL_010a: ret } // end of method Switch::SwitchOnStringInForLoop .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed { // Code size 139 (0x8b) .maxstack 4 .locals init (string V_0) IL_0000: ldstr "a" IL_0005: ldstr "b" IL_000a: ldstr "c" IL_000f: ldstr "d" IL_0014: leave.s IL_0016 IL_0016: ldarg.0 IL_0017: ldlen IL_0018: conv.i4 IL_0019: brfalse.s IL_0020 IL_001b: ldarg.0 IL_001c: ldc.i4.0 IL_001d: ldelem.ref IL_001e: br.s IL_0025 IL_0020: ldstr "dummy" IL_0025: dup IL_0026: stloc.0 IL_0027: brfalse.s IL_0080 IL_0029: ldloc.0 IL_002a: call string [mscorlib]System.String::IsInterned(string) IL_002f: stloc.0 IL_0030: ldloc.0 IL_0031: ldstr "a" IL_0036: beq.s IL_0052 IL_0038: ldloc.0 IL_0039: ldstr "b" IL_003e: beq.s IL_005e IL_0040: ldloc.0 IL_0041: ldstr "c" IL_0046: beq.s IL_006a IL_0048: ldloc.0 IL_0049: ldstr "d" IL_004e: beq.s IL_0076 IL_0050: br.s IL_0080 IL_0052: ldstr "a" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: br.s IL_0080 IL_005e: ldstr "b" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: br.s IL_0080 IL_006a: ldstr "c" IL_006f: call void [mscorlib]System.Console::WriteLine(string) IL_0074: br.s IL_0080 IL_0076: ldstr "d" IL_007b: call void [mscorlib]System.Console::WriteLine(string) IL_0080: ldstr "end" IL_0085: call void [mscorlib]System.Console::WriteLine(string) IL_008a: ret } // end of method Switch::SwitchWithComplexCondition .method public hidebysig static void SwitchWithArray(string[] args) cil managed { // Code size 127 (0x7f) .maxstack 4 .locals init (string V_0) IL_0000: ldstr "a" IL_0005: ldstr "b" IL_000a: ldstr "c" IL_000f: ldstr "d" IL_0014: leave.s IL_0016 IL_0016: ldarg.0 IL_0017: ldc.i4.0 IL_0018: ldelem.ref IL_0019: dup IL_001a: stloc.0 IL_001b: brfalse.s IL_0074 IL_001d: ldloc.0 IL_001e: call string [mscorlib]System.String::IsInterned(string) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldstr "a" IL_002a: beq.s IL_0046 IL_002c: ldloc.0 IL_002d: ldstr "b" IL_0032: beq.s IL_0052 IL_0034: ldloc.0 IL_0035: ldstr "c" IL_003a: beq.s IL_005e IL_003c: ldloc.0 IL_003d: ldstr "d" IL_0042: beq.s IL_006a IL_0044: br.s IL_0074 IL_0046: ldstr "a" IL_004b: call void [mscorlib]System.Console::WriteLine(string) IL_0050: br.s IL_0074 IL_0052: ldstr "b" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: br.s IL_0074 IL_005e: ldstr "c" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: br.s IL_0074 IL_006a: ldstr "d" IL_006f: call void [mscorlib]System.Console::WriteLine(string) IL_0074: ldstr "end" IL_0079: call void [mscorlib]System.Console::WriteLine(string) IL_007e: ret } // end of method Switch::SwitchWithArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Switch::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch .class private auto ansi '' extends [mscorlib]System.Object { .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000006-1' .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Hashtable '$$method0x6000007-2' } // end of class '' // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file C:\Users\Siegfried\Projects\ILSpy master\ICSharpCode.Decompiler.Tests\TestCases\ILPretty\CS1xSwitch_Release.res ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs ================================================ using System; internal class CallIndirect { private unsafe void Test(IntPtr f) { ((delegate* unmanaged[Stdcall])f)(42); } private unsafe void UnmanagedDefaultCall(IntPtr f) { ((delegate* unmanaged)f)(42); } private unsafe void CustomCall(IntPtr f) { ((delegate* unmanaged[Custom])f)(42); } private unsafe void MultipleCustomCall(IntPtr f) { ((delegate* unmanaged[SuppressGCTransition, Custom])f)(42); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .class private auto ansi beforefieldinit CallIndirect extends [System.Private.CoreLib]System.Object { // Methods .method private hidebysig instance void Test ( native int f ) cil managed { .maxstack 8 ldc.i4.s 42 ldarg.1 calli unmanaged stdcall void(int32) ret } // end of method Example::Test .method private hidebysig instance void UnmanagedDefaultCall ( native int f ) cil managed { .maxstack 8 ldc.i4.s 42 ldarg.1 calli unmanaged void(int32) ret } // end of method Example::Test .method private hidebysig instance void CustomCall ( native int f ) cil managed { .maxstack 8 ldc.i4.s 42 ldarg.1 calli unmanaged void modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvCustom)(int32) ret } // end of method Example::Test .method private hidebysig instance void MultipleCustomCall ( native int f ) cil managed { .maxstack 8 ldc.i4.s 42 ldarg.1 calli unmanaged void modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvCustom) modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvSuppressGCTransition) (int32) ret } // end of method Example::Test .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206e // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Example::.ctor } // end of class Example ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ConstantBlobs.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class ConstantBlobs { public static void Float_Int32(float f1 = 0f, float f2 = -1f, float f3 = int.MaxValue, float f4 = int.MinValue) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ConstantBlobs.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConstantBlobs { .ver 1:0:0:0 } .module ConstantBlobs.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ConstantBlobs extends [mscorlib]System.Object { .method public hidebysig static void Float_Int32( [opt] float32 f1, [opt] float32 f2, [opt] float32 f3, [opt] float32 f4 ) cil managed { .param [1] = int32(0) .param [2] = int32(-1) .param [3] = int32(2147483647) .param [4] = int32(-2147483648) // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Program::Float_Int32 } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs ================================================ using System; public sealed class TestClass : IDisposable { void IDisposable.Dispose() { } public void Test(TestClass other) { ((IDisposable)this).Dispose(); ((IDisposable)other).Dispose(); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly DirectCallToExplicitInterfaceImpl { .ver 1:0:0:0 } .module DirectCallToExplicitInterfaceImpl.exe .class public auto ansi sealed TestClass extends [mscorlib]System.Object implements [mscorlib]System.IDisposable { // Methods .method private final hidebysig newslot virtual instance void System.IDisposable.Dispose () cil managed { .override method instance void [mscorlib]System.IDisposable::Dispose() ret } .method public hidebysig void Test (class TestClass other) cil managed { ldarg.0 call instance void TestClass::System.IDisposable.Dispose() ldarg.1 call instance void TestClass::System.IDisposable.Dispose() ret } } // end of class TestClass ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.cs ================================================ internal class EmptyBodies { public static void RetVoid() { } public static int RetInt() { /*Error: Method body consists only of 'ret', but nothing is being returned. Decompiled assembly might be a reference assembly.*/; } public static void Nop() { /*Error: End of method reached without returning.*/; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EmptyBodies.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .class private auto ansi beforefieldinit EmptyBodies extends [CORE_ASSEMBLY]System.Object { // I cannot test a truly empty body because the assembler will automatically add a ret instruction. .method public hidebysig static void RetVoid () cil managed { .maxstack 8 ret } .method public hidebysig static int32 RetInt () cil managed { .maxstack 8 ret } .method public hidebysig static void Nop () cil managed { .maxstack 8 nop } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206e // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Example::.ctor } // end of class EmptyBodies ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EvalOrder.cs ================================================ using System; using System.Runtime.InteropServices; internal class EvalOrder { private SimpleStruct field; public static void Test(EvalOrder p) { // ldflda (and potential NRE) before MyStruct ctor call ref SimpleStruct reference = ref p.field; reference = new SimpleStruct(1); } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct SimpleStruct { public SimpleStruct(int val) { Console.WriteLine(val); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/EvalOrder.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .class private auto ansi beforefieldinit EvalOrder extends [System.Private.CoreLib]System.Object { .field private valuetype SimpleStruct 'field' // Methods .method public hidebysig static void Test ( class EvalOrder p ) cil managed { // Method begins at RVA 0x20f0 // Code size 14 (0xe) .maxstack 8 ldarg.0 ldflda valuetype SimpleStruct EvalOrder::'field' ldc.i4.1 call instance void SimpleStruct::.ctor(int32) ret } // end of method EvalOrder::Test .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206e // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Example::.ctor } // end of class Example .class private sequential ansi sealed beforefieldinit SimpleStruct extends [System.Runtime]System.ValueType { .pack 0 .size 1 // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( int32 val ) cil managed { // Method begins at RVA 0x20f9 // Code size 9 (0x9) .maxstack 8 IL_0000: nop IL_0001: ldarg.1 IL_0002: call void [System.Console]System.Console::WriteLine(int32) IL_0007: nop IL_0008: ret } // end of method SimpleStruct::.ctor } // end of class SimpleStruct ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.cs ================================================ using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal static class ExtensionPropertiesV1 { extension(ICollection collection) where T : notnull { public bool IsEmpty => collection.Count == 0; public int Test { get { return 42; } set { } } public void AddIfNotNull(T item) { if (item != null) { collection.Add(item); } } public T2 Cast(int index) where T2 : T { return (T2)(object)collection.ElementAt(index); } public static void StaticExtension() { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.il ================================================ // To extend use: // see https://lab.razor.fyi/#fVHNbhMxEFYRqrBP0CcYbpsDVlioVDU_UrSNUBCqImVPnHC8k8TgtRd7tiSqcuMh-gbceQBeihdA66bJNlK5WDPzfTPffGP-65TzqXdLL0uhwtndaR20XcJsEwjLHm9nInPGoCLtbBAf0KLX6j-MSVnWJOcGjzhXWi6tC6RVeBoRmStwZKXZBH1M-6Tt96NSvvIoC22XT9VFLsO30OPcyhJDJRXCJJutpK8aIXGFypWVNuhFjoFCfDMZMIipR6INv-VMW0JvpYFAkrQCZWQIMF4T2qCdnXpXoSeNgbNbzhg-AP18mEwOl-nnQ1D7rAM_VugRcrgE68jWxnAW-1lVz41WMHfOwCSMy4o2MGj3iszVlmAwgG6Ptzq0JWgMQBzDlvgQMY9Uewvv017Mt_ENezzm2_aoG6cLGBXFZHHt6Lo2JslBE5adhnPfpBeQNCV4PYBm_QjtMNZadlQUkddpaT_SylPIZKB-ng6TxoK2Ba7390nhEvKD6s5JkqedxM2_oqJOS2tssERLI0ruh0TJR2K7P4z-ZjHef2RyMNcsueVsy7f87NW56Irum1Sk5-8uuuJt9-LjM20-sxd3P__-_rN4-fzLyfrkHw // created using https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-tools/NuGet/Microsoft.Net.Compilers.Toolset/overview/5.0.0-2.25380.108 .class private auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1 extends [System.Runtime]System.Object { .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname beforefieldinit '<>E__0`1' extends [System.Runtime]System.Object { // Methods .method private hidebysig specialname static void '$' ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x209d // Header size: 1 // Code size: 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<>E__0`1'::'$' .method public hidebysig specialname instance bool get_IsEmpty () cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::get_IsEmpty .method public hidebysig specialname instance int32 get_Test () cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::get_Test .method public hidebysig specialname instance void set_Test ( int32 'value' ) cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::set_Test .method public hidebysig instance void AddIfNotNull ( !T item ) cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::AddIfNotNull .method public hidebysig instance !!T2 Cast<(!T) T2> ( int32 index ) cil managed { .param type T2 .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::Cast .method public hidebysig static void StaticExtension () cil managed { // Method begins at RVA 0x209f // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '<>E__0`1'::StaticExtension // Properties .property instance bool IsEmpty() { .get instance bool ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::get_IsEmpty() } .property instance int32 Test() { .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::get_Test() .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::set_Test(int32) } } // end of class <>E__0`1 // Methods .method public hidebysig static bool get_IsEmpty ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 10 (0xa) .maxstack 8 IL_0000: ldarg.0 IL_0001: callvirt instance int32 class [System.Runtime]System.Collections.Generic.ICollection`1::get_Count() IL_0006: ldc.i4.0 IL_0007: ceq IL_0009: ret } // end of method ExtensionProperties::get_IsEmpty .method public hidebysig static int32 get_Test ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { // Method begins at RVA 0x205b // Header size: 1 // Code size: 4 (0x4) .maxstack 8 IL_0000: nop IL_0001: ldc.i4.s 42 IL_0003: ret } // end of method ExtensionProperties::get_Test .method public hidebysig static void set_Test ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, int32 'value' ) cil managed { // Method begins at RVA 0x2060 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method ExtensionProperties::set_Test .method public hidebysig static void AddIfNotNull ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, !!T item ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2064 // Header size: 12 // Code size: 25 (0x19) .maxstack 2 .locals init ( [0] bool ) IL_0000: nop IL_0001: ldarg.1 IL_0002: box !!T IL_0007: ldnull IL_0008: cgt.un IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: brfalse.s IL_0018 IL_000e: nop IL_000f: ldarg.0 IL_0010: ldarg.1 IL_0011: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0016: nop IL_0017: nop IL_0018: ret } // end of method ExtensionProperties::AddIfNotNull .method public hidebysig static !!T2 Cast ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, int32 index ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2089 // Header size: 1 // Code size: 19 (0x13) .maxstack 8 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: call !!0 [System.Linq]System.Linq.Enumerable::ElementAt(class [System.Runtime]System.Collections.Generic.IEnumerable`1, int32) IL_0008: box !!T IL_000d: unbox.any !!T2 IL_0012: ret } // end of method ExtensionProperties::Cast .method public hidebysig static void StaticExtension () cil managed { // Method begins at RVA 0x2060 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method ExtensionProperties::StaticExtension } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.cs ================================================ using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal static class ExtensionPropertiesV2 { extension(ICollection collection) where T : notnull { public bool IsEmpty => collection.Count == 0; public int Test { get { return 42; } set { } } public void AddIfNotNull(T item) { if (item != null) { collection.Add(item); } } public T2 Cast(int index) where T2 : T { return (T2)(object)collection.ElementAt(index); } public static void StaticExtension() { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.il ================================================ // To extend use: // see https://lab.razor.fyi/#fVHBbtNAEFUQArwn6BcMPdkSrFyrFVKbVIrcgIKqKKKrHkBIbOyps3S9a3bHtFEViQvfwy9w4Qf4D66ckd00dSOVy2pn3nvz5u2y748ZmzpbOFnyzG_9fVR7ZQo4WXjC8oB1K55arTEjZY3nb9CgU9l_GOOyrEnONG5wjpQsjPWkMn8_wlOb49BIvfBqk3aszJeNlpg7lLkyxX19LqQ_3xz0rjakSuSn6LyyppWzD9J7LGd6sQ9CugLptZMlXlh3Hm7zyUik1uGwql6sRIOdmMfb0UfGjCzRVzJDGKcnc-mqJgI_wsyWldLouEBPvj1T6dHz8fHUIdGCXbFAGUJnpAZPklQGmZbew-iS0DQuU2crdKTQnyYsuGJBgDdQXxyG49t374tDyNZVBBdzdAgC9sFYMrXWLGj1QVXPtMpgZq2GsR-VFS1g0NXy1NaGYDCA-IB1FMoQNCGgHRMUeHMLHFLtDOwmB229bE-_xtt62R311aochnk-PptYmtRahwIUYRk1nGuROoOwacHzATTrt9AKCzrLDvO85UUd7zteIoFUeuqL5DBsIiiT4-X6fRLYB3HrukoSiiQK7ewzZhR1vEYaSzQ0pPB6SGt5x2z1i22-k_a-_srwNlyz5JIFS7ZkW8_2eMzjlwlP9nb3dvhO_OrtA6XfB09-__n14-fZ04efevPeZe9br_cP // created using https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-tools/NuGet/Microsoft.Net.Compilers.Toolset/overview/5.0.0-2.25451.107 .assembly TestProject { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = { } .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = { int32(8) } .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = { property bool WrapNonExceptionThrows = bool(true) } .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = { int32(263) } .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = { string('.NETCoreApp,Version=10.0') } .permissionset reqmin = { [System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = { property bool SkipVerification = bool(true) } } .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class private auto ansi '' { } // end of class .class private auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2 extends [System.Runtime]System.Object { .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = { uint8(1) } .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = { uint8(0) } .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = { } // Nested Types .class nested public auto ansi sealed specialname '$847CB318C385471B1F4E7BD0A197DBCA'<$T0> extends [System.Runtime]System.Object { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = { } // Nested Types .class nested public auto ansi abstract sealed specialname '$6A79ECC07A7731C57E8EB4E3D9C8B38B' extends [System.Runtime]System.Object { // Methods .method public hidebysig specialname static void '$' ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = { } // Method begins at RVA 0x20a0 // Header size: 1 // Code size: 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '$6A79ECC07A7731C57E8EB4E3D9C8B38B'::'$' } // end of class $6A79ECC07A7731C57E8EB4E3D9C8B38B // Methods .method public hidebysig specialname instance bool get_IsEmpty () cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::get_IsEmpty .method public hidebysig specialname instance int32 get_Test () cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::get_Test .method public hidebysig specialname instance void set_Test ( int32 'value' ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::set_Test .method public hidebysig instance void AddIfNotNull ( !$T0 item ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::AddIfNotNull .method public hidebysig instance !!T2 Cast<(!$T0) T2> ( int32 index ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } .param type T2 .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = { uint8(0) } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::Cast .method public hidebysig static void StaticExtension () cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } // Method begins at RVA 0x209d // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::StaticExtension // Properties .property instance bool IsEmpty() { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } .get instance bool ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::get_IsEmpty() } .property instance int32 Test() { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = { string('$6A79ECC07A7731C57E8EB4E3D9C8B38B') } .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::get_Test() .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::set_Test(int32) } } // end of class $847CB318C385471B1F4E7BD0A197DBCA // Methods .method public hidebysig static bool get_IsEmpty ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 10 (0xa) .maxstack 8 IL_0000: ldarg.0 IL_0001: callvirt instance int32 class [System.Runtime]System.Collections.Generic.ICollection`1::get_Count() IL_0006: ldc.i4.0 IL_0007: ceq IL_0009: ret } // end of method ExtensionPropertiesV2::get_IsEmpty .method public hidebysig static int32 get_Test ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection ) cil managed { // Method begins at RVA 0x205b // Header size: 1 // Code size: 4 (0x4) .maxstack 8 IL_0000: nop IL_0001: ldc.i4.s 42 IL_0003: ret } // end of method ExtensionPropertiesV2::get_Test .method public hidebysig static void set_Test ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, int32 'value' ) cil managed { // Method begins at RVA 0x2060 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method ExtensionPropertiesV2::set_Test .method public hidebysig static void AddIfNotNull ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, !!T item ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = { } // Method begins at RVA 0x2064 // Header size: 12 // Code size: 25 (0x19) .maxstack 2 .locals init ( [0] bool ) IL_0000: nop IL_0001: ldarg.1 IL_0002: box !!T IL_0007: ldnull IL_0008: cgt.un IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: brfalse.s IL_0018 IL_000e: nop IL_000f: ldarg.0 IL_0010: ldarg.1 IL_0011: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0016: nop IL_0017: nop IL_0018: ret } // end of method ExtensionPropertiesV2::AddIfNotNull .method public hidebysig static !!T2 Cast ( class [System.Runtime]System.Collections.Generic.ICollection`1 collection, int32 index ) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = { } // Method begins at RVA 0x2089 // Header size: 1 // Code size: 19 (0x13) .maxstack 8 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: call !!0 [System.Linq]System.Linq.Enumerable::ElementAt(class [System.Runtime]System.Collections.Generic.IEnumerable`1, int32) IL_0008: box !!T IL_000d: unbox.any !!T2 IL_0012: ret } // end of method ExtensionPropertiesV2::Cast .method public hidebysig static void StaticExtension () cil managed { // Method begins at RVA 0x2060 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method ExtensionPropertiesV2::StaticExtension } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2 // references .assembly extern System.Runtime { .publickeytoken = ( b0 3f 5f 7f 11 d5 0a 3a ) .ver 10:0:0:0 } .assembly extern System.Linq { .publickeytoken = ( b0 3f 5f 7f 11 d5 0a 3a ) .ver 10:0:0:0 } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops.fs ================================================ open System let disposable() = { new IDisposable with member x.Dispose() = () } let getSeq() = seq { yield 1; } let getList() = [ 1 ] let getArray() = [| 1 |] [] let main argv = // nested using scopes? use disp1 = use disp2 = disposable() Console.WriteLine "Hello 1" disposable() // for loop over seq for i in getSeq() do Console.WriteLine i // for loop over list for i in getList() do Console.WriteLine i // for loop over array for i in getArray() do Console.WriteLine i 0 // return an integer exit code ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs ================================================ // C:\Users\Siegfried\Documents\Visual Studio 2017\Projects\ConsoleApp13\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe // ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // Global type: // Entry point: Program.main // Architecture: AnyCPU (32-bit preferred) // Runtime: .NET 4.0 using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.FSharp.Collections; using Microsoft.FSharp.Core; using Microsoft.FSharp.Core.CompilerServices; [assembly: FSharpInterfaceDataVersion(2, 0, 0)] [assembly: AssemblyTitle("ConsoleApplication1")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ConsoleApplication1")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("e0674ff5-5e8f-4d4e-a88f-e447192454c7")] [CompilationMapping(SourceConstructFlags.Module)] public static class Program { [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] internal sealed class disposable_00403 : IDisposable { private void System_002DIDisposable_002DDispose() { } void IDisposable.Dispose() { //ILSpy generated this explicit interface implementation from .override directive in System-IDisposable-Dispose this.System_002DIDisposable_002DDispose(); } } [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] internal sealed class getSeq_00405(int pc, int current) : GeneratedSequenceBase { [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] public int pc = pc; [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] public int current = current; public override int GenerateNext(ref IEnumerable next) { switch (pc) { default: pc = 1; current = 1; return 1; case 1: pc = 2; break; case 2: break; } current = 0; return 0; } public override void Close() { pc = 2; } public bool get_CheckClose() { switch (pc) { default: return false; case 0: case 2: return false; } } [DebuggerNonUserCode] [CompilerGenerated] public int get_LastGenerated() { return current; } [DebuggerNonUserCode] [CompilerGenerated] public override IEnumerator GetFreshEnumerator() { return new getSeq_00405(0, 0); } } public static IDisposable disposable() { return new disposable_00403(); } public static IEnumerable getSeq() { return new getSeq_00405(0, 0); } public static FSharpList getList() { return FSharpList.Cons(1, FSharpList.Empty); } public static int[] getArray() { return new int[1] { 1 }; } [EntryPoint] public static int main(string[] argv) { IDisposable disposable; using (Program.disposable()) { Console.WriteLine("Hello 1"); disposable = Program.disposable(); } using (disposable) { IEnumerable seq = getSeq(); foreach (int item in seq) { Console.WriteLine(item); } FSharpList fSharpList = getList(); for (FSharpList tailOrNull = fSharpList.TailOrNull; tailOrNull != null; tailOrNull = fSharpList.TailOrNull) { int headOrDefault = fSharpList.HeadOrDefault; Console.WriteLine(headOrDefault); fSharpList = tailOrNull; } int[] array = getArray(); foreach (int value in array) { Console.WriteLine(value); } return 0; } } } namespace _003CStartupCode_0024ConsoleApplication1_003E { internal static class _0024AssemblyInfo { } internal static class _0024Program { } } namespace _003CStartupCode_0024ConsoleApplication1_003E._0024.NETFramework_002CVersion_003Dv4._6._1 { internal static class AssemblyAttributes { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.il ================================================ // C:\Users\Siegfried\Documents\Visual Studio 2017\Projects\ConsoleApp13\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly extern FSharp.Core { .publickeytoken = ( b0 3f 5f 7f 11 d5 0a 3a ) .ver 4:4:1:0 } .assembly ConsoleApplication1 { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32, int32, int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1c 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b 2c 56 65 72 73 69 6f 6e 3d 76 34 2e 36 2e 31 01 00 54 0e 14 46 72 61 6d 65 77 6f 72 6b 44 69 73 70 6c 61 79 4e 61 6d 65 14 2e 4e 45 54 20 46 72 61 6d 65 77 6f 72 6b 20 34 2e 36 2e 31 ) .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 13 43 6f 6e 73 6f 6c 65 41 70 70 6c 69 63 61 74 69 6f 6e 31 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 13 43 6f 6e 73 6f 6c 65 41 70 70 6c 69 63 61 74 69 6f 6e 31 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6f 70 79 72 69 67 68 74 20 c2 a9 20 20 32 30 31 37 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCultureAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 65 30 36 37 34 66 66 35 2d 35 65 38 66 2d 34 64 34 65 2d 61 38 38 66 2d 65 34 34 37 31 39 32 34 35 34 63 37 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 01 01 00 00 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module ConsoleApplication1.exe // MVID: {59F64D20-6A1F-D4CE-A745-0383204DF659} .corflags 0x00020003 // ILOnly, Required32Bit, Preferred32Bit .class private auto ansi '' extends [mscorlib]System.Object { } // end of class .class public auto ansi abstract sealed Program extends [mscorlib]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) // Nested Types .class nested assembly auto auto sealed specialname serializable beforefieldinit disposable@3 extends [mscorlib]System.Object implements [mscorlib]System.IDisposable { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 ) // Methods .method public specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x21c8 // Code size 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: callvirt instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: pop IL_0008: ret } // end of method disposable@3::.ctor .method private final hidebysig newslot virtual instance void 'System-IDisposable-Dispose' () cil managed { .override method instance void [mscorlib]System.IDisposable::Dispose() // Method begins at RVA 0x21d4 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method disposable@3::'System-IDisposable-Dispose' } // end of class disposable@3 .class nested assembly auto auto sealed specialname serializable beforefieldinit getSeq@5 extends class [FSharp.Core]Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1 { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 ) // Fields .field public int32 pc .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 current .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public specialname rtspecialname instance void .ctor ( int32 pc, int32 current ) cil managed { // Method begins at RVA 0x21d8 // Code size 21 (0x15) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32 Program/getSeq@5::pc IL_0007: ldarg.0 IL_0008: ldarg.2 IL_0009: stfld int32 Program/getSeq@5::current IL_000e: ldarg.0 IL_000f: call instance void class [FSharp.Core]Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1::.ctor() IL_0014: ret } // end of method getSeq@5::.ctor .method public strict virtual instance int32 GenerateNext ( class [mscorlib]System.Collections.Generic.IEnumerable`1& next ) cil managed { // Method begins at RVA 0x21f0 // Code size 66 (0x42) .maxstack 6 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::pc IL_0006: ldc.i4.1 IL_0007: sub IL_0008: switch (IL_0017, IL_0019) IL_0015: br.s IL_0021 IL_0017: br.s IL_001b IL_0019: br.s IL_001e IL_001b: nop IL_001c: br.s IL_0032 IL_001e: nop IL_001f: br.s IL_0039 IL_0021: nop IL_0022: ldarg.0 IL_0023: ldc.i4.1 IL_0024: stfld int32 Program/getSeq@5::pc IL_0029: ldarg.0 IL_002a: ldc.i4.1 IL_002b: stfld int32 Program/getSeq@5::current IL_0030: ldc.i4.1 IL_0031: ret IL_0032: ldarg.0 IL_0033: ldc.i4.2 IL_0034: stfld int32 Program/getSeq@5::pc IL_0039: ldarg.0 IL_003a: ldc.i4.0 IL_003b: stfld int32 Program/getSeq@5::current IL_0040: ldc.i4.0 IL_0041: ret } // end of method getSeq@5::GenerateNext .method public strict virtual instance void Close () cil managed { // Method begins at RVA 0x2240 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.2 IL_0002: stfld int32 Program/getSeq@5::pc IL_0007: ret } // end of method getSeq@5::Close .method public strict virtual instance bool get_CheckClose () cil managed { // Method begins at RVA 0x224c // Code size 45 (0x2d) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::pc IL_0006: switch (IL_0019, IL_001b, IL_001d) IL_0017: br.s IL_0028 IL_0019: br.s IL_001f IL_001b: br.s IL_0022 IL_001d: br.s IL_0025 IL_001f: nop IL_0020: br.s IL_002b IL_0022: nop IL_0023: br.s IL_0029 IL_0025: nop IL_0026: br.s IL_002b IL_0028: nop IL_0029: ldc.i4.0 IL_002a: ret IL_002b: ldc.i4.0 IL_002c: ret } // end of method getSeq@5::get_CheckClose .method public strict virtual instance int32 get_LastGenerated () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x227c // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::current IL_0006: ret } // end of method getSeq@5::get_LastGenerated .method public strict virtual instance class [mscorlib]System.Collections.Generic.IEnumerator`1 GetFreshEnumerator () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2284 // Code size 8 (0x8) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: newobj instance void Program/getSeq@5::.ctor(int32, int32) IL_0007: ret } // end of method getSeq@5::GetFreshEnumerator } // end of class getSeq@5 // Methods .method public static class [mscorlib]System.IDisposable disposable () cil managed { // Method begins at RVA 0x2050 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void Program/disposable@3::.ctor() IL_0005: ret } // end of method Program::disposable .method public static class [mscorlib]System.Collections.Generic.IEnumerable`1 getSeq () cil managed { // Method begins at RVA 0x2058 // Code size 8 (0x8) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: newobj instance void Program/getSeq@5::.ctor(int32, int32) IL_0007: ret } // end of method Program::getSeq .method public static class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 getList () cil managed { // Method begins at RVA 0x2064 // Code size 12 (0xc) .maxstack 8 IL_0000: ldc.i4.1 IL_0001: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_Empty() IL_0006: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::Cons(!0, class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1) IL_000b: ret } // end of method Program::getList .method public static int32[] getArray () cil managed { // Method begins at RVA 0x2074 // Code size 15 (0xf) .maxstack 8 IL_0000: ldc.i4.1 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.any [mscorlib]System.Int32 IL_000e: ret } // end of method Program::getArray .method public static int32 main ( string[] argv ) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2084 // Code size 270 (0x10e) .maxstack 4 .entrypoint .locals init ( [0] class [mscorlib]System.IDisposable, [1] class [mscorlib]System.IDisposable, [2] class [mscorlib]System.IDisposable, [3] class [mscorlib]System.IDisposable, [4] int32, [5] class [mscorlib]System.Collections.Generic.IEnumerable`1, [6] class [mscorlib]System.Collections.Generic.IEnumerator`1, [7] class [FSharp.Core]Microsoft.FSharp.Core.Unit, [8] int32, [9] class [mscorlib]System.IDisposable, [10] class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [11] class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [12] int32, [13] int32[], [14] int32, [15] int32, [16] class [mscorlib]System.IDisposable ) IL_0000: call class [mscorlib]System.IDisposable Program::disposable() IL_0005: stloc.1 .try { IL_0006: ldstr "Hello 1" IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: call class [mscorlib]System.IDisposable Program::disposable() IL_0015: stloc.2 IL_0016: leave.s IL_0032 } // end .try finally { IL_0018: ldloc.1 IL_0019: isinst [mscorlib]System.IDisposable IL_001e: stloc.3 IL_001f: ldloc.3 IL_0020: brfalse.s IL_0024 IL_0022: br.s IL_0026 IL_0024: br.s IL_002f IL_0026: ldloc.3 IL_0027: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002c: ldnull IL_002d: pop IL_002e: endfinally IL_002f: ldnull IL_0030: pop IL_0031: endfinally } // end handler IL_0032: ldloc.2 IL_0033: stloc.0 .try { IL_0034: call class [mscorlib]System.Collections.Generic.IEnumerable`1 Program::getSeq() IL_0039: stloc.s 5 IL_003b: ldloc.s 5 IL_003d: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0042: stloc.s 6 .try { // loop start (head: IL_0044) IL_0044: ldloc.s 6 IL_0046: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_004b: brfalse.s IL_0060 IL_004d: ldloc.s 6 IL_004f: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_0054: stloc.s 8 IL_0056: ldloc.s 8 IL_0058: call void [mscorlib]System.Console::WriteLine(int32) IL_005d: nop IL_005e: br.s IL_0044 // end loop IL_0060: ldnull IL_0061: stloc.s 7 IL_0063: leave.s IL_0083 } // end .try finally { IL_0065: ldloc.s 6 IL_0067: isinst [mscorlib]System.IDisposable IL_006c: stloc.s 9 IL_006e: ldloc.s 9 IL_0070: brfalse.s IL_0074 IL_0072: br.s IL_0076 IL_0074: br.s IL_0080 IL_0076: ldloc.s 9 IL_0078: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_007d: ldnull IL_007e: pop IL_007f: endfinally IL_0080: ldnull IL_0081: pop IL_0082: endfinally } // end handler IL_0083: ldloc.s 7 IL_0085: pop IL_0086: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 Program::getList() IL_008b: stloc.s 10 IL_008d: ldloc.s 10 IL_008f: call instance class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_TailOrNull() IL_0094: stloc.s 11 // loop start (head: IL_0096) IL_0096: ldloc.s 11 IL_0098: ldnull IL_0099: cgt.un IL_009b: brfalse.s IL_00bd IL_009d: ldloc.s 10 IL_009f: call instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_HeadOrDefault() IL_00a4: stloc.s 12 IL_00a6: ldloc.s 12 IL_00a8: call void [mscorlib]System.Console::WriteLine(int32) IL_00ad: ldloc.s 11 IL_00af: stloc.s 10 IL_00b1: ldloc.s 10 IL_00b3: call instance class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_TailOrNull() IL_00b8: stloc.s 11 IL_00ba: nop IL_00bb: br.s IL_0096 // end loop IL_00bd: call int32[] Program::getArray() IL_00c2: stloc.s 13 IL_00c4: ldc.i4.0 IL_00c5: stloc.s 14 IL_00c7: br.s IL_00e1 // loop start (head: IL_00e1) IL_00c9: ldloc.s 13 IL_00cb: ldloc.s 14 IL_00cd: ldelem.any [mscorlib]System.Int32 IL_00d2: stloc.s 15 IL_00d4: ldloc.s 15 IL_00d6: call void [mscorlib]System.Console::WriteLine(int32) IL_00db: ldloc.s 14 IL_00dd: ldc.i4.1 IL_00de: add IL_00df: stloc.s 14 IL_00e1: ldloc.s 14 IL_00e3: ldloc.s 13 IL_00e5: ldlen IL_00e6: conv.i4 IL_00e7: blt.s IL_00c9 // end loop IL_00e9: ldc.i4.0 IL_00ea: stloc.s 4 IL_00ec: leave.s IL_010b } // end .try finally { IL_00ee: ldloc.0 IL_00ef: isinst [mscorlib]System.IDisposable IL_00f4: stloc.s 16 IL_00f6: ldloc.s 16 IL_00f8: brfalse.s IL_00fc IL_00fa: br.s IL_00fe IL_00fc: br.s IL_0108 IL_00fe: ldloc.s 16 IL_0100: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0105: ldnull IL_0106: pop IL_0107: endfinally IL_0108: ldnull IL_0109: pop IL_010a: endfinally } // end handler IL_010b: ldloc.s 4 IL_010d: ret } // end of method Program::main } // end of class Program .class private auto ansi abstract sealed '.$Program' extends [mscorlib]System.Object { } // end of class .$Program .class private auto ansi abstract sealed '.$AssemblyInfo' extends [mscorlib]System.Object { } // end of class .$AssemblyInfo .class private auto ansi abstract sealed '.$.NETFramework,Version=v4.6.1.AssemblyAttributes' extends [mscorlib]System.Object { } // end of class .$.NETFramework,Version=v4.6.1.AssemblyAttributes ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs ================================================ // C:\Users\Siegfried\Documents\Visual Studio 2017\Projects\ConsoleApp13\ConsoleApplication1\bin\Release\ConsoleApplication1.exe // ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // Global type: // Entry point: Program.main // Architecture: AnyCPU (32-bit preferred) // Runtime: .NET 4.0 using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.FSharp.Collections; using Microsoft.FSharp.Core; using Microsoft.FSharp.Core.CompilerServices; [assembly: FSharpInterfaceDataVersion(2, 0, 0)] [assembly: AssemblyTitle("ConsoleApplication1")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ConsoleApplication1")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("e0674ff5-5e8f-4d4e-a88f-e447192454c7")] [CompilationMapping(SourceConstructFlags.Module)] public static class Program { [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] internal sealed class disposable_00403 : IDisposable { private void System_002DIDisposable_002DDispose() { } void IDisposable.Dispose() { //ILSpy generated this explicit interface implementation from .override directive in System-IDisposable-Dispose this.System_002DIDisposable_002DDispose(); } } [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] internal sealed class getSeq_00405(int pc, int current) : GeneratedSequenceBase { [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] public int pc = pc; [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] public int current = current; public override int GenerateNext(ref IEnumerable next) { switch (pc) { default: pc = 1; current = 1; return 1; case 1: pc = 2; break; case 2: break; } current = 0; return 0; } public override void Close() { pc = 2; } public bool get_CheckClose() { switch (pc) { default: return false; case 0: case 2: return false; } } [DebuggerNonUserCode] [CompilerGenerated] public int get_LastGenerated() { return current; } [DebuggerNonUserCode] [CompilerGenerated] public override IEnumerator GetFreshEnumerator() { return new getSeq_00405(0, 0); } } public static IDisposable disposable() { return new disposable_00403(); } public static IEnumerable getSeq() { return new getSeq_00405(0, 0); } public static FSharpList getList() { return FSharpList.Cons(1, FSharpList.Empty); } public static int[] getArray() { return new int[1] { 1 }; } [EntryPoint] public static int main(string[] argv) { IDisposable disposable; using (Program.disposable()) { Console.WriteLine("Hello 1"); disposable = Program.disposable(); } using (disposable) { IEnumerable seq = getSeq(); foreach (int item in seq) { Console.WriteLine(item); } FSharpList fSharpList = FSharpList.Cons(1, FSharpList.Empty); for (FSharpList tailOrNull = fSharpList.TailOrNull; tailOrNull != null; tailOrNull = fSharpList.TailOrNull) { int headOrDefault = fSharpList.HeadOrDefault; Console.WriteLine(headOrDefault); fSharpList = tailOrNull; } int[] array = new int[1] { 1 }; for (int headOrDefault = 0; headOrDefault < array.Length; headOrDefault++) { Console.WriteLine(array[headOrDefault]); } return 0; } } } namespace _003CStartupCode_0024ConsoleApplication1_003E { internal static class _0024AssemblyInfo { } internal static class _0024Program { } } namespace _003CStartupCode_0024ConsoleApplication1_003E._0024.NETFramework_002CVersion_003Dv4._6._1 { internal static class AssemblyAttributes { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.il ================================================ // C:\Users\Siegfried\Documents\Visual Studio 2017\Projects\ConsoleApp13\ConsoleApplication1\bin\Release\ConsoleApplication1.exe .assembly extern mscorlib { .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 ) .ver 4:0:0:0 } .assembly extern FSharp.Core { .publickeytoken = ( b0 3f 5f 7f 11 d5 0a 3a ) .ver 4:4:1:0 } .assembly ConsoleApplication1 { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32, int32, int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1c 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b 2c 56 65 72 73 69 6f 6e 3d 76 34 2e 36 2e 31 01 00 54 0e 14 46 72 61 6d 65 77 6f 72 6b 44 69 73 70 6c 61 79 4e 61 6d 65 14 2e 4e 45 54 20 46 72 61 6d 65 77 6f 72 6b 20 34 2e 36 2e 31 ) .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 13 43 6f 6e 73 6f 6c 65 41 70 70 6c 69 63 61 74 69 6f 6e 31 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 13 43 6f 6e 73 6f 6c 65 41 70 70 6c 69 63 61 74 69 6f 6e 31 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6f 70 79 72 69 67 68 74 20 c2 a9 20 20 32 30 31 37 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCultureAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 65 30 36 37 34 66 66 35 2d 35 65 38 66 2d 34 64 34 65 2d 61 38 38 66 2d 65 34 34 37 31 39 32 34 35 34 63 37 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2e 30 2e 30 2e 30 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 00 00 00 00 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module ConsoleApplication1.exe // MVID: {59F64D28-6A1F-D4CE-A745-0383284DF659} .corflags 0x00020003 // ILOnly, Required32Bit, Preferred32Bit .class private auto ansi '' extends [mscorlib]System.Object { } // end of class .class public auto ansi abstract sealed Program extends [mscorlib]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) // Nested Types .class nested assembly auto auto sealed specialname serializable beforefieldinit disposable@3 extends [mscorlib]System.Object implements [mscorlib]System.IDisposable { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 ) // Methods .method public specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x21bc // Code size 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: callvirt instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: pop IL_0008: ret } // end of method disposable@3::.ctor .method private final hidebysig newslot virtual instance void 'System-IDisposable-Dispose' () cil managed { .override method instance void [mscorlib]System.IDisposable::Dispose() // Method begins at RVA 0x21c8 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method disposable@3::'System-IDisposable-Dispose' } // end of class disposable@3 .class nested assembly auto auto sealed specialname serializable beforefieldinit getSeq@5 extends class [FSharp.Core]Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1 { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 06 00 00 00 00 00 ) // Fields .field public int32 pc .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 current .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public specialname rtspecialname instance void .ctor ( int32 pc, int32 current ) cil managed { // Method begins at RVA 0x21cc // Code size 21 (0x15) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld int32 Program/getSeq@5::pc IL_0007: ldarg.0 IL_0008: ldarg.2 IL_0009: stfld int32 Program/getSeq@5::current IL_000e: ldarg.0 IL_000f: call instance void class [FSharp.Core]Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1::.ctor() IL_0014: ret } // end of method getSeq@5::.ctor .method public strict virtual instance int32 GenerateNext ( class [mscorlib]System.Collections.Generic.IEnumerable`1& next ) cil managed { // Method begins at RVA 0x21e4 // Code size 62 (0x3e) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::pc IL_0006: ldc.i4.1 IL_0007: sub IL_0008: switch (IL_0018, IL_001b) IL_0015: nop IL_0016: br.s IL_001e IL_0018: nop IL_0019: br.s IL_002e IL_001b: nop IL_001c: br.s IL_0035 IL_001e: ldarg.0 IL_001f: ldc.i4.1 IL_0020: stfld int32 Program/getSeq@5::pc IL_0025: ldarg.0 IL_0026: ldc.i4.1 IL_0027: stfld int32 Program/getSeq@5::current IL_002c: ldc.i4.1 IL_002d: ret IL_002e: ldarg.0 IL_002f: ldc.i4.2 IL_0030: stfld int32 Program/getSeq@5::pc IL_0035: ldarg.0 IL_0036: ldc.i4.0 IL_0037: stfld int32 Program/getSeq@5::current IL_003c: ldc.i4.0 IL_003d: ret } // end of method getSeq@5::GenerateNext .method public strict virtual instance void Close () cil managed { // Method begins at RVA 0x2224 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.2 IL_0002: stfld int32 Program/getSeq@5::pc IL_0007: ret } // end of method getSeq@5::Close .method public strict virtual instance bool get_CheckClose () cil managed { // Method begins at RVA 0x2230 // Code size 39 (0x27) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::pc IL_0006: switch (IL_001a, IL_001d, IL_0020) IL_0017: nop IL_0018: br.s IL_0023 IL_001a: nop IL_001b: br.s IL_0025 IL_001d: nop IL_001e: br.s IL_0023 IL_0020: nop IL_0021: br.s IL_0025 IL_0023: ldc.i4.0 IL_0024: ret IL_0025: ldc.i4.0 IL_0026: ret } // end of method getSeq@5::get_CheckClose .method public strict virtual instance int32 get_LastGenerated () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2258 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld int32 Program/getSeq@5::current IL_0006: ret } // end of method getSeq@5::get_LastGenerated .method public strict virtual instance class [mscorlib]System.Collections.Generic.IEnumerator`1 GetFreshEnumerator () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2260 // Code size 8 (0x8) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: newobj instance void Program/getSeq@5::.ctor(int32, int32) IL_0007: ret } // end of method getSeq@5::GetFreshEnumerator } // end of class getSeq@5 // Methods .method public static class [mscorlib]System.IDisposable disposable () cil managed { // Method begins at RVA 0x2050 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void Program/disposable@3::.ctor() IL_0005: ret } // end of method Program::disposable .method public static class [mscorlib]System.Collections.Generic.IEnumerable`1 getSeq () cil managed { // Method begins at RVA 0x2058 // Code size 8 (0x8) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: ldc.i4.0 IL_0002: newobj instance void Program/getSeq@5::.ctor(int32, int32) IL_0007: ret } // end of method Program::getSeq .method public static class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 getList () cil managed { // Method begins at RVA 0x2064 // Code size 12 (0xc) .maxstack 8 IL_0000: ldc.i4.1 IL_0001: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_Empty() IL_0006: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::Cons(!0, class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1) IL_000b: ret } // end of method Program::getList .method public static int32[] getArray () cil managed { // Method begins at RVA 0x2074 // Code size 15 (0xf) .maxstack 8 IL_0000: ldc.i4.1 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.any [mscorlib]System.Int32 IL_000e: ret } // end of method Program::getArray .method public static int32 main ( string[] argv ) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2084 // Code size 259 (0x103) .maxstack 6 .entrypoint .locals init ( [0] class [mscorlib]System.IDisposable, [1] class [mscorlib]System.IDisposable, [2] class [mscorlib]System.IDisposable, [3] class [mscorlib]System.IDisposable, [4] int32, [5] class [mscorlib]System.Collections.Generic.IEnumerable`1, [6] class [mscorlib]System.Collections.Generic.IEnumerator`1, [7] class [FSharp.Core]Microsoft.FSharp.Core.Unit, [8] class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [9] class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [10] int32, [11] int32[] ) IL_0000: call class [mscorlib]System.IDisposable Program::disposable() IL_0005: stloc.1 .try { IL_0006: ldstr "Hello 1" IL_000b: call void [mscorlib]System.Console::WriteLine(string) IL_0010: call class [mscorlib]System.IDisposable Program::disposable() IL_0015: stloc.2 IL_0016: leave.s IL_002e } // end .try finally { IL_0018: ldloc.1 IL_0019: isinst [mscorlib]System.IDisposable IL_001e: stloc.3 IL_001f: ldloc.3 IL_0020: brfalse.s IL_002b IL_0022: ldloc.3 IL_0023: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0028: ldnull IL_0029: pop IL_002a: endfinally IL_002b: ldnull IL_002c: pop IL_002d: endfinally } // end handler IL_002e: ldloc.2 IL_002f: stloc.0 .try { IL_0030: call class [mscorlib]System.Collections.Generic.IEnumerable`1 Program::getSeq() IL_0035: stloc.s 5 IL_0037: ldloc.s 5 IL_0039: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_003e: stloc.s 6 .try { // loop start (head: IL_0040) IL_0040: ldloc.s 6 IL_0042: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0047: brfalse.s IL_0058 IL_0049: ldloc.s 6 IL_004b: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_0050: call void [mscorlib]System.Console::WriteLine(int32) IL_0055: nop IL_0056: br.s IL_0040 // end loop IL_0058: ldnull IL_0059: stloc.s 7 IL_005b: leave.s IL_0074 } // end .try finally { IL_005d: ldloc.s 6 IL_005f: isinst [mscorlib]System.IDisposable IL_0064: stloc.1 IL_0065: ldloc.1 IL_0066: brfalse.s IL_0071 IL_0068: ldloc.1 IL_0069: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_006e: ldnull IL_006f: pop IL_0070: endfinally IL_0071: ldnull IL_0072: pop IL_0073: endfinally } // end handler IL_0074: ldloc.s 7 IL_0076: pop IL_0077: ldc.i4.1 IL_0078: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_Empty() IL_007d: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::Cons(!0, class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1) IL_0082: stloc.s 8 IL_0084: ldloc.s 8 IL_0086: call instance class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_TailOrNull() IL_008b: stloc.s 9 // loop start (head: IL_008d) IL_008d: ldloc.s 9 IL_008f: ldnull IL_0090: cgt.un IL_0092: brfalse.s IL_00b4 IL_0094: ldloc.s 8 IL_0096: call instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_HeadOrDefault() IL_009b: stloc.s 10 IL_009d: ldloc.s 10 IL_009f: call void [mscorlib]System.Console::WriteLine(int32) IL_00a4: ldloc.s 9 IL_00a6: stloc.s 8 IL_00a8: ldloc.s 8 IL_00aa: call instance class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1::get_TailOrNull() IL_00af: stloc.s 9 IL_00b1: nop IL_00b2: br.s IL_008d // end loop IL_00b4: ldc.i4.1 IL_00b5: newarr [mscorlib]System.Int32 IL_00ba: dup IL_00bb: ldc.i4.0 IL_00bc: ldc.i4.1 IL_00bd: stelem.any [mscorlib]System.Int32 IL_00c2: stloc.s 11 IL_00c4: ldc.i4.0 IL_00c5: stloc.s 10 IL_00c7: br.s IL_00dd // loop start (head: IL_00dd) IL_00c9: ldloc.s 11 IL_00cb: ldloc.s 10 IL_00cd: ldelem.any [mscorlib]System.Int32 IL_00d2: call void [mscorlib]System.Console::WriteLine(int32) IL_00d7: ldloc.s 10 IL_00d9: ldc.i4.1 IL_00da: add IL_00db: stloc.s 10 IL_00dd: ldloc.s 10 IL_00df: ldloc.s 11 IL_00e1: ldlen IL_00e2: conv.i4 IL_00e3: blt.s IL_00c9 // end loop IL_00e5: ldc.i4.0 IL_00e6: stloc.s 4 IL_00e8: leave.s IL_0100 } // end .try finally { IL_00ea: ldloc.0 IL_00eb: isinst [mscorlib]System.IDisposable IL_00f0: stloc.1 IL_00f1: ldloc.1 IL_00f2: brfalse.s IL_00fd IL_00f4: ldloc.1 IL_00f5: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_00fa: ldnull IL_00fb: pop IL_00fc: endfinally IL_00fd: ldnull IL_00fe: pop IL_00ff: endfinally } // end handler IL_0100: ldloc.s 4 IL_0102: ret } // end of method Program::main } // end of class Program .class private auto ansi abstract sealed '.$Program' extends [mscorlib]System.Object { } // end of class .$Program .class private auto ansi abstract sealed '.$AssemblyInfo' extends [mscorlib]System.Object { } // end of class .$AssemblyInfo .class private auto ansi abstract sealed '.$.NETFramework,Version=v4.6.1.AssemblyAttributes' extends [mscorlib]System.Object { } // end of class .$.NETFramework,Version=v4.6.1.AssemblyAttributes ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing.fs ================================================ module FSharpUsingPatterns open System open System.IO let sample1() = use fs = File.Create("x.txt") fs.WriteByte(byte 1) let sample2() = Console.WriteLine("some text") use fs = File.Create("x.txt") fs.WriteByte(byte 2) Console.WriteLine("some text") let sample3() = Console.WriteLine("some text") do use fs = File.Create("x.txt") fs.WriteByte(byte 3) Console.WriteLine("some text") let sample4() = Console.WriteLine("some text") let firstByte = use fs = File.OpenRead("x.txt") fs.ReadByte() Console.WriteLine("read:" + firstByte.ToString()) let sample5() = Console.WriteLine("some text") let firstByte = use fs = File.OpenRead("x.txt") fs.ReadByte() let secondByte = use fs = File.OpenRead("x.txt") fs.ReadByte() |> ignore fs.ReadByte() Console.WriteLine("read: {0}, {1}", firstByte, secondByte) ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.cs ================================================ using System; using System.IO; public static class FSharpUsingPatterns { public static void sample1() { using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte((byte)1); } } public static void sample2() { Console.WriteLine("some text"); using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte((byte)2); Console.WriteLine("some text"); } } public static void sample3() { Console.WriteLine("some text"); using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte((byte)3); } Console.WriteLine("some text"); } public static void sample4() { Console.WriteLine("some text"); int num; using (FileStream fileStream = File.OpenRead("x.txt")) { num = fileStream.ReadByte(); } int num2 = num; Console.WriteLine("read:" + num2); } public static void sample5() { Console.WriteLine("some text"); int num; using (FileStream fileStream = File.OpenRead("x.txt")) { num = fileStream.ReadByte(); } int num2 = num; int num3; using (FileStream fileStream = File.OpenRead("x.txt")) { fileStream.ReadByte(); num3 = fileStream.ReadByte(); } int num4 = num3; Console.WriteLine("read: {0}, {1}", num2, num4); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.il ================================================ .class public auto ansi abstract sealed FSharpUsingPatterns extends [mscorlib]System.Object { // Methods .method public static void sample1 () cil managed { // Method begins at RVA 0x2050 // Code size 53 (0x35) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "x.txt" IL_0006: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_000b: stloc.0 .try { IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: conv.u1 IL_000f: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_0014: ldnull IL_0015: stloc.1 IL_0016: leave.s IL_0032 } // end .try finally { IL_0018: ldloc.0 IL_0019: isinst [mscorlib]System.IDisposable IL_001e: stloc.2 IL_001f: ldloc.2 IL_0020: brfalse.s IL_0024 IL_0022: br.s IL_0026 IL_0024: br.s IL_002f IL_0026: ldloc.2 IL_0027: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002c: ldnull IL_002d: pop IL_002e: endfinally IL_002f: ldnull IL_0030: pop IL_0031: endfinally } // end handler IL_0032: ldloc.1 IL_0033: pop IL_0034: ret } // end of method FSharpUsingPatterns::sample1 .method public static void sample2 () cil managed { // Method begins at RVA 0x20a4 // Code size 73 (0x49) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: ldstr "x.txt" IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_0015: stloc.0 .try { IL_0016: ldloc.0 IL_0017: ldc.i4.2 IL_0018: conv.u1 IL_0019: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_001e: ldstr "some text" IL_0023: call void [mscorlib]System.Console::WriteLine(string) IL_0028: ldnull IL_0029: stloc.1 IL_002a: leave.s IL_0046 } // end .try finally { IL_002c: ldloc.0 IL_002d: isinst [mscorlib]System.IDisposable IL_0032: stloc.2 IL_0033: ldloc.2 IL_0034: brfalse.s IL_0038 IL_0036: br.s IL_003a IL_0038: br.s IL_0043 IL_003a: ldloc.2 IL_003b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0040: ldnull IL_0041: pop IL_0042: endfinally IL_0043: ldnull IL_0044: pop IL_0045: endfinally } // end handler IL_0046: ldloc.1 IL_0047: pop IL_0048: ret } // end of method FSharpUsingPatterns::sample2 .method public static void sample3 () cil managed { // Method begins at RVA 0x210c // Code size 73 (0x49) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: ldstr "x.txt" IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_0015: stloc.0 .try { IL_0016: ldloc.0 IL_0017: ldc.i4.3 IL_0018: conv.u1 IL_0019: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_001e: ldnull IL_001f: stloc.1 IL_0020: leave.s IL_003c } // end .try finally { IL_0022: ldloc.0 IL_0023: isinst [mscorlib]System.IDisposable IL_0028: stloc.2 IL_0029: ldloc.2 IL_002a: brfalse.s IL_002e IL_002c: br.s IL_0030 IL_002e: br.s IL_0039 IL_0030: ldloc.2 IL_0031: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0036: ldnull IL_0037: pop IL_0038: endfinally IL_0039: ldnull IL_003a: pop IL_003b: endfinally } // end handler IL_003c: ldloc.1 IL_003d: pop IL_003e: ldstr "some text" IL_0043: call void [mscorlib]System.Console::WriteLine(string) IL_0048: ret } // end of method FSharpUsingPatterns::sample3 .method public static void sample4 () cil managed { // Method begins at RVA 0x2174 // Code size 89 (0x59) .maxstack 4 .locals init ( [0] int32 firstByte, [1] class [mscorlib]System.IO.FileStream fs, [2] int32, [3] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldstr "x.txt" IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0016: stloc.1 .try { IL_0017: ldloc.1 IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_001d: stloc.2 IL_001e: leave.s IL_003a } // end .try finally { IL_0020: ldloc.1 IL_0021: isinst [mscorlib]System.IDisposable IL_0026: stloc.3 IL_0027: ldloc.3 IL_0028: brfalse.s IL_002c IL_002a: br.s IL_002e IL_002c: br.s IL_0037 IL_002e: ldloc.3 IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0034: ldnull IL_0035: pop IL_0036: endfinally IL_0037: ldnull IL_0038: pop IL_0039: endfinally } // end handler IL_003a: ldloc.2 IL_003b: stloc.0 IL_003c: ldstr "read:" IL_0041: ldloca.s firstByte IL_0043: constrained. [mscorlib]System.Int32 IL_0049: callvirt instance string [mscorlib]System.Object::ToString() IL_004e: call string [mscorlib]System.String::Concat(string, string) IL_0053: call void [mscorlib]System.Console::WriteLine(string) IL_0058: ret } // end of method FSharpUsingPatterns::sample4 .method public static void sample5 () cil managed { // Method begins at RVA 0x21ec // Code size 155 (0x9b) .maxstack 5 .locals init ( [0] int32 firstByte, [1] class [mscorlib]System.IO.FileStream fs, [2] int32, [3] class [mscorlib]System.IDisposable, [4] int32 secondByte, [5] class [mscorlib]System.IO.FileStream fs, [6] int32, [7] int32, [8] int32, [9] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldstr "x.txt" IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0016: stloc.1 .try { IL_0017: ldloc.1 IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_001d: stloc.2 IL_001e: leave.s IL_003a } // end .try finally { IL_0020: ldloc.1 IL_0021: isinst [mscorlib]System.IDisposable IL_0026: stloc.3 IL_0027: ldloc.3 IL_0028: brfalse.s IL_002c IL_002a: br.s IL_002e IL_002c: br.s IL_0037 IL_002e: ldloc.3 IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0034: ldnull IL_0035: pop IL_0036: endfinally IL_0037: ldnull IL_0038: pop IL_0039: endfinally } // end handler IL_003a: ldloc.2 IL_003b: stloc.0 IL_003c: nop IL_003d: ldstr "x.txt" IL_0042: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0047: stloc.s fs .try { IL_0049: ldloc.s fs IL_004b: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_0050: stloc.s 7 IL_0052: ldloc.s 7 IL_0054: stloc.s 8 IL_0056: ldloc.s fs IL_0058: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_005d: stloc.s 6 IL_005f: leave.s IL_007f } // end .try finally { IL_0061: ldloc.s fs IL_0063: isinst [mscorlib]System.IDisposable IL_0068: stloc.s 9 IL_006a: ldloc.s 9 IL_006c: brfalse.s IL_0070 IL_006e: br.s IL_0072 IL_0070: br.s IL_007c IL_0072: ldloc.s 9 IL_0074: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0079: ldnull IL_007a: pop IL_007b: endfinally IL_007c: ldnull IL_007d: pop IL_007e: endfinally } // end handler IL_007f: ldloc.s 6 IL_0081: stloc.s secondByte IL_0083: ldstr "read: {0}, {1}" IL_0088: ldloc.0 IL_0089: box [mscorlib]System.Int32 IL_008e: ldloc.s secondByte IL_0090: box [mscorlib]System.Int32 IL_0095: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_009a: ret } // end of method FSharpUsingPatterns::sample5 } // end of class FSharpUsingPatterns ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.cs ================================================ using System; using System.IO; public static class FSharpUsingPatterns { public static void sample1() { using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte(1); } } public static void sample2() { Console.WriteLine("some text"); using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte(2); Console.WriteLine("some text"); } } public static void sample3() { Console.WriteLine("some text"); using (FileStream fileStream = File.Create("x.txt")) { fileStream.WriteByte(3); } Console.WriteLine("some text"); } public static void sample4() { Console.WriteLine("some text"); int num; using (FileStream fileStream = File.OpenRead("x.txt")) { num = fileStream.ReadByte(); } int num2 = num; Console.WriteLine("read:" + num2); } public static void sample5() { Console.WriteLine("some text"); int num; using (FileStream fileStream = File.OpenRead("x.txt")) { num = fileStream.ReadByte(); } int num2 = num; int num3; using (FileStream fileStream = File.OpenRead("x.txt")) { fileStream.ReadByte(); num3 = fileStream.ReadByte(); } num = num3; Console.WriteLine("read: {0}, {1}", num2, num); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.il ================================================ .class public auto ansi abstract sealed FSharpUsingPatterns extends [mscorlib]System.Object { // Methods .method public static void sample1 () cil managed { // Method begins at RVA 0x2050 // Code size 48 (0x30) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "x.txt" IL_0006: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_000b: stloc.0 .try { IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_0013: ldnull IL_0014: stloc.1 IL_0015: leave.s IL_002d } // end .try finally { IL_0017: ldloc.0 IL_0018: isinst [mscorlib]System.IDisposable IL_001d: stloc.2 IL_001e: ldloc.2 IL_001f: brfalse.s IL_002a IL_0021: ldloc.2 IL_0022: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0027: ldnull IL_0028: pop IL_0029: endfinally IL_002a: ldnull IL_002b: pop IL_002c: endfinally } // end handler IL_002d: ldloc.1 IL_002e: pop IL_002f: ret } // end of method FSharpUsingPatterns::sample1 .method public static void sample2 () cil managed { // Method begins at RVA 0x209c // Code size 68 (0x44) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: ldstr "x.txt" IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_0015: stloc.0 .try { IL_0016: ldloc.0 IL_0017: ldc.i4.2 IL_0018: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_001d: ldstr "some text" IL_0022: call void [mscorlib]System.Console::WriteLine(string) IL_0027: ldnull IL_0028: stloc.1 IL_0029: leave.s IL_0041 } // end .try finally { IL_002b: ldloc.0 IL_002c: isinst [mscorlib]System.IDisposable IL_0031: stloc.2 IL_0032: ldloc.2 IL_0033: brfalse.s IL_003e IL_0035: ldloc.2 IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_003b: ldnull IL_003c: pop IL_003d: endfinally IL_003e: ldnull IL_003f: pop IL_0040: endfinally } // end handler IL_0041: ldloc.1 IL_0042: pop IL_0043: ret } // end of method FSharpUsingPatterns::sample2 .method public static void sample3 () cil managed { // Method begins at RVA 0x20fc // Code size 68 (0x44) .maxstack 4 .locals init ( [0] class [mscorlib]System.IO.FileStream fs, [1] class [mscorlib]System.Object, [2] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: ldstr "x.txt" IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) IL_0015: stloc.0 .try { IL_0016: ldloc.0 IL_0017: ldc.i4.3 IL_0018: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) IL_001d: ldnull IL_001e: stloc.1 IL_001f: leave.s IL_0037 } // end .try finally { IL_0021: ldloc.0 IL_0022: isinst [mscorlib]System.IDisposable IL_0027: stloc.2 IL_0028: ldloc.2 IL_0029: brfalse.s IL_0034 IL_002b: ldloc.2 IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0031: ldnull IL_0032: pop IL_0033: endfinally IL_0034: ldnull IL_0035: pop IL_0036: endfinally } // end handler IL_0037: ldloc.1 IL_0038: pop IL_0039: ldstr "some text" IL_003e: call void [mscorlib]System.Console::WriteLine(string) IL_0043: ret } // end of method FSharpUsingPatterns::sample3 .method public static void sample4 () cil managed { // Method begins at RVA 0x215c // Code size 85 (0x55) .maxstack 4 .locals init ( [0] int32 firstByte, [1] class [mscorlib]System.IO.FileStream fs, [2] int32, [3] class [mscorlib]System.IDisposable ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldstr "x.txt" IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0016: stloc.1 .try { IL_0017: ldloc.1 IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_001d: stloc.2 IL_001e: leave.s IL_0036 } // end .try finally { IL_0020: ldloc.1 IL_0021: isinst [mscorlib]System.IDisposable IL_0026: stloc.3 IL_0027: ldloc.3 IL_0028: brfalse.s IL_0033 IL_002a: ldloc.3 IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0030: ldnull IL_0031: pop IL_0032: endfinally IL_0033: ldnull IL_0034: pop IL_0035: endfinally } // end handler IL_0036: ldloc.2 IL_0037: stloc.0 IL_0038: ldstr "read:" IL_003d: ldloca.s firstByte IL_003f: constrained. [mscorlib]System.Int32 IL_0045: callvirt instance string [mscorlib]System.Object::ToString() IL_004a: call string [mscorlib]System.String::Concat(string, string) IL_004f: call void [mscorlib]System.Console::WriteLine(string) IL_0054: ret } // end of method FSharpUsingPatterns::sample4 .method public static void sample5 () cil managed { // Method begins at RVA 0x21d0 // Code size 134 (0x86) .maxstack 5 .locals init ( [0] int32 firstByte, [1] class [mscorlib]System.IO.FileStream fs, [2] int32 secondByte, [3] class [mscorlib]System.IDisposable, [4] int32, [5] int32 ) IL_0000: nop IL_0001: ldstr "some text" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldstr "x.txt" IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0016: stloc.1 .try { IL_0017: ldloc.1 IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_001d: stloc.2 IL_001e: leave.s IL_0036 } // end .try finally { IL_0020: ldloc.1 IL_0021: isinst [mscorlib]System.IDisposable IL_0026: stloc.3 IL_0027: ldloc.3 IL_0028: brfalse.s IL_0033 IL_002a: ldloc.3 IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0030: ldnull IL_0031: pop IL_0032: endfinally IL_0033: ldnull IL_0034: pop IL_0035: endfinally } // end handler IL_0036: ldloc.2 IL_0037: stloc.0 IL_0038: nop IL_0039: ldstr "x.txt" IL_003e: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) IL_0043: stloc.1 .try { IL_0044: ldloc.1 IL_0045: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_004a: stloc.s 5 IL_004c: ldloc.1 IL_004d: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() IL_0052: stloc.s 4 IL_0054: leave.s IL_006c } // end .try finally { IL_0056: ldloc.1 IL_0057: isinst [mscorlib]System.IDisposable IL_005c: stloc.3 IL_005d: ldloc.3 IL_005e: brfalse.s IL_0069 IL_0060: ldloc.3 IL_0061: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0066: ldnull IL_0067: pop IL_0068: endfinally IL_0069: ldnull IL_006a: pop IL_006b: endfinally } // end handler IL_006c: ldloc.s 4 IL_006e: stloc.2 IL_006f: ldstr "read: {0}, {1}" IL_0074: ldloc.0 IL_0075: box [mscorlib]System.Int32 IL_007a: ldloc.2 IL_007b: box [mscorlib]System.Int32 IL_0080: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0085: ret } // end of method FSharpUsingPatterns::sample5 } // end of class FSharpUsingPatterns ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.cs ================================================ // ClassLibrary1.UnknownClassTest using System; using System.Collections.Generic; using UnknownNamespace; namespace ClassLibrary1 { public class UnknownClassTest : EventArgs { public void MethodUnknownClass() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown UnknownClass val = new UnknownClass(); int? unknownProperty = val.UnknownProperty; int? num = (val.UnknownProperty = unknownProperty.GetValueOrDefault()); int? num3 = num; List list = new List { val[unknownProperty.Value] ?? "", val.NotProperty, val.get_NotPropertyWithGeneric(42), val[42], val.get_NotPropertyWithParameterAndGeneric(int.MinValue), val.get_PropertyCalledGet, val.set_HasReturnType(), val.set_HasReturnType("") }; val.get_NoReturnType(); val.set_NoValue(); val.OnEvent += Instance_OnEvent; val.OnEvent -= Instance_OnEvent; string text = val[(long?)null]; val[(long?)long.MaxValue] = text; IntPtr intPtr = val[UIntPtr.Zero, "Hello"]; val[(UIntPtr)32uL, "World"] = intPtr; } public void MethodUnknownGenericClass() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_00cd: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00e1: Expected O, but got Unknown UnknownGenericClass val = new UnknownGenericClass(); UnknownEventArgs e = (val.UnknownProperty = val.UnknownProperty); List list = new List { val[((object)e).GetHashCode()] ?? "", val.NotProperty, val.get_NotPropertyWithGeneric(42), val[42], val.get_NotPropertyWithParameterAndGeneric(int.MinValue), val.get_PropertyCalledGet }; val.OnEvent += Instance_OnEvent; val.OnEvent -= Instance_OnEvent; UnknownEventArgs e2 = val[(UnknownEventArgs)null]; val[new UnknownEventArgs()] = e2; UnknownEventArgs e3 = val[new UnknownEventArgs(), new UnknownEventArgs()]; val[new UnknownEventArgs(), new UnknownEventArgs()] = e3; } public void MethodUnknownStatic() { int? num = (UnknownStaticClass.UnknownProperty = UnknownStaticClass.UnknownProperty); List list = new List { UnknownStaticClass[num.Value] ?? "", UnknownStaticClass.NotProperty, UnknownStaticClass.get_NotPropertyWithGeneric(42), UnknownStaticClass[42], UnknownStaticClass.get_NotPropertyWithParameterAndGeneric(int.MinValue), UnknownStaticClass.get_PropertyCalledGet }; UnknownStaticClass.OnEvent += Instance_OnEvent; UnknownStaticClass.OnEvent -= Instance_OnEvent; } public void MethodUnknownStaticGeneric() { string text = (UnknownStaticGenericClass.UnknownProperty = UnknownStaticGenericClass.UnknownProperty); List list = new List { UnknownStaticGenericClass[text.Length] ?? "", UnknownStaticGenericClass.NotProperty, UnknownStaticGenericClass.get_NotPropertyWithGeneric(42), UnknownStaticGenericClass[42], UnknownStaticGenericClass.get_NotPropertyWithParameterAndGeneric(int.MinValue), UnknownStaticGenericClass.get_PropertyCalledGet }; UnknownStaticGenericClass.OnEvent += Instance_OnEvent; UnknownStaticGenericClass.OnEvent -= Instance_OnEvent; } public void MethodUnknownIndexerInitializer() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) new UnknownClass { ["a"] = 1, ["b"] = 2 }; } private void Instance_OnEvent(object sender, EventArgs e) { throw new NotImplementedException(); } private void Instance_OnEvent(object sender, UnknownEventArgs e) { throw new NotImplementedException(); } private void Instance_OnEvent(object sender, string e) { throw new NotImplementedException(); } private static void Instance_OnEvent(object sender, object e) { throw new NotImplementedException(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/GuessAccessors.il ================================================ .class /* 33554434 */ public auto ansi beforefieldinit ClassLibrary1.UnknownClassTest extends [netstandard]System.EventArgs { // Methods .method /* 100663297 */ public hidebysig instance void MethodUnknownClass () cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 325 (0x145) .maxstack 4 .locals /* 285212673 */ init ( [0] class [UnknownAssembly]UnknownNamespace.UnknownClass 'instance', [1] valuetype [netstandard]System.Nullable`1 valueOfProp, [2] valuetype [netstandard]System.Nullable`1 x, [3] class [netstandard]System.Collections.Generic.List`1 list, [4] string valueOfIndexer, [5] native int valueOfMultiIndexer, [6] valuetype [netstandard]System.Nullable`1, [7] valuetype [netstandard]System.Nullable`1 ) IL_0000: nop IL_0001: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownClass::.ctor() /* 167772171 */ IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: callvirt instance valuetype [netstandard]System.Nullable`1 [UnknownAssembly]UnknownNamespace.UnknownClass::get_UnknownProperty() /* 167772172 */ IL_000d: stloc.1 IL_000e: ldloc.0 IL_000f: ldloca.s 6 IL_0011: ldloca.s 1 IL_0013: call instance !0 valuetype [netstandard]System.Nullable`1::GetValueOrDefault() /* 167772173 */ IL_0018: call instance void valuetype [netstandard]System.Nullable`1::.ctor(!0) /* 167772174 */ IL_001d: ldloc.s 6 IL_001f: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_UnknownProperty(valuetype [netstandard]System.Nullable`1) /* 167772175 */ IL_0024: nop IL_0025: ldloc.s 6 IL_0027: stloc.2 IL_0028: newobj instance void class [netstandard]System.Collections.Generic.List`1::.ctor() /* 167772176 */ IL_002d: dup IL_002e: ldloc.0 IL_002f: ldloca.s 1 IL_0031: call instance !0 valuetype [netstandard]System.Nullable`1::get_Value() /* 167772177 */ IL_0036: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_NotPropertyWithParameter(int32) /* 167772178 */ IL_003b: dup IL_003c: brtrue.s IL_0044 IL_003e: pop IL_003f: ldstr "" /* 1879048193 */ IL_0044: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0049: nop IL_004a: dup IL_004b: ldloc.0 IL_004c: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_NotProperty() /* 167772180 */ IL_0051: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0056: nop IL_0057: dup IL_0058: ldloc.0 IL_0059: ldc.i4.s 42 IL_005b: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_NotPropertyWithGeneric(int32) /* 721420289 */ IL_0060: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0065: nop IL_0066: dup IL_0067: ldloc.0 IL_0068: ldc.i4.s 42 IL_006a: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_NotPropertyWithParameter(int32) /* 167772178 */ IL_006f: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0074: nop IL_0075: dup IL_0076: ldloc.0 IL_0077: ldc.i4 -2147483648 IL_007c: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_NotPropertyWithParameterAndGeneric(int32) /* 721420290 */ IL_0081: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0086: nop IL_0087: dup IL_0088: ldloc.0 IL_0089: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_get_PropertyCalledGet() /* 167772183 */ IL_008e: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0093: nop IL_0094: dup IL_0095: ldloc.0 IL_0096: callvirt instance int32 [UnknownAssembly]UnknownNamespace.UnknownClass::set_HasReturnType() /* 167772184 */ IL_009b: box [netstandard]System.Int32 /* 16777235 */ IL_00a0: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_00a5: nop IL_00a6: dup IL_00a7: ldloc.0 IL_00a8: ldstr "" /* 1879048193 */ IL_00ad: callvirt instance int32 [UnknownAssembly]UnknownNamespace.UnknownClass::set_HasReturnType(string) /* 167772185 */ IL_00b2: box [netstandard]System.Int32 /* 16777235 */ IL_00b7: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_00bc: nop IL_00bd: stloc.3 IL_00be: ldloc.0 IL_00bf: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::get_NoReturnType() /* 167772186 */ IL_00c4: nop IL_00c5: ldloc.0 IL_00c6: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_NoValue() /* 167772187 */ IL_00cb: nop IL_00cc: ldloc.0 IL_00cd: ldarg.0 IL_00ce: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [netstandard]System.EventArgs) /* 100663301 */ IL_00d4: newobj instance void [netstandard]System.EventHandler::.ctor(object, native int) /* 167772188 */ IL_00d9: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::add_OnEvent(class [netstandard]System.EventHandler) /* 167772189 */ IL_00de: nop IL_00df: ldloc.0 IL_00e0: ldarg.0 IL_00e1: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [netstandard]System.EventArgs) /* 100663301 */ IL_00e7: newobj instance void [netstandard]System.EventHandler::.ctor(object, native int) /* 167772188 */ IL_00ec: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::remove_OnEvent(class [netstandard]System.EventHandler) /* 167772190 */ IL_00f1: nop IL_00f2: ldloc.0 IL_00f3: ldloca.s 7 IL_00f5: initobj valuetype [netstandard]System.Nullable`1 /* 452984835 */ IL_00fb: ldloc.s 7 IL_00fd: callvirt instance string [UnknownAssembly]UnknownNamespace.UnknownClass::get_Item(valuetype [netstandard]System.Nullable`1) /* 167772191 */ IL_0102: stloc.s 4 IL_0104: ldloc.0 IL_0105: ldc.i8 9223372036854775807 IL_010e: newobj instance void valuetype [netstandard]System.Nullable`1::.ctor(!0) /* 167772192 */ IL_0113: ldloc.s 4 IL_0115: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_Item(valuetype [netstandard]System.Nullable`1, string) /* 167772193 */ IL_011a: nop IL_011b: ldloc.0 IL_011c: ldsfld native uint [netstandard]System.UIntPtr::Zero /* 167772194 */ IL_0121: ldstr "Hello" /* 1879048195 */ IL_0126: callvirt instance native int& [UnknownAssembly]UnknownNamespace.UnknownClass::get_Item(native uint, string) /* 167772195 */ IL_012b: ldind.i IL_012c: stloc.s 5 IL_012e: ldloc.0 IL_012f: ldc.i4.s 32 IL_0131: conv.i8 IL_0132: call native uint [netstandard]System.UIntPtr::op_Explicit(uint64) /* 167772196 */ IL_0137: ldstr "World" /* 1879048207 */ IL_013c: callvirt instance native int& [UnknownAssembly]UnknownNamespace.UnknownClass::get_Item(native uint, string) /* 167772195 */ IL_0141: ldloc.s 5 IL_0143: stind.i IL_0144: ret } // end of method UnknownClassTest::MethodUnknownClass .method /* 100663298 */ public hidebysig instance void MethodUnknownGenericClass () cil managed { // Method begins at RVA 0x21a4 // Header size: 12 // Code size: 227 (0xe3) .maxstack 4 .locals /* 285212674 */ init ( [0] class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1 'instance', [1] class [UnknownAssembly]UnknownNamespace.UnknownEventArgs valueOfProp, [2] class [netstandard]System.Collections.Generic.List`1 list, [3] class [UnknownAssembly]UnknownNamespace.UnknownEventArgs valueOfIndexer, [4] class [UnknownAssembly]UnknownNamespace.UnknownEventArgs valueOfMultiIndexer ) IL_0000: nop IL_0001: newobj instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::.ctor() /* 167772197 */ IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: callvirt instance !0 class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_UnknownProperty() /* 167772198 */ IL_000d: stloc.1 IL_000e: ldloc.0 IL_000f: ldloc.1 IL_0010: callvirt instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::set_UnknownProperty(!0) /* 167772199 */ IL_0015: nop IL_0016: newobj instance void class [netstandard]System.Collections.Generic.List`1::.ctor() /* 167772176 */ IL_001b: dup IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: callvirt instance int32 [netstandard]System.Object::GetHashCode() /* 167772200 */ IL_0023: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_NotPropertyWithParameter(int32) /* 167772201 */ IL_0028: dup IL_0029: brtrue.s IL_0031 IL_002b: pop IL_002c: ldstr "" /* 1879048193 */ IL_0031: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0036: nop IL_0037: dup IL_0038: ldloc.0 IL_0039: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_NotProperty() /* 167772202 */ IL_003e: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0043: nop IL_0044: dup IL_0045: ldloc.0 IL_0046: ldc.i4.s 42 IL_0048: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_NotPropertyWithGeneric(int32) /* 721420291 */ IL_004d: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0052: nop IL_0053: dup IL_0054: ldloc.0 IL_0055: ldc.i4.s 42 IL_0057: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_NotPropertyWithParameter(int32) /* 167772201 */ IL_005c: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0061: nop IL_0062: dup IL_0063: ldloc.0 IL_0064: ldc.i4 -2147483648 IL_0069: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_NotPropertyWithParameterAndGeneric(int32) /* 721420292 */ IL_006e: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0073: nop IL_0074: dup IL_0075: ldloc.0 IL_0076: callvirt instance string class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_get_PropertyCalledGet() /* 167772205 */ IL_007b: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0080: nop IL_0081: stloc.2 IL_0082: ldloc.0 IL_0083: ldarg.0 IL_0084: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [UnknownAssembly]UnknownNamespace.UnknownEventArgs) /* 100663302 */ IL_008a: newobj instance void class [netstandard]System.EventHandler`1::.ctor(object, native int) /* 167772206 */ IL_008f: callvirt instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::add_OnEvent(class [netstandard]System.EventHandler`1) /* 167772207 */ IL_0094: nop IL_0095: ldloc.0 IL_0096: ldarg.0 IL_0097: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [UnknownAssembly]UnknownNamespace.UnknownEventArgs) /* 100663302 */ IL_009d: newobj instance void class [netstandard]System.EventHandler`1::.ctor(object, native int) /* 167772206 */ IL_00a2: callvirt instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::remove_OnEvent(class [netstandard]System.EventHandler`1) /* 167772208 */ IL_00a7: nop IL_00a8: ldloc.0 IL_00a9: ldnull IL_00aa: callvirt instance !0 class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_Item(!0) /* 167772209 */ IL_00af: stloc.3 IL_00b0: ldloc.0 IL_00b1: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownEventArgs::.ctor() /* 167772210 */ IL_00b6: ldloc.3 IL_00b7: callvirt instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::set_Item(!0, !0) /* 167772211 */ IL_00bc: nop IL_00bd: ldloc.0 IL_00be: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownEventArgs::.ctor() /* 167772210 */ IL_00c3: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownEventArgs::.ctor() /* 167772210 */ IL_00c8: callvirt instance !0 class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::get_Item(!0, !0) /* 167772212 */ IL_00cd: stloc.s 4 IL_00cf: ldloc.0 IL_00d0: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownEventArgs::.ctor() /* 167772210 */ IL_00d5: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownEventArgs::.ctor() /* 167772210 */ IL_00da: ldloc.s 4 IL_00dc: callvirt instance void class [UnknownAssembly]UnknownNamespace.UnknownGenericClass`1::set_Item(!0, !0, !0) /* 167772213 */ IL_00e1: nop IL_00e2: ret } // end of method UnknownClassTest::MethodUnknownGenericClass .method /* 100663299 */ public hidebysig instance void MethodUnknownStatic () cil managed { // Method begins at RVA 0x2294 // Header size: 12 // Code size: 154 (0x9a) .maxstack 4 .locals /* 285212675 */ init ( [0] valuetype [netstandard]System.Nullable`1 valueOfProp, [1] class [netstandard]System.Collections.Generic.List`1 list ) IL_0000: nop IL_0001: call valuetype [netstandard]System.Nullable`1 [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_UnknownProperty() /* 167772214 */ IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call void [UnknownAssembly]UnknownNamespace.UnknownStaticClass::set_UnknownProperty(valuetype [netstandard]System.Nullable`1) /* 167772215 */ IL_000d: nop IL_000e: newobj instance void class [netstandard]System.Collections.Generic.List`1::.ctor() /* 167772176 */ IL_0013: dup IL_0014: ldloca.s 0 IL_0016: call instance !0 valuetype [netstandard]System.Nullable`1::get_Value() /* 167772177 */ IL_001b: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_NotPropertyWithParameter(int32) /* 167772216 */ IL_0020: dup IL_0021: brtrue.s IL_0029 IL_0023: pop IL_0024: ldstr "" /* 1879048193 */ IL_0029: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_002e: nop IL_002f: dup IL_0030: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_NotProperty() /* 167772217 */ IL_0035: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_003a: nop IL_003b: dup IL_003c: ldc.i4.s 42 IL_003e: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_NotPropertyWithGeneric(int32) /* 721420293 */ IL_0043: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0048: nop IL_0049: dup IL_004a: ldc.i4.s 42 IL_004c: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_NotPropertyWithParameter(int32) /* 167772216 */ IL_0051: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0056: nop IL_0057: dup IL_0058: ldc.i4 -2147483648 IL_005d: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_NotPropertyWithParameterAndGeneric(int32) /* 721420294 */ IL_0062: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0067: nop IL_0068: dup IL_0069: call string [UnknownAssembly]UnknownNamespace.UnknownStaticClass::get_get_PropertyCalledGet() /* 167772220 */ IL_006e: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0073: nop IL_0074: stloc.1 IL_0075: ldarg.0 IL_0076: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [netstandard]System.EventArgs) /* 100663301 */ IL_007c: newobj instance void [netstandard]System.EventHandler::.ctor(object, native int) /* 167772188 */ IL_0081: call void [UnknownAssembly]UnknownNamespace.UnknownStaticClass::add_OnEvent(class [netstandard]System.EventHandler) /* 167772221 */ IL_0086: nop IL_0087: ldarg.0 IL_0088: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, class [netstandard]System.EventArgs) /* 100663301 */ IL_008e: newobj instance void [netstandard]System.EventHandler::.ctor(object, native int) /* 167772188 */ IL_0093: call void [UnknownAssembly]UnknownNamespace.UnknownStaticClass::remove_OnEvent(class [netstandard]System.EventHandler) /* 167772222 */ IL_0098: nop IL_0099: ret } // end of method UnknownClassTest::MethodUnknownStatic .method /* 100663300 */ public hidebysig instance void MethodUnknownStaticGeneric () cil managed { // Method begins at RVA 0x233c // Header size: 12 // Code size: 153 (0x99) .maxstack 4 .locals /* 285212676 */ init ( [0] string valueOfProp, [1] class [netstandard]System.Collections.Generic.List`1 list ) IL_0000: nop IL_0001: call !0 class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_UnknownProperty() /* 167772223 */ IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call void class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::set_UnknownProperty(!0) /* 167772224 */ IL_000d: nop IL_000e: newobj instance void class [netstandard]System.Collections.Generic.List`1::.ctor() /* 167772176 */ IL_0013: dup IL_0014: ldloc.0 IL_0015: callvirt instance int32 [netstandard]System.String::get_Length() /* 167772225 */ IL_001a: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_NotPropertyWithParameter(int32) /* 167772226 */ IL_001f: dup IL_0020: brtrue.s IL_0028 IL_0022: pop IL_0023: ldstr "" /* 1879048193 */ IL_0028: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_002d: nop IL_002e: dup IL_002f: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_NotProperty() /* 167772227 */ IL_0034: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0039: nop IL_003a: dup IL_003b: ldc.i4.s 42 IL_003d: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_NotPropertyWithGeneric(int32) /* 721420295 */ IL_0042: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0047: nop IL_0048: dup IL_0049: ldc.i4.s 42 IL_004b: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_NotPropertyWithParameter(int32) /* 167772226 */ IL_0050: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0055: nop IL_0056: dup IL_0057: ldc.i4 -2147483648 IL_005c: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_NotPropertyWithParameterAndGeneric(int32) /* 721420296 */ IL_0061: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0066: nop IL_0067: dup IL_0068: call string class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::get_get_PropertyCalledGet() /* 167772230 */ IL_006d: callvirt instance void class [netstandard]System.Collections.Generic.List`1::Add(!0) /* 167772179 */ IL_0072: nop IL_0073: stloc.1 IL_0074: ldarg.0 IL_0075: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, string) /* 100663303 */ IL_007b: newobj instance void class [netstandard]System.EventHandler`1::.ctor(object, native int) /* 167772231 */ IL_0080: call void class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::add_OnEvent(class [netstandard]System.EventHandler`1) /* 167772232 */ IL_0085: nop IL_0086: ldarg.0 IL_0087: ldftn instance void ClassLibrary1.UnknownClassTest::Instance_OnEvent(object, string) /* 100663303 */ IL_008d: newobj instance void class [netstandard]System.EventHandler`1::.ctor(object, native int) /* 167772231 */ IL_0092: call void class [UnknownAssembly]UnknownNamespace.UnknownStaticGenericClass`1::remove_OnEvent(class [netstandard]System.EventHandler`1) /* 167772233 */ IL_0097: nop IL_0098: ret } // end of method UnknownClassTest::MethodUnknownStaticGeneric .method public hidebysig instance void MethodUnknownIndexerInitializer () cil managed { // Method begins at RVA 0x2050 // Code size 32 (0x20) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [UnknownAssembly]UnknownNamespace.UnknownClass::.ctor() IL_0006: dup IL_0007: ldstr "a" IL_000c: ldc.i4.1 IL_000d: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_Item(string, int32) IL_0012: nop IL_0013: ldstr "b" IL_0018: ldc.i4.2 IL_0019: callvirt instance void [UnknownAssembly]UnknownNamespace.UnknownClass::set_Item(string, int32) IL_001e: nop IL_001f: ret } // end of method C::MethodUnknownIndexerInitializer .method /* 100663301 */ private hidebysig instance void Instance_OnEvent ( object sender, class [netstandard]System.EventArgs e ) cil managed { // Method begins at RVA 0x23e1 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [netstandard]System.NotImplementedException::.ctor() /* 167772234 */ IL_0006: throw } // end of method UnknownClassTest::Instance_OnEvent .method /* 100663302 */ private hidebysig instance void Instance_OnEvent ( object sender, class [UnknownAssembly]UnknownNamespace.UnknownEventArgs e ) cil managed { // Method begins at RVA 0x23e9 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [netstandard]System.NotImplementedException::.ctor() /* 167772234 */ IL_0006: throw } // end of method UnknownClassTest::Instance_OnEvent .method /* 100663303 */ private hidebysig instance void Instance_OnEvent ( object sender, string e ) cil managed { // Method begins at RVA 0x23f1 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [netstandard]System.NotImplementedException::.ctor() /* 167772234 */ IL_0006: throw } // end of method UnknownClassTest::Instance_OnEvent .method /* 100663304 */ private hidebysig static void Instance_OnEvent ( object sender, object e ) cil managed { // Method begins at RVA 0x23f9 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [netstandard]System.NotImplementedException::.ctor() /* 167772234 */ IL_0006: throw } // end of method UnknownClassTest::Instance_OnEvent .method /* 100663305 */ public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2401 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [netstandard]System.EventArgs::.ctor() /* 167772235 */ IL_0006: nop IL_0007: ret } // end of method UnknownClassTest::.ctor } // end of class ClassLibrary1.UnknownClassTest ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public class Issue1038 where TR : class, new() { public event Action TestEvent = delegate { }; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System.Core { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2 { // Fields .field private class [System.Core]System.Action`2 TestEvent .field private static class [System.Core]System.Action`2 '<>f__am$cache0' .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 ldarg.0 ldsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' brtrue.s IL_0019 ldnull ldftn void class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'m__0'(!0, !1) newobj instance void class [System.Core]System.Action`2::.ctor(object, native int) stsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' IL_0019: ldsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' stfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent ret } .method public hidebysig specialname instance void add_TestEvent (class [System.Core]System.Action`2 'value') cil managed { .maxstack 3 .locals init ( [0] class [System.Core]System.Action`2, [1] class [System.Core]System.Action`2 ) ldarg.0 ldfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent stloc.0 IL_0007: ldloc.0 stloc.1 ldarg.0 ldflda class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent ldloc.1 ldarg.1 call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass class [System.Core]System.Action`2 ldloc.0 call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange>(!!0&, !!0, !!0) stloc.0 ldloc.0 ldloc.1 bne.un IL_0007 ret } .method public hidebysig specialname instance void remove_TestEvent (class [System.Core]System.Action`2 'value') cil managed { .maxstack 3 .locals init ( [0] class [System.Core]System.Action`2, [1] class [System.Core]System.Action`2 ) ldarg.0 ldfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent stloc.0 IL_0007: ldloc.0 stloc.1 ldarg.0 ldflda class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent ldloc.1 ldarg.1 call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass class [System.Core]System.Action`2 ldloc.0 call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange>(!!0&, !!0, !!0) stloc.0 ldloc.0 ldloc.1 bne.un IL_0007 ret } .method private hidebysig static void 'm__0' (!TK '', !TR '') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 ret } // Events .event class [System.Core]System.Action`2 TestEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::add_TestEvent(class [System.Core]System.Action`2) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::remove_TestEvent(class [System.Core]System.Action`2) } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public class Issue1047 { private static bool dummy; private void ProblemMethod() { while (!dummy) { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System.Core { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1047 { .field private static bool dummy; .method private hidebysig instance void ProblemMethod() cil managed { IL_0000: ldfld bool ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1047::dummy; brfalse L_tgt1 br L_exit L_tgt1: br IL_0000 br IL_0000 L_exit: ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1145.cs ================================================ using System; public sealed class EvType : MulticastDelegate { } [Serializable] public class OwningClass { public event EvType EvName; } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1145.il ================================================ .class public auto ansi sealed EvType extends [mscorlib]System.MulticastDelegate { // Methods not included. Just the run of the mill ctor + {Begin|End}?Invoke } .class public auto ansi serializable beforefieldinit OwningClass { .field class EvType EvName .event EvType EvName { .addon instance void OwningClass::add_EvName(class EvType) .removeon instance void OwningClass::remove_EvName(class EvType) } .method public hidebysig specialname instance void add_EvName ( class EvType 'value' ) cil managed { .maxstack 3 .locals init ( [0] class EvType, [1] class EvType ) IL_0000: ldarg.0 IL_0001: ldfld class EvType OwningClass::EvName IL_0006: stloc.0 // loop start (head: IL_0007) IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldarg.0 IL_000a: ldflda class EvType OwningClass::EvName IL_000f: ldloc.1 IL_0010: ldarg.1 IL_0011: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0016: castclass EvType IL_001b: ldloc.0 IL_001c: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0021: stloc.0 IL_0022: ldloc.0 IL_0023: ldloc.1 IL_0024: bne.un IL_0007 // end loop IL_0029: ret } // end of method OwningClass::add_EvName .method public hidebysig specialname instance void remove_EvName ( class EvType 'value' ) cil managed { .maxstack 3 .locals init ( [0] class EvType, [1] class EvType ) IL_0000: ldarg.0 IL_0001: ldfld class EvType OwningClass::EvName IL_0006: stloc.0 // loop start (head: IL_0007) IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldarg.0 IL_000a: ldflda class EvType OwningClass::EvName IL_000f: ldloc.1 IL_0010: ldarg.1 IL_0011: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0016: castclass EvType IL_001b: ldloc.0 IL_001c: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0021: stloc.0 IL_0022: ldloc.0 IL_0023: ldloc.1 IL_0024: bne.un IL_0007 // end loop IL_0029: ret } // end of method OwningClass::remove_EvName } // end of class OwningClass ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1157.cs ================================================ using System; namespace Issue1157 { internal abstract class BaseClass { public abstract event EventHandler IDontKnowMeHereOut; } internal class OtherClass : BaseClass { public override event EventHandler IDontKnowMeHereOut; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1157.il ================================================ .assembly Issue1157 { .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module Issue1157.exe // MVID: {4C6C7F98-AEB2-4A19-BE6F-43171E6113F1} .corflags 0x00020003 // ILOnly, Required32Bit, Preferred32Bit .class private auto ansi abstract beforefieldinit Issue1157.BaseClass extends [mscorlib]System.Object { // Methods .method public hidebysig specialname newslot abstract virtual instance void add_IDontKnowMeHereOut ( class [mscorlib]System.EventHandler 'value' ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) } // end of method BaseClass::add_IDontKnowMeHereOut .method public hidebysig specialname newslot abstract virtual instance void remove_IDontKnowMeHereOut ( class [mscorlib]System.EventHandler 'value' ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) } // end of method BaseClass::remove_IDontKnowMeHereOut .method family hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2063 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method BaseClass::.ctor // Events .event [mscorlib]System.EventHandler IDontKnowMeHereOut { .addon instance void Issue1157.BaseClass::add_IDontKnowMeHereOut(class [mscorlib]System.EventHandler) .removeon instance void Issue1157.BaseClass::remove_IDontKnowMeHereOut(class [mscorlib]System.EventHandler) } } // end of class Issue1157.BaseClass .class private auto ansi beforefieldinit Issue1157.OtherClass extends Issue1157.BaseClass { // Fields .field private class [mscorlib]System.EventHandler IDontKnowMeHereOut .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) // Methods .method public hidebysig specialname virtual instance void add_IDontKnowMeHereOut ( class [mscorlib]System.EventHandler 'value' ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x206c // Code size 41 (0x29) .maxstack 3 .locals init ( [0] class [mscorlib]System.EventHandler, [1] class [mscorlib]System.EventHandler, [2] class [mscorlib]System.EventHandler ) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler Issue1157.OtherClass::IDontKnowMeHereOut IL_0006: stloc.0 // loop start (head: IL_0007) IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler Issue1157.OtherClass::IDontKnowMeHereOut IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 // end loop IL_0028: ret } // end of method OtherClass::add_IDontKnowMeHereOut .method public hidebysig specialname virtual instance void remove_IDontKnowMeHereOut ( class [mscorlib]System.EventHandler 'value' ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x20a4 // Code size 41 (0x29) .maxstack 3 .locals init ( [0] class [mscorlib]System.EventHandler, [1] class [mscorlib]System.EventHandler, [2] class [mscorlib]System.EventHandler ) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler Issue1157.OtherClass::IDontKnowMeHereOut IL_0006: stloc.0 // loop start (head: IL_0007) IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler Issue1157.OtherClass::IDontKnowMeHereOut IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 // end loop IL_0028: ret } // end of method OtherClass::remove_IDontKnowMeHereOut .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20d9 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void Issue1157.BaseClass::.ctor() IL_0006: nop IL_0007: ret } // end of method OtherClass::.ctor // Events .event [mscorlib]System.EventHandler IDontKnowMeHereOut { .addon instance void Issue1157.OtherClass::add_IDontKnowMeHereOut(class [mscorlib]System.EventHandler) .removeon instance void Issue1157.OtherClass::remove_IDontKnowMeHereOut(class [mscorlib]System.EventHandler) } } // end of class Issue1157.OtherClass ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1256.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue1256 { public void Method(Enum e, object o, string s) { int num = (int)(object)e; object obj = new object(); int num2 = (int)obj; long num3 = (long)o; int num4 = (int)(object)s; int num5 = (int)num3; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1256.il ================================================ .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1256 extends [mscorlib]System.Object { // Methods .method public hidebysig instance void Method ( class [mscorlib]System.Enum e, object o, string s ) cil managed { // Method begins at RVA 0x2050 // Code size 41 (0x29) .maxstack 1 .locals init ( [0] int32, [1] object, [2] int32, [3] int64, [4] int32, [5] int32 ) IL_0000: nop IL_0001: ldarg.1 IL_0002: unbox.any [mscorlib]System.Int32 IL_0007: stloc.0 IL_0008: newobj instance void [mscorlib]System.Object::.ctor() IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: unbox.any [mscorlib]System.Int32 IL_0014: stloc.2 IL_0015: ldarg.2 IL_0016: unbox.any [mscorlib]System.Int64 IL_001b: stloc.3 IL_001c: ldarg.3 IL_001d: unbox.any [mscorlib]System.Int32 IL_0022: stloc.s 4 IL_0024: ldloc.3 IL_0025: conv.i4 IL_0026: stloc.s 5 IL_0028: ret } // end of method Issue1256::Method .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2085 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Issue1256::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1256 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1323.cs ================================================ public enum Enum0 { // error: enumerator has no value const_0, // error: enumerator has no value const_1, // error: enumerator has no value const_2, // error: enumerator has no value const_3 } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1323.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System.Core { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue1323 { .ver 1:0:0:0 } .module Issue1323.dll // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class public auto ansi sealed Enum0 extends [mscorlib]System.Enum { // Fields .field public specialname rtspecialname int32 value__ .field public static literal valuetype Enum0 const_0 .field public static literal valuetype Enum0 const_1 .field public static literal valuetype Enum0 const_2 .field public static literal valuetype Enum0 const_3 } // end of class Enum0 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs ================================================ using System; using System.ComponentModel; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.VisualBasic; using Microsoft.VisualBasic.CompilerServices; [assembly: Embedded] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] #pragma warning disable format namespace Issue1325 { [StandardModule] internal sealed class Program { [STAThread] public static void Main(string[] args) { } public static void TestCode(Test t, int i) { string text = ""; text += File.ReadAllText("Test.txt"); text += "asdf"; t.set_Parameterized(i, text); t.Unparameterized = text + "asdf"; } } internal class Test { public string Parameterized { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public string Unparameterized { get; set; } } } namespace Microsoft.VisualBasic { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class, Inherited = false)] [CompilerGenerated] [EditorBrowsable(EditorBrowsableState.Never)] [Embedded] internal sealed class Embedded : Attribute { } } namespace Microsoft.VisualBasic.CompilerServices { [EditorBrowsable(EditorBrowsableState.Never)] [AttributeUsage(AttributeTargets.Class, Inherited = false)] [CompilerGenerated] [Embedded] internal sealed class StandardModuleAttribute : Attribute { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:2:1:0 } .assembly extern System.Diagnostics.Debug { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:1:1:0 } .assembly extern System.IO.FileSystem { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:1:1:0 } .assembly Issue1325 { .custom instance void Microsoft.VisualBasic.Embedded::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V 65 72 73 69 6F 6E 3D 76 32 2E 32 01 00 54 0E 14 // ersion=v2.2..T.. 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 // FrameworkDisplay 4E 61 6D 65 00 ) // Name. .custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 05 44 65 62 75 67 00 00 ) // ...Debug.. .custom instance void [System.Runtime]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. .custom instance void [System.Runtime]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 ) // ...1.0.0.. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module Issue1325.dll // MVID: {CE79A917-9454-47A4-8FF9-0348706BB573} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x01240000 // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi sealed Issue1325.Program extends [System.Runtime]System.Object { .custom instance void Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) .method public static void Main(string[] args) cil managed { .entrypoint .custom instance void [System.Runtime]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method Program::Main .method public static void TestCode(class Issue1325.Test t, int32 i) cil managed { // Code size 64 (0x40) .maxstack 3 .locals init (string V_0) IL_0000: nop IL_0001: ldstr "" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldstr "Test.txt" IL_000d: call string [System.IO.FileSystem]System.IO.File::ReadAllText(string) IL_0012: call string [System.Runtime]System.String::Concat(string, string) IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: ldstr "asdf" IL_001e: call string [System.Runtime]System.String::Concat(string, string) IL_0023: stloc.0 IL_0024: ldarg.0 IL_0025: ldarg.1 IL_0026: ldloc.0 IL_0027: callvirt instance void Issue1325.Test::set_Parameterized(int32, string) IL_002c: nop IL_002d: ldarg.0 IL_002e: ldloc.0 IL_002f: ldstr "asdf" IL_0034: call string [System.Runtime]System.String::Concat(string, string) IL_0039: callvirt instance void Issue1325.Test::set_Unparameterized(string) IL_003e: nop IL_003f: ret } // end of method Program::TestCode } // end of class Issue1325.Program .class private auto ansi Issue1325.Test extends [System.Runtime]System.Object { .field private string _Unparameterized .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Diagnostics.Debug]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .method public specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: ret } // end of method Test::.ctor .method public specialname instance string get_Parameterized(int32 i) cil managed { // Code size 7 (0x7) .maxstack 1 .locals init (string V_0) IL_0000: nop IL_0001: newobj instance void [System.Runtime]System.NotImplementedException::.ctor() IL_0006: throw } // end of method Test::get_Parameterized .method public specialname instance void set_Parameterized(int32 i, string 'value') cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [System.Runtime]System.NotImplementedException::.ctor() IL_0006: throw } // end of method Test::set_Parameterized .method public specialname instance string get_Unparameterized() cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string Issue1325.Test::_Unparameterized IL_0006: br.s IL_0008 IL_0008: ret } // end of method Test::get_Unparameterized .method public specialname instance void set_Unparameterized(string AutoPropertyValue) cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string Issue1325.Test::_Unparameterized IL_0007: ret } // end of method Test::set_Unparameterized .property instance string Parameterized(int32) { .get instance string Issue1325.Test::get_Parameterized(int32) .set instance void Issue1325.Test::set_Parameterized(int32, string) } // end of property Test::Parameterized .property instance string Unparameterized() { .get instance string Issue1325.Test::get_Unparameterized() .set instance void Issue1325.Test::set_Unparameterized(string) } // end of property Test::Unparameterized } // end of class Issue1325.Test .class private auto ansi sealed Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute extends [System.Runtime]System.Attribute { .custom instance void Microsoft.VisualBasic.Embedded::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Runtime]System.AttributeUsageAttribute::.ctor(valuetype [System.Runtime]System.AttributeTargets) = ( 01 00 04 00 00 00 01 00 54 02 09 49 6E 68 65 72 // ........T..Inher 69 74 65 64 00 ) // ited. .custom instance void [System.Runtime]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System.Runtime]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .method public specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Attribute::.ctor() IL_0006: ret } // end of method StandardModuleAttribute::.ctor } // end of class Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute .class private auto ansi sealed Microsoft.VisualBasic.Embedded extends [System.Runtime]System.Attribute { .custom instance void Microsoft.VisualBasic.Embedded::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Runtime]System.AttributeUsageAttribute::.ctor(valuetype [System.Runtime]System.AttributeTargets) = ( 01 00 07 00 00 00 01 00 54 02 09 49 6E 68 65 72 // ........T..Inher 69 74 65 64 00 ) // ited. .custom instance void [System.Runtime]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System.Runtime]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .method public specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Attribute::.ctor() IL_0006: ret } // end of method Embedded::.ctor } // end of class Microsoft.VisualBasic.Embedded ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.vb ================================================ Imports System Imports System.IO Module Program Sub Main(args As String()) End Sub Sub TestCode(ByVal t As Test, ByVal i As Integer) Dim s As String = "" s += File.ReadAllText("Test.txt") s += "asdf" t.Parameterized(i) = s t.Unparameterized = s + "asdf" End Sub End Module Class Test Public Property Parameterized(ByVal i As Integer) As String Get Throw New NotImplementedException() End Get Set(value As String) Throw New NotImplementedException() End Set End Property Public Property Unparameterized As String End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1389.cs ================================================ // Issue1389.Program using System; namespace Issue1389 { public class Program { private static object GetObject() { throw null; } private static void UnusedResultOfIsinst() { _ = GetObject() is TypeCode; } private static bool BoolResultOfIsinst() { return GetObject() is TypeCode; } private static object EnumResultOfIsinst(object A_0) { return (A_0 is TypeCode) ? A_0 : null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1389.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly Issue1389 { .hash algorithm 0x00008004 .ver 1:0:4059:39717 } .module Issue1389.dll .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000003 // ILONLY 32BITREQUIRED .class public auto ansi beforefieldinit Issue1389.Program extends [mscorlib]System.Object { // Methods .method /* 06000001 */ private hidebysig static object GetObject () cil managed { // Method begins at RVA 0x2050 // Code size 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: throw } // end of method Program::GetObject .method /* 06000002 */ private hidebysig static void UnusedResultOfIsinst () cil managed { // Method begins at RVA 0x2053 // Code size 12 (0xc) .maxstack 8 IL_0000: call object Issue1389.Program::GetObject() /* 06000001 */ IL_0005: isinst [mscorlib]System.TypeCode /* 01000002 */ IL_000a: pop IL_000b: ret } // end of method Program::UnusedResultOfIsinst .method /* 06000003 */ private hidebysig static bool BoolResultOfIsinst () cil managed { // Method begins at RVA 0x2060 // Code size 14 (0xe) .maxstack 8 IL_0000: call object Issue1389.Program::GetObject() /* 06000001 */ IL_0005: isinst [mscorlib]System.TypeCode /* 01000002 */ IL_000a: ldnull IL_000b: cgt.un IL_000d: ret } // end of method Program::BoolResultOfIsinst .method /* 06000004 */ private hidebysig static object EnumResultOfIsinst ( object A_0 ) cil managed { // Method begins at RVA 0x206f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: isinst [mscorlib]System.TypeCode /* 01000002 */ IL_0006: ret } // end of method Program::EnumResultOfIsinst } // end of class Issue1389.Program ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1454.cs ================================================ using System.Collections; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public class Issue1454 { public static int GetCardinality(BitArray bitArray) { int[] array = new int[(bitArray.Count >> 5) + 1]; bitArray.CopyTo(array, 0); int num = 0; array[array.Length - 1] &= ~(-1 << bitArray.Count % 32); for (int i = 0; i < array.Length; i++) { int num2 = array[i]; num2 -= (num2 >> 1) & 0x55555555; num2 = (num2 & 0x33333333) + ((num2 >> 2) & 0x33333333); num2 = ((num2 + (num2 >> 4)) & 0xF0F0F0F) * 16843009 >> 24; num += num2; } return num; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1454.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly Issue1454 { .hash algorithm 0x00008004 .ver 1:0:4059:39717 } .module Issue1454.dll .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000003 // ILONLY 32BITREQUIRED .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1454 extends [mscorlib]System.Object { .method public hidebysig static int32 GetCardinality ( class [mscorlib]System.Collections.BitArray bitArray ) cil managed { .maxstack 5 .locals init ( [0] int32[], [1] int32, [2] int32, [3] int32 ) IL_0000: ldarg.0 IL_0001: callvirt instance int32 [mscorlib]System.Collections.BitArray::get_Count() IL_0006: ldc.i4.5 IL_0007: shr IL_0008: ldc.i4.1 IL_0009: add IL_000a: newarr [mscorlib]System.Int32 IL_000f: stloc.0 IL_0010: ldarg.0 IL_0011: ldloc.0 IL_0012: ldc.i4.0 IL_0013: callvirt instance void [mscorlib]System.Collections.BitArray::CopyTo(class [mscorlib]System.Array, int32) IL_0018: ldc.i4.0 IL_0019: stloc.1 IL_001a: ldloc.0 IL_001b: ldloc.0 IL_001c: ldlen IL_001d: conv.i4 IL_001e: ldc.i4.1 IL_001f: sub IL_0020: ldelema [mscorlib]System.Int32 IL_0025: dup IL_0026: ldind.i4 IL_0027: ldc.i4.m1 IL_0028: ldarg.0 IL_0029: callvirt instance int32 [mscorlib]System.Collections.BitArray::get_Count() IL_002e: ldc.i4.s 32 IL_0030: rem IL_0031: ldc.i4.s 31 IL_0033: and IL_0034: shl IL_0035: not IL_0036: and IL_0037: stind.i4 IL_0038: ldc.i4.0 IL_0039: stloc.2 IL_003a: br.s IL_007b // loop start (head: IL_007b) IL_003c: ldloc.0 IL_003d: ldloc.2 IL_003e: ldelem.i4 IL_003f: stloc.3 IL_0040: ldloc.3 IL_0041: ldloc.3 IL_0042: ldc.i4.1 IL_0043: shr IL_0044: ldc.i4 1431655765 IL_0049: and IL_004a: sub IL_004b: stloc.3 IL_004c: ldloc.3 IL_004d: ldc.i4 858993459 IL_0052: and IL_0053: ldloc.3 IL_0054: ldc.i4.2 IL_0055: shr IL_0056: ldc.i4 858993459 IL_005b: and IL_005c: add IL_005d: stloc.3 IL_005e: ldloc.3 IL_005f: ldloc.3 IL_0060: ldc.i4.4 IL_0061: shr IL_0062: add IL_0063: ldc.i4 252645135 IL_0068: and IL_0069: ldc.i4 16843009 IL_006e: mul IL_006f: ldc.i4.s 24 IL_0071: shr IL_0072: stloc.3 IL_0073: ldloc.1 IL_0074: ldloc.3 IL_0075: add IL_0076: stloc.1 IL_0077: ldloc.2 IL_0078: ldc.i4.1 IL_0079: add IL_007a: stloc.2 IL_007b: ldloc.2 IL_007c: ldloc.0 IL_007d: ldlen IL_007e: conv.i4 IL_007f: blt.s IL_003c // end loop IL_0081: ldloc.1 IL_0082: ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1681.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class BaseClass { public int importsClausePosition; } internal class Issue1681 : BaseClass { public void Test() { _ = importsClausePosition; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1681.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.BaseClass extends [mscorlib]System.Object { .field public int32 importsClausePosition } .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1681 extends ICSharpCode.Decompiler.Tests.TestCases.ILPretty.BaseClass { .method public hidebysig instance void Test() cil managed { // Code size 18 (0x12) .maxstack 8 ldarg.0 ldfld int32 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.BaseClass::importsClausePosition pop ret } // end of method Issue1681::Test } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue1918 { public static Guid[] NullVal; public unsafe void ProblemFunction(Guid[] A_0, int A_1) { fixed (Guid* ptr = A_0) { void* ptr2 = ptr; UIntPtr* ptr3 = (UIntPtr*)ptr2 - 1; UIntPtr uIntPtr = *ptr3; try { *ptr3 = (UIntPtr)(ulong)A_1; } finally { *ptr3 = uIntPtr; } } fixed (Guid[] ptr = NullVal) { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue1918 { .ver 1:0:0:0 } .module Issue1918.exe .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1918 extends [mscorlib]System.Object { .method public hidebysig instance void ProblemFunction (valuetype [mscorlib]System.Guid[] '', int32 '' ) cil managed { .maxstack 2 .locals init ( [0] valuetype [mscorlib]System.Guid[], [1] int32, [2] void*, [3] valuetype [mscorlib]System.Guid[] pinned, [4] native uint*, [5] native uint ) IL_0000: ldarg.1 stloc.0 IL_0010: ldarg.2 stloc.1 ldloc.0 dup stloc.3 brfalse.s IL_0026 ldloc.3 ldlen conv.i4 brtrue.s IL_002b IL_0026: ldc.i4.0 conv.u stloc.2 br.s IL_0034 IL_002b: ldloc.3 ldc.i4.0 ldelema [mscorlib]System.Guid conv.u stloc.2 IL_0034: ldloc.2 sizeof [mscorlib]System.UIntPtr sub stloc.s 4 ldloc.s 4 ldind.i stloc.s 5 .try { ldloc.s 4 ldloc.1 conv.i8 call native uint [mscorlib]System.UIntPtr::op_Explicit(uint64) stind.i ldarg.1 leave.s IL_005c } // end .try finally { ldloc.s 4 ldloc.s 5 stind.i endfinally } // end handler IL_005c: ldsfld valuetype [mscorlib]System.Guid[] ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1918::NullVal stloc.3 ret } .field public static valuetype [mscorlib]System.Guid[] NullVal } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1922.cs ================================================ namespace Issue1922 { public class Program { public static long fnWorks(int x, int y) { return (long)((((ulong)y & 0xFFFFFFuL) << 24) | ((ulong)x & 0xFFFFFFuL)); } public static long fnFails(int x, int y) { return ((y & 0xFFFFFFL) << 24) | (x & 0xFFFFFFL); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1922.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly Issue1922 { .hash algorithm 0x00008004 .ver 1:0:4059:39717 } .module Issue1389.dll .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000003 // ILONLY 32BITREQUIRED .class public auto ansi beforefieldinit Issue1922.Program extends [mscorlib]System.Object { // Methods .method public hidebysig static int64 fnWorks (int32 x, int32 y) cil managed { .maxstack 8 ldarg.1 conv.i8 ldc.i4 16777215 conv.i8 and ldc.i4.s 24 shl ldarg.0 conv.i8 ldc.i4 16777215 conv.i8 and or ret } .method public hidebysig static int64 fnFails (int32 x, int32 y) cil managed { .maxstack 8 ldarg.1 conv.i8 ldc.i8 16777215 and ldc.i4.s 24 shl ldarg.0 conv.i8 ldc.i8 16777215 and or ret } } // end of class Issue1922.Program ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2104.cs ================================================ using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue2104 { [CompilerGenerated] private readonly string text; public string Text { [CompilerGenerated] get { return text; } } public Issue2104(string text) { this.text = text; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2104.il ================================================ .assembly issue2104 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 ) .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .module issue2104.exe // MVID: {A7498FA1-F4D7-486E-A872-3DC0CAFD87E2} .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .class private auto ansi '' { } // end of class .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104 extends [mscorlib]System.Object { // Fields .field private initonly string 'text' .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig specialname instance string get_Text () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2074 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104::text IL_0006: ret } // end of method ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104::get_Text .method public hidebysig specialname rtspecialname instance void .ctor ( string text ) cil managed { // Method begins at RVA 0x207c // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104::'text' IL_000d: ret } // end of method ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104::.ctor // Properties .property instance string Text() { .get instance string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104::get_Text() } } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2104 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2260SwitchString.cs ================================================ using System.Windows.Forms; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue2260 { private void dgvItemList_CellValueChanged(object sender, DataGridViewCellEventArgs e) { string text = default(string); string s = default(string); switch (text) { case "rowno": break; case "item_no": new object(); break; case "stock_qty": { decimal result2 = default(decimal); if (!decimal.TryParse(s, out result2)) { new object(); } else if (result2 < 1m) { new object(); } break; } case "new_price": { decimal num = default(decimal); break; } case "new_price4": { decimal result4 = default(decimal); if (decimal.TryParse(s, out result4) && !(result4 < 0m)) { } break; } case "new_price1": { decimal result3 = default(decimal); if (!decimal.TryParse(s, out result3)) { new object(); break; } if (result3 < 0m) { new object(); } new object(); break; } case "new_price2": { decimal result = default(decimal); if (!decimal.TryParse(s, out result)) { new object(); } else if (result < 0m) { new object(); } break; } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2260SwitchString.il ================================================ .assembly extern System.Data { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System.Windows.Forms { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue2260 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 ) .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .module Issue2260.exe // MVID: {A7498FA1-F4D7-486E-A872-3DC0CAFD87E2} .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .class private auto ansi '' { } // end of class .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2260 extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } .method private hidebysig instance void dgvItemList_CellValueChanged ( object sender, class [System.Windows.Forms]System.Windows.Forms.DataGridViewCellEventArgs e ) cil managed { // Method begins at RVA 0x2088 // Code size 885 (0x375) .maxstack 2 .locals init ( [0] string, [1] class [System.Data]System.Data.DataRow, [2] string, [3] class [System.Data]System.Data.DataTable, [4] string, [5] uint32, [6] object, [7] class [System.Data]System.Data.DataTable, [8] valuetype [mscorlib]System.Decimal, [9] valuetype [mscorlib]System.Decimal, [10] valuetype [mscorlib]System.Decimal, [11] valuetype [mscorlib]System.Decimal, [12] valuetype [mscorlib]System.Decimal, [13] valuetype [mscorlib]System.Decimal, [14] valuetype [mscorlib]System.Decimal, [15] valuetype [mscorlib]System.Decimal, [16] valuetype [mscorlib]System.Decimal, [17] valuetype [mscorlib]System.Decimal, [18] valuetype [mscorlib]System.Decimal, [19] valuetype [mscorlib]System.Decimal, [20] valuetype [mscorlib]System.Decimal, [21] valuetype [mscorlib]System.Decimal, [22] valuetype [mscorlib]System.Decimal, [23] valuetype [mscorlib]System.Decimal, [24] valuetype [mscorlib]System.Decimal, [25] valuetype [mscorlib]System.Decimal, [26] valuetype [mscorlib]System.Decimal ) IL_0000: ldloc.0 IL_0001: stloc.s 4 IL_0003: ldloc.s 4 IL_0005: call uint32 ''::ComputeStringHash(string) IL_000a: stloc.s 5 IL_000c: ldloc.s 5 IL_000e: ldc.i4 -1481643850 IL_0013: bgt.un.s IL_0031 IL_0015: ldloc.s 5 IL_0017: ldc.i4 1288728352 IL_001c: beq.s IL_0063 IL_001e: ldloc.s 5 IL_0020: ldc.i4 -1700853173 IL_0025: beq.s IL_0092 IL_0027: ldloc.s 5 IL_0029: ldc.i4 -1481643850 IL_002e: beq.s IL_0080 IL_0030: ret IL_0031: ldloc.s 5 IL_0033: ldc.i4 -497189362 IL_0038: bgt.un.s IL_0050 IL_003a: ldloc.s 5 IL_003c: ldc.i4 -513966981 IL_0041: beq IL_00c8 IL_0046: ldloc.s 5 IL_0048: ldc.i4 -497189362 IL_004d: beq.s IL_00b6 IL_004f: ret IL_0050: ldloc.s 5 IL_0052: ldc.i4 -451434784 IL_0057: beq.s IL_0071 IL_0059: ldloc.s 5 IL_005b: ldc.i4 -413301267 IL_0060: beq.s IL_00a4 IL_0062: ret IL_0063: ldloc.s 4 IL_0065: ldstr "rowno" IL_006a: call bool [mscorlib]System.String::op_Equality(string, string) IL_006f: pop IL_0070: ret IL_0071: ldloc.s 4 IL_0073: ldstr "item_no" IL_0078: call bool [mscorlib]System.String::op_Equality(string, string) IL_007d: brtrue.s IL_00da IL_007f: ret IL_0080: ldloc.s 4 IL_0082: ldstr "stock_qty" IL_0087: call bool [mscorlib]System.String::op_Equality(string, string) IL_008c: brtrue IL_021f IL_0091: ret IL_0092: ldloc.s 4 IL_0094: ldstr "new_price" IL_0099: call bool [mscorlib]System.String::op_Equality(string, string) IL_009e: brtrue IL_0285 IL_00a3: ret IL_00a4: ldloc.s 4 IL_00a6: ldstr "new_price4" IL_00ab: call bool [mscorlib]System.String::op_Equality(string, string) IL_00b0: brtrue IL_02d6 IL_00b5: ret IL_00b6: ldloc.s 4 IL_00b8: ldstr "new_price1" IL_00bd: call bool [mscorlib]System.String::op_Equality(string, string) IL_00c2: brtrue IL_0303 IL_00c7: ret IL_00c8: ldloc.s 4 IL_00ca: ldstr "new_price2" IL_00cf: call bool [mscorlib]System.String::op_Equality(string, string) IL_00d4: brtrue IL_0338 IL_00d9: ret IL_00da: nop IL_00db: nop IL_00dc: nop IL_00dd: nop IL_00de: nop IL_00df: nop IL_00e0: nop IL_00e1: nop IL_00e2: nop IL_00e3: nop IL_00e4: nop IL_00e5: nop IL_00e6: nop IL_00e7: nop IL_00e8: nop IL_00e9: nop IL_00ea: nop IL_00eb: nop IL_00ec: nop IL_00ed: nop IL_00ee: nop IL_00ef: nop IL_00f0: nop IL_00f1: nop IL_00f2: nop IL_00f3: nop IL_00f4: nop IL_00f5: nop IL_00f6: nop IL_00f7: nop IL_00f8: nop IL_00f9: nop IL_00fa: nop IL_00fb: nop IL_00fc: nop IL_00fd: nop IL_00fe: nop IL_00ff: nop IL_0100: nop IL_0101: nop IL_0102: nop IL_0103: nop IL_0104: nop IL_0105: nop IL_0106: nop IL_0107: nop IL_0108: nop IL_0109: nop IL_010a: nop IL_010b: nop IL_010c: nop IL_010d: nop IL_010e: nop IL_010f: nop IL_0110: nop IL_0111: nop IL_0112: nop IL_0113: nop IL_0114: nop IL_0115: nop IL_0116: nop IL_0117: nop IL_0118: nop IL_0119: nop IL_011a: nop IL_011b: nop IL_011c: nop IL_011d: nop IL_011e: nop IL_011f: nop IL_0120: nop IL_0121: nop IL_0122: nop IL_0123: nop IL_0124: nop IL_0125: nop IL_0126: nop IL_0127: nop IL_0128: nop IL_0129: nop IL_012a: nop IL_012b: nop IL_012c: nop IL_012d: nop IL_012e: nop IL_012f: nop IL_0130: nop IL_0131: nop IL_0132: nop IL_0133: nop IL_0134: nop IL_0135: nop IL_0136: nop IL_0137: nop IL_0138: nop IL_0139: nop IL_013a: nop IL_013b: nop IL_013c: nop IL_013d: nop IL_013e: nop IL_013f: nop IL_0140: nop IL_0141: nop IL_0142: nop IL_0143: nop IL_0144: nop IL_0145: nop IL_0146: nop IL_0147: nop IL_0148: nop IL_0149: nop IL_014a: nop IL_014b: nop IL_014c: nop IL_014d: nop IL_014e: nop IL_014f: nop IL_0150: nop IL_0151: nop IL_0152: nop IL_0153: nop IL_0154: nop IL_0155: nop IL_0156: nop IL_0157: nop IL_0158: nop IL_0159: nop IL_015a: nop IL_015b: nop IL_015c: nop IL_015d: nop IL_015e: nop IL_015f: nop IL_0160: nop IL_0161: nop IL_0162: nop IL_0163: nop IL_0164: nop IL_0165: nop IL_0166: nop IL_0167: nop IL_0168: nop IL_0169: nop IL_016a: nop IL_016b: nop IL_016c: nop IL_016d: nop IL_016e: nop IL_016f: nop IL_0170: nop IL_0171: nop IL_0172: nop IL_0173: nop IL_0174: nop IL_0175: nop IL_0176: nop IL_0177: nop IL_0178: nop IL_0179: nop IL_017a: nop IL_017b: nop IL_017c: nop IL_017d: nop IL_017e: nop IL_017f: nop IL_0180: nop IL_0181: nop IL_0182: nop IL_0183: nop IL_0184: nop IL_0185: nop IL_0186: nop IL_0187: nop IL_0188: nop IL_0189: nop IL_018a: nop IL_018b: nop IL_018c: nop IL_018d: nop IL_018e: nop IL_018f: nop IL_0190: nop IL_0191: nop IL_0192: nop IL_0193: nop IL_0194: nop IL_0195: nop IL_0196: nop IL_0197: nop IL_0198: nop IL_0199: nop IL_019a: nop IL_019b: nop IL_019c: nop IL_019d: nop IL_019e: nop IL_019f: nop IL_01a0: nop IL_01a1: nop IL_01a2: nop IL_01a3: nop IL_01a4: nop IL_01a5: nop IL_01a6: nop IL_01a7: nop IL_01a8: nop IL_01a9: nop IL_01aa: nop IL_01ab: nop IL_01ac: nop IL_01ad: nop IL_01ae: nop IL_01af: nop IL_01b0: nop IL_01b1: nop IL_01b2: nop IL_01b3: nop IL_01b4: nop IL_01b5: nop IL_01b6: nop IL_01b7: nop IL_01b8: nop IL_01b9: nop IL_01ba: nop IL_01bb: nop IL_01bc: nop IL_01bd: nop IL_01be: nop IL_01bf: nop IL_01c0: nop IL_01c1: nop IL_01c2: nop IL_01c3: nop IL_01c4: nop IL_01c5: nop IL_01c6: nop IL_01c7: nop IL_01c8: nop IL_01c9: nop IL_01ca: nop IL_01cb: nop IL_01cc: nop IL_01cd: nop IL_01ce: nop IL_01cf: nop IL_01d0: nop IL_01d1: nop IL_01d2: nop IL_01d3: nop IL_01d4: nop IL_01d5: nop IL_01d6: nop IL_01d7: nop IL_01d8: nop IL_01d9: nop IL_01da: nop IL_01db: nop IL_01dc: nop IL_01dd: nop IL_01de: nop IL_01df: nop IL_01e0: nop IL_01e1: nop IL_01e2: nop IL_01e3: nop IL_01e4: nop IL_01e5: nop IL_01e6: nop IL_01e7: nop IL_01e8: nop IL_01e9: nop IL_01ea: nop IL_01eb: nop IL_01ec: nop IL_01ed: nop IL_01ee: nop IL_01ef: nop IL_01f0: nop IL_01f1: nop IL_01f2: nop IL_01f3: nop IL_01f4: nop IL_01f5: nop IL_01f6: nop IL_01f7: nop IL_01f8: nop IL_01f9: nop IL_01fa: nop IL_01fb: nop IL_01fc: nop IL_01fd: nop IL_01fe: nop IL_01ff: nop IL_0200: nop IL_0201: nop IL_0202: nop IL_0203: nop IL_0204: nop IL_0205: nop IL_0206: nop IL_0207: nop IL_0208: nop IL_0209: nop IL_020a: nop IL_020b: nop IL_020c: nop IL_020d: nop IL_020e: nop IL_020f: nop IL_0210: nop IL_0211: nop IL_0212: nop IL_0213: nop IL_0214: nop IL_0215: nop IL_0216: nop IL_0217: newobj instance void [mscorlib]System.Object::.ctor() IL_021c: pop IL_021d: nop IL_021e: ret IL_021f: ldloca.s 10 IL_0221: initobj [mscorlib]System.Decimal IL_0227: ldloc.2 IL_0228: ldloca.s 10 IL_022a: call bool [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Decimal&) IL_022f: brtrue.s IL_023c IL_0231: nop IL_0232: newobj instance void [mscorlib]System.Object::.ctor() IL_0237: pop IL_0238: nop IL_0239: nop IL_023a: nop IL_023b: ret IL_023c: ldloc.s 10 IL_023e: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::One IL_0243: call bool [mscorlib]System.Decimal::op_LessThan(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal) IL_0248: brfalse.s IL_025c IL_024a: nop IL_024b: nop IL_024c: nop IL_024d: nop IL_024e: nop IL_024f: nop IL_0250: nop IL_0251: newobj instance void [mscorlib]System.Object::.ctor() IL_0256: pop IL_0257: nop IL_0258: nop IL_0259: nop IL_025a: nop IL_025b: nop IL_025c: nop IL_025d: nop IL_025e: nop IL_025f: nop IL_0260: nop IL_0261: nop IL_0262: nop IL_0263: nop IL_0264: nop IL_0265: nop IL_0266: nop IL_0267: nop IL_0268: nop IL_0269: nop IL_026a: nop IL_026b: nop IL_026c: nop IL_026d: nop IL_026e: nop IL_026f: nop IL_0270: nop IL_0271: nop IL_0272: nop IL_0273: nop IL_0274: nop IL_0275: nop IL_0276: nop IL_0277: nop IL_0278: nop IL_0279: nop IL_027a: nop IL_027b: nop IL_027c: nop IL_027d: nop IL_027e: nop IL_027f: nop IL_0280: nop IL_0281: nop IL_0282: nop IL_0283: nop IL_0284: ret IL_0285: ldloca.s 15 IL_0287: initobj [mscorlib]System.Decimal IL_028d: nop IL_028e: nop IL_028f: nop IL_0290: nop IL_0291: nop IL_0292: nop IL_0293: nop IL_0294: nop IL_0295: nop IL_0296: nop IL_0297: nop IL_0298: nop IL_0299: nop IL_029a: nop IL_029b: nop IL_029c: nop IL_029d: nop IL_029e: nop IL_029f: nop IL_02a0: nop IL_02a1: nop IL_02a2: nop IL_02a3: nop IL_02a4: nop IL_02a5: nop IL_02a6: nop IL_02a7: nop IL_02a8: nop IL_02a9: nop IL_02aa: nop IL_02ab: nop IL_02ac: nop IL_02ad: nop IL_02ae: nop IL_02af: nop IL_02b0: nop IL_02b1: nop IL_02b2: nop IL_02b3: nop IL_02b4: nop IL_02b5: nop IL_02b6: nop IL_02b7: nop IL_02b8: nop IL_02b9: nop IL_02ba: nop IL_02bb: nop IL_02bc: nop IL_02bd: nop IL_02be: nop IL_02bf: nop IL_02c0: nop IL_02c1: nop IL_02c2: nop IL_02c3: nop IL_02c4: nop IL_02c5: nop IL_02c6: nop IL_02c7: nop IL_02c8: nop IL_02c9: nop IL_02ca: nop IL_02cb: nop IL_02cc: nop IL_02cd: nop IL_02ce: nop IL_02cf: nop IL_02d0: nop IL_02d1: nop IL_02d2: nop IL_02d3: nop IL_02d4: nop IL_02d5: ret IL_02d6: ldloca.s 20 IL_02d8: initobj [mscorlib]System.Decimal IL_02de: ldloc.2 IL_02df: ldloca.s 20 IL_02e1: call bool [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Decimal&) IL_02e6: brtrue.s IL_02ed IL_02e8: nop IL_02e9: nop IL_02ea: nop IL_02eb: nop IL_02ec: ret IL_02ed: ldloc.s 20 IL_02ef: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Zero IL_02f4: call bool [mscorlib]System.Decimal::op_LessThan(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal) IL_02f9: brfalse IL_0374 IL_02fe: nop IL_02ff: nop IL_0300: nop IL_0301: nop IL_0302: ret IL_0303: ldloca.s 21 IL_0305: initobj [mscorlib]System.Decimal IL_030b: ldloc.2 IL_030c: ldloca.s 21 IL_030e: call bool [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Decimal&) IL_0313: brtrue.s IL_031c IL_0315: newobj instance void [mscorlib]System.Object::.ctor() IL_031a: pop IL_031b: ret IL_031c: ldloc.s 21 IL_031e: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Zero IL_0323: call bool [mscorlib]System.Decimal::op_LessThan(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal) IL_0328: brfalse.s IL_0330 IL_032a: newobj instance void [mscorlib]System.Object::.ctor() IL_032f: pop IL_0330: nop IL_0331: newobj instance void [mscorlib]System.Object::.ctor() IL_0336: pop IL_0337: ret IL_0338: ldloca.s 26 IL_033a: initobj [mscorlib]System.Decimal IL_0340: ldloc.2 IL_0341: ldloca.s 26 IL_0343: call bool [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Decimal&) IL_0348: brtrue.s IL_0354 IL_034a: nop IL_034b: nop IL_034c: newobj instance void [mscorlib]System.Object::.ctor() IL_0351: pop IL_0352: nop IL_0353: ret IL_0354: ldloc.s 26 IL_0356: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Zero IL_035b: call bool [mscorlib]System.Decimal::op_LessThan(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal) IL_0360: brfalse.s IL_0374 IL_0362: nop IL_0363: nop IL_0364: nop IL_0365: nop IL_0366: nop IL_0367: newobj instance void [mscorlib]System.Object::.ctor() IL_036c: pop IL_036d: nop IL_036e: nop IL_036f: nop IL_0370: nop IL_0371: nop IL_0372: nop IL_0373: nop IL_0374: ret } // end of method FrmSheetPX::dgvItemList_CellValueChanged } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue2260 .class private auto ansi sealed '' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method assembly hidebysig static uint32 ComputeStringHash ( string s ) cil managed { // Method begins at RVA 0x2050 // Code size 44 (0x2c) .maxstack 2 .locals init ( [0] uint32, [1] int32 ) IL_0000: ldarg.0 IL_0001: brfalse.s IL_002a IL_0003: ldc.i4 -2128831035 IL_0008: stloc.0 IL_0009: ldc.i4.0 IL_000a: stloc.1 IL_000b: br.s IL_0021 // loop start (head: IL_0021) IL_000d: ldarg.0 IL_000e: ldloc.1 IL_000f: callvirt instance char [mscorlib]System.String::get_Chars(int32) IL_0014: ldloc.0 IL_0015: xor IL_0016: ldc.i4 16777619 IL_001b: mul IL_001c: stloc.0 IL_001d: ldloc.1 IL_001e: ldc.i4.1 IL_001f: add IL_0020: stloc.1 IL_0021: ldloc.1 IL_0022: ldarg.0 IL_0023: callvirt instance int32 [mscorlib]System.String::get_Length() IL_0028: blt.s IL_000d // end loop IL_002a: ldloc.0 IL_002b: ret } // end of method ''::ComputeStringHash } // end of class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2443.cs ================================================ using System; using System.Reflection; using ClassLibrary1; [assembly: AssemblyCompany("ConsoleApp8")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ConsoleApp8")] [assembly: AssemblyTitle("ConsoleApp8")] namespace ConsoleApp8 { internal class Program : Class1 { public Program(string _) : base(_) { Console.WriteLine("Class1(string)<-Program(string)"); } private static void Main(string[] args) { new Program(""); Console.WriteLine("Hello World!"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue2443.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern ClassLibrary1 { .ver 1:0:0:0 } .assembly ConsoleApp8 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1C 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ....NETFramework 2C 56 65 72 73 69 6F 6E 3D 76 34 2E 37 2E 32 01 // ,Version=v4.7.2. 00 54 0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 // .T..FrameworkDis 70 6C 61 79 4E 61 6D 65 14 2E 4E 45 54 20 46 72 // playName..NET Fr 61 6D 65 77 6F 72 6B 20 34 2E 37 2E 32 ) // amework 4.7.2 .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 0B 43 6F 6E 73 6F 6C 65 41 70 70 38 00 00 ) // ...ConsoleApp8.. .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 07 52 65 6C 65 61 73 65 00 00 ) // ...Release.. .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. .custom instance void [mscorlib]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 ) // ...1.0.0.. .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0B 43 6F 6E 73 6F 6C 65 41 70 70 38 00 00 ) // ...ConsoleApp8.. .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0B 43 6F 6E 73 6F 6C 65 41 70 70 38 00 00 ) // ...ConsoleApp8.. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module ConsoleApp8.exe // MVID: {C71BB5CC-A98C-4CF4-B8A4-000055F94187} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x02890000 // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ConsoleApp8.Program extends [ClassLibrary1]ClassLibrary1.Class1 { .method public hidebysig specialname rtspecialname instance void .ctor(string _) cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance void [ClassLibrary1]ClassLibrary1.Class1::.ctor(string) IL_0007: ldstr "Class1(string)<-Program(string)" IL_000c: call void [mscorlib]System.Console::WriteLine(string) IL_0011: ret } // end of method Program::.ctor .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 22 (0x16) .maxstack 8 IL_0000: ldstr "" IL_0005: newobj instance void ConsoleApp8.Program::.ctor(string) IL_000a: pop IL_000b: ldstr "Hello World!" IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ret } // end of method Program::Main } // end of class ConsoleApp8.Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file Issue2443.res ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3344CkFinite.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public class Issue3344 { private static float GetFloat() { return 3.5f; } private static float CkFinite() { float num = GetFloat(); if (!float.IsFinite(num)) { throw new ArithmeticException(); } return num; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3344CkFinite.il ================================================ .assembly extern System.Runtime { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue3344 { .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3344 extends [System.Runtime]System.Object { .method private hidebysig static float32 GetFloat () cil managed { .maxstack 8 IL_0000: ldc.r4 3.5 IL_0005: ret } .method private hidebysig static float32 CkFinite () cil managed { .maxstack 1 .locals init ( [0] float32 ) call float32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3344::GetFloat() ckfinite ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3421.cs ================================================ internal class Issue3421 { private string name; private object value; public virtual void SetValue(object value) { switch (name) { case "##Name##": return; case "##Value##": this.value = value; return; case "##InnerText##": this.value = value.ToString(); return; case null: return; } if (this.value == null) { this.value = ""; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3421.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .class private auto ansi beforefieldinit Issue3421 extends [CORE_ASSEMBLY]System.Object { // Fields .field private string name .field private object 'value' .field private static class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2 '<>f__switch$map1D' .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig virtual instance void SetValue ( object 'value' ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 180 (0xb4) .maxstack 27 .locals init ( [0] string, [1] class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2, [2] int32 ) IL_0000: ldarg.0 IL_0001: ldfld string Issue3421::name IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse IL_0093 IL_000d: ldsfld class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2 Issue3421::'<>f__switch$map1D' IL_0012: brtrue IL_0048 IL_0017: ldc.i4.3 IL_0018: newobj instance void class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2::.ctor(int32) IL_001d: stloc.1 IL_001e: ldloc.1 IL_001f: ldstr "##Name##" IL_0024: ldc.i4.0 IL_0025: callvirt instance void class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_002a: ldloc.1 IL_002b: ldstr "##Value##" IL_0030: ldc.i4.1 IL_0031: callvirt instance void class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0036: ldloc.1 IL_0037: ldstr "##InnerText##" IL_003c: ldc.i4.2 IL_003d: callvirt instance void class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0042: ldloc.1 IL_0043: stsfld class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2 Issue3421::'<>f__switch$map1D' IL_0048: ldsfld class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2 Issue3421::'<>f__switch$map1D' IL_004d: ldloc.0 IL_004e: ldloca.s 2 IL_0050: callvirt instance bool class [CORE_ASSEMBLY]System.Collections.Generic.Dictionary`2::TryGetValue(!0, !1&) IL_0055: brfalse IL_0098 IL_005a: ldloc.2 IL_005b: switch (IL_0071, IL_0076, IL_0082) IL_006c: br IL_0098 IL_0071: br IL_00b3 IL_0076: ldarg.0 IL_0077: ldarg.1 IL_0078: stfld object Issue3421::'value' IL_007d: br IL_00b3 IL_0082: ldarg.0 IL_0083: ldarg.1 IL_0084: callvirt instance string [CORE_ASSEMBLY]System.Object::ToString() IL_0089: stfld object Issue3421::'value' IL_008e: br IL_00b3 IL_0093: br IL_00b3 IL_0098: ldarg.0 IL_0099: ldfld object Issue3421::'value' IL_009e: brtrue IL_00ae IL_00a3: ldarg.0 IL_00a4: ldstr "" IL_00a9: stfld object Issue3421::'value' IL_00ae: br IL_00b3 IL_00b3: ret } // end of method Issue3421::SetValue .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2110 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [CORE_ASSEMBLY]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Issue3421::.ctor } // end of class Issue3421 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3442.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442 { public class Class : Interface { private void M() where T : Interface { } void Interface.M() { //ILSpy generated this explicit interface implementation from .override directive in M this.M(); } } public interface Interface { void M() where T : Interface; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3442.il ================================================ .assembly extern System.Runtime { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue3442 { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 ) .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = ( 2e 01 80 8a 53 79 73 74 65 6d 2e 53 65 63 75 72 69 74 79 2e 50 65 72 6d 69 73 73 69 6f 6e 73 2e 53 65 63 75 72 69 74 79 50 65 72 6d 69 73 73 69 6f 6e 41 74 74 72 69 62 75 74 65 2c 20 53 79 73 74 65 6d 2e 52 75 6e 74 69 6d 65 2c 20 56 65 72 73 69 6f 6e 3d 39 2e 30 2e 30 2e 30 2c 20 43 75 6c 74 75 72 65 3d 6e 65 75 74 72 61 6c 2c 20 50 75 62 6c 69 63 4b 65 79 54 6f 6b 65 6e 3d 62 30 33 66 35 66 37 66 31 31 64 35 30 61 33 61 15 01 54 02 10 53 6b 69 70 56 65 72 69 66 69 63 61 74 69 6f 6e 01 ) .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class private auto ansi '' { } // end of class .class interface public auto ansi abstract beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface { // Methods .method public hidebysig newslot abstract virtual instance void M<(ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface) T> () cil managed { } // end of method Interface::M } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Class extends [System.Runtime]System.Object implements ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface { // Methods .method private final hidebysig newslot virtual instance void M<(ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface) T> () cil managed { .override method instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Interface::M() // Method begins at RVA 0x2050 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Class::M .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2052 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: ret } // end of method Class::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3442.Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3465.cs ================================================ using System; #if EXPECTED_OUTPUT using System.Runtime.CompilerServices; #endif namespace Issue3465 { internal class Program { private static Program programNull; private static Program GetProgram() { return null; } private static bool Test3465() { Program program = GetProgram(); return System.Runtime.CompilerServices.Unsafe.As(ref program) > System.Runtime.CompilerServices.Unsafe.As(ref programNull); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3465.il ================================================ .class private auto ansi '' { } // end of class .class private auto ansi beforefieldinit Issue3465.Program extends [System.Runtime]System.Object { // Fields .field private static class Issue3465.Program programNull // Methods .method private hidebysig static class Issue3465.Program GetProgram () cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: ret } // end of method Issue3465.Program::GetProgram .method private hidebysig static bool Test3465 () cil managed { // Method begins at RVA 0x2054 // Header size: 12 // Code size: 7 (0x7) .maxstack 1 .locals init ( [0] bool ) IL_0000: call class Issue3465.Program Issue3465.Program::GetProgram() IL_0001: ldsfld class Issue3465.Program Issue3465.Program::programNull cgt.un IL_0002: stloc.0 IL_0003: br.s IL_0005 IL_0005: ldloc.0 IL_0006: ret } // end of method Program::Test3465 .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2067 // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Issue3465.Program::.ctor } // end of class Issue3465.Program ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3466.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public class Issue3466 { public static implicit operator Issue3466(T t) { return null; } public static bool M(Issue3466 x) { return x != null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3466.il ================================================ .assembly extern System.Runtime { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue3466 { .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3466`1 extends [System.Runtime]System.Object { .method public hidebysig specialname static class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3466`1 op_Implicit ( !T t ) cil managed { .maxstack 8 IL_0000: ldnull IL_0001: ret } .method public final hidebysig static bool M(class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3466`1 x) cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0004 IL_0002: ldc.i4.0 IL_0003: br.s IL_0005 IL_0004: ldc.i4.1 IL_0005: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3504.cs ================================================ using System; internal class Issue3504 { private void Method(Console console) { console.WriteLine("Hello."); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3504.il ================================================ .class private auto ansi beforefieldinit Issue3504 extends [System.Runtime]System.Object { // Methods .method private hidebysig instance void Method (class [System.Console]System.Console console) cil managed { // Method begins at RVA 0x2050 // Header size: 1 // Code size: 13 (0xd) .maxstack 8 IL_0000: nop IL_0001: ldarg.1 IL_0002: ldstr "Hello." IL_0007: call instance void [System.Console]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret } // end of method Issue3504::Method .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x205e // Header size: 1 // Code size: 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Issue3504::.ctor } // end of class Issue3504 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3524.cs ================================================ public class C { public static int X { get { return 32; } set { } } static C() { X = 1; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3524.il ================================================ .assembly _ { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 ) .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .hash algorithm 0x00008004 // SHA1 .ver 0:0:0:0 } .class public auto ansi beforefieldinit C extends [System.Runtime]System.Object { // Methods .method public hidebysig specialname static int32 get_X () cil managed { // Method begins at RVA 0x2050 // Code size 6 (0x6) .maxstack 8 IL_0000: ldc.i4 32 IL_0005: ret } // end of method C::get_X .method public hidebysig specialname static void set_X ( int32 'value' ) cil managed { // Method begins at RVA 0x2058 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method C::set_X .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2068 // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method C::.ctor .method private hidebysig specialname rtspecialname static void .cctor () cil managed { // Method begins at RVA 0x2074 // Code size 11 (0xb) .maxstack 8 IL_0000: ldc.i4 1 IL_0005: call void C::set_X(int32) IL_000a: ret } // end of method C::.cctor // Properties .property int32 X() { .get int32 C::get_X() .set void C::set_X(int32) } } // end of class C ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3552.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public static class Issue3552 { public static Issue3552_IntegerPair MakePair1(int x, int y) { Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y }; return issue3552_IntegerPairBuilder.ToPair(); } public static Issue3552_IntegerPair MakePair2(int x, int y) { Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y }; return issue3552_IntegerPairBuilder.ToPair(); } public static Issue3552_IntegerPair MakePair3(int x, int y) { Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y }; return issue3552_IntegerPairBuilder.ToPair(); } } public struct Issue3552_IntegerPair { public int X; public int Y; } public struct Issue3552_IntegerPairBuilder : IEnumerable, IEnumerable { private int index; private Issue3552_IntegerPair pair; public readonly Issue3552_IntegerPair ToPair() { return pair; } public void Add(int value) { switch (index) { case 0: pair.X = value; break; case 1: pair.Y = value; break; default: throw new IndexOutOfRangeException(); } index++; } public IEnumerator GetEnumerator() { return null; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3552.il ================================================ .class public auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552 extends [System.Runtime]System.Object { // Methods .method public hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair1 ( int32 x, int32 y ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 34 (0x22) .maxstack 2 .locals init ( [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder ) IL_0000: ldloca.s 0 IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder IL_0008: ldloca.s 0 IL_000a: ldarg.0 IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_0010: ldloca.s 0 IL_0012: ldarg.1 IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_0015: ldloca.s 0 IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair() IL_0021: ret } // end of method Issue3552::MakePair1 .method public hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair2 ( int32 x, int32 y ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 34 (0x22) .maxstack 2 .locals init ( [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder ) IL_0000: ldloca.s 0 IL_0001: dup IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder IL_0008: dup IL_000a: ldarg.0 IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_0012: ldarg.1 IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_0015: ldloca.s 0 IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair() IL_0021: ret } // end of method Issue3552::MakePair2 .method public hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair3 ( int32 x, int32 y ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 34 (0x22) .maxstack 2 .locals init ( [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder ) IL_0000: ldloca.s 0 IL_0001: dup IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder IL_0008: dup IL_000a: ldarg.0 IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_0010: dup IL_0012: ldarg.1 IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32) IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair() IL_0021: ret } // end of method Issue3552::MakePair3 } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552 .class public sequential ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair extends [System.Runtime]System.ValueType { // Fields .field public int32 X .field public int32 Y } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair .class public sequential ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder extends [System.Runtime]System.ValueType implements class [System.Runtime]System.Collections.Generic.IEnumerable`1, [System.Runtime]System.Collections.IEnumerable { // Fields .field private int32 index .field private valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair pair // Methods .method public hidebysig instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ToPair () cil managed { .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x207e // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair IL_0006: ret } // end of method Issue3552_IntegerPairBuilder::ToPair .method public hidebysig instance void Add ( int32 'value' ) cil managed { // Method begins at RVA 0x2088 // Header size: 12 // Code size: 65 (0x41) .maxstack 3 .locals init ( [0] int32 ) IL_0000: ldarg.0 IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0010 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq.s IL_001e IL_000e: br.s IL_002c IL_0010: ldarg.0 IL_0011: ldflda valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair IL_0016: ldarg.1 IL_0017: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair::X IL_001c: br.s IL_0032 IL_001e: ldarg.0 IL_001f: ldflda valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair IL_0024: ldarg.1 IL_0025: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair::Y IL_002a: br.s IL_0032 IL_002c: newobj instance void [System.Runtime]System.IndexOutOfRangeException::.ctor() IL_0031: throw IL_0032: ldarg.0 IL_0033: ldarg.0 IL_0034: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index IL_0039: ldc.i4.1 IL_003a: add IL_003b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index IL_0040: ret } // end of method Issue3552_IntegerPairBuilder::Add .method public final hidebysig newslot virtual instance class [System.Runtime]System.Collections.Generic.IEnumerator`1 GetEnumerator () cil managed { // Method begins at RVA 0x20d5 // Header size: 1 // Code size: 2 (0x2) .maxstack 8 IL_0000: ldnull IL_0001: ret } // end of method Issue3552_IntegerPairBuilder::GetEnumerator .method private final hidebysig newslot virtual instance class [System.Runtime]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () cil managed { .override method instance class [System.Runtime]System.Collections.IEnumerator [System.Runtime]System.Collections.IEnumerable::GetEnumerator() // Method begins at RVA 0x20d8 // Header size: 1 // Code size: 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance class [System.Runtime]System.Collections.Generic.IEnumerator`1 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::GetEnumerator() IL_0006: ret } // end of method Issue3552_IntegerPairBuilder::System.Collections.IEnumerable.GetEnumerator } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue379 { public virtual void Test() where T : new() { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue379 extends [mscorlib]System.Object { .method public hidebysig virtual instance void Test<.ctor T>() cil managed { // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method Program::Test } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.VisualBasic.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { [StandardModule] internal sealed class Issue646 { [STAThread] public static void Main() { List list = new List(); foreach (string item in list) { Debug.WriteLine(item); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern Microsoft.VisualBasic { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 10:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class private auto ansi sealed ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue646 extends [mscorlib]System.Object { .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public static void Main () cil managed { .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x21b4 // Code size 61 (0x3d) .maxstack 1 .entrypoint .locals init ( [0] class [mscorlib]System.Collections.Generic.List`1, [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator, [2] string, [3] bool ) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() IL_000d: stloc.1 IL_000e: br.s IL_0020 // loop start (head: IL_0020) IL_0010: ldloca.s 1 IL_0012: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() IL_0017: stloc.2 IL_0018: ldloc.2 IL_0019: call void [System]System.Diagnostics.Debug::WriteLine(string) IL_001e: nop IL_001f: nop IL_0020: ldloca.s 1 IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() IL_0027: stloc.3 IL_0028: ldloc.3 IL_0029: brtrue.s IL_0010 // end loop IL_002b: leave.s IL_003c } // end .try finally { IL_002d: ldloca.s 1 IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_003a: nop IL_003b: endfinally } // end handler IL_003c: ret } // end of method Module1::Main } // end of class ConsoleApp11.Module1 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs ================================================ using System; public static class Issue684 { static int Main(string[] A_0) { int[] array = new int[1000]; int num = int.Parse(Console.ReadLine()); // Point of this test was to ensure the stack slot here uses an appropriate type, // (bool instead of int). Unfortunately our type fixup runs too late to affect variable names. bool num2 = num >= 1000; if (!num2) { num2 = num < 2; } if (num2) { Console.WriteLine(-1); } else { int i = 2; for (int num3 = 2; num3 <= num; num3 = i) { Console.WriteLine(num3); for (; i <= num; i += num3) { int num4 = 1; array[i] = num4; } i = num3; while (true) { bool num5 = i <= num; if (num5) { num5 = array[i] != 0; } if (!num5) { break; } i++; } } } return 0; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Issue684 { .ver 1:0:0:0 } .module Issue684.exe .class public auto ansi abstract sealed Issue684 extends [mscorlib]System.Object { // Methods .method static privatescope int32 Main$PST06000001 ( string[] '' ) cil managed { // Method begins at RVA 0x2050 // Code size 196 (0xc4) .maxstack 11 .entrypoint .locals init ( [0] int32, [1] int32, [2] int32, [3] int32[], [4] int32 ) IL_0000: ldc.i4 1000 IL_0005: newarr [mscorlib]System.Int32 IL_000a: stloc.3 IL_000b: call string [mscorlib]System.Console::ReadLine() IL_0010: call int32 [mscorlib]System.Int32::Parse(string) IL_0015: stloc.2 IL_0016: ldloc.2 IL_0017: ldc.i4 1000 IL_001c: clt IL_001e: ldc.i4.0 IL_001f: ceq IL_0021: dup IL_0022: brtrue IL_0030 IL_0027: pop IL_0028: ldloc.2 IL_0029: ldc.i4 2 IL_002e: clt IL_0030: brfalse IL_0045 IL_0035: ldc.i4 1 IL_003a: neg IL_003b: call void [mscorlib]System.Console::WriteLine(int32) IL_0040: br IL_00c2 IL_0045: ldc.i4 2 IL_004a: stloc.0 IL_004b: ldc.i4 2 IL_0050: stloc.1 // loop start (head: IL_0051) IL_0051: ldloc.1 IL_0052: ldloc.2 IL_0053: cgt IL_0055: ldc.i4.0 IL_0056: ceq IL_0058: brfalse IL_00c2 IL_005d: ldloc.1 IL_005e: call void [mscorlib]System.Console::WriteLine(int32) // loop start (head: IL_0063) IL_0063: ldloc.0 IL_0064: ldloc.2 IL_0065: cgt IL_0067: ldc.i4.0 IL_0068: ceq IL_006a: brfalse IL_0088 IL_006f: ldc.i4 1 IL_0074: stloc.s 4 IL_0076: ldloc.3 IL_0077: ldloc.0 IL_0078: ldloc.s 4 IL_007a: stelem.any [mscorlib]System.Int32 IL_007f: ldloc.0 IL_0080: ldloc.1 IL_0081: add IL_0082: stloc.0 IL_0083: br IL_0063 // end loop IL_0088: ldloc.1 IL_0089: stloc.0 // loop start (head: IL_008a) IL_008a: ldloc.0 IL_008b: ldloc.2 IL_008c: cgt IL_008e: ldc.i4.0 IL_008f: ceq IL_0091: dup IL_0092: brfalse IL_00a9 IL_0097: pop IL_0098: ldloc.3 IL_0099: ldloc.0 IL_009a: ldelem.any [mscorlib]System.Int32 IL_009f: ldc.i4 0 IL_00a4: ceq IL_00a6: ldc.i4.0 IL_00a7: ceq IL_00a9: brfalse IL_00bb IL_00ae: ldloc.0 IL_00af: ldc.i4 1 IL_00b4: add IL_00b5: stloc.0 IL_00b6: br IL_008a // end loop IL_00bb: ldloc.0 IL_00bc: stloc.1 IL_00bd: br IL_0051 // end loop IL_00c2: ldc.i4.0 IL_00c3: ret } // end of method Program::Main } // end of class Issue684 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue959.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue959 { public void Test(bool arg) { if (!arg && arg) { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue959.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly ConsoleApp11 { .ver 1:0:0:0 } .module ConsoleApp11.exe // MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue959 extends [mscorlib]System.Object { .method public hidebysig instance void Test( bool arg ) cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: ldarg.1 IL_0001: brfalse IL_000b IL_0006: br IL_0011 IL_000b: ldarg.1 IL_000c: brtrue IL_0011 IL_0011: ret } // end of method Program::Test } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue982.cs ================================================ using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class Issue982 { private string textStr; private string textStr2; public string Text { get { return textStr; } set { textStr = value; } } [IndexerName("Text2")] public string this[int index] { get { return textStr2; } set { textStr2 = value; } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue982.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 3.5.30729.1 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern System { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly extern Microsoft.VisualBasic { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 10:0:0:0 } .assembly ICSharpCode.Decompiler.Tests.TestCases.ILPretty { .hash algorithm 0x00008004 .ver 1:0:0:0 } .module Issue982.exe // MVID: {1DAED9CF-459B-4BA6-A091-CB3BDEF963F8} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITREQUIRED // Image base: 0x06E80000 // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 05 54 65 78 74 32 00 00 ) // ...Text2.. .field private string textStr .field private string textStr2 .method public specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Issue982::.ctor .method public specialname instance string get_Text() cil managed { // Code size 12 (0xc) .maxstack 1 .locals init ([0] string Text) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::textStr IL_0007: stloc.0 IL_0008: br.s IL_000a IL_000a: ldloc.0 IL_000b: ret } // end of method Issue982::get_Text .method public specialname instance void set_Text(string str) cil managed { // Code size 9 (0x9) .maxstack 8 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: stfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::textStr IL_0008: ret } // end of method Issue982::set_Text .method public specialname instance string get_Text2(int32 index) cil managed { // Code size 12 (0xc) .maxstack 1 .locals init ([0] string Text2) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::textStr2 IL_0007: stloc.0 IL_0008: br.s IL_000a IL_000a: ldloc.0 IL_000b: ret } // end of method Issue982::get_Text2 .method public specialname instance void set_Text2(int32 index, string str) cil managed { // Code size 9 (0x9) .maxstack 8 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.2 IL_0003: stfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::textStr2 IL_0008: ret } // end of method Issue982::set_Text2 .property instance string Text() { .get instance string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::get_Text() .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::set_Text(string) } // end of property Issue982::Text .property instance string Text2(int32) { .get instance string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::get_Text2(int32) .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982::set_Text2(int32, string) } // end of property Issue982::Text2 } // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue982 ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.cs ================================================ using System; public class MonoFixed { public unsafe void FixMultipleStrings(string text) { fixed (char* ptr = text) { fixed (char* userName = Environment.UserName) { fixed (char* ptr2 = text) { *ptr = 'c'; *userName = 'd'; *ptr2 = 'e'; } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.il ================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly MonoFixed { .ver 1:0:0:0 } .module MonoFixed.exe .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WindowsCui .corflags 0x00000001 // ILOnly .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .class public auto ansi beforefieldinit MonoFixed extends [mscorlib]System.Object { .method public hidebysig instance void FixMultipleStrings (string text) cil managed { .maxstack 7 .locals init ( [0] char* pinned, [1] char* pinned, [2] char* pinned, [3] string pinned, [4] string pinned, [5] string pinned ) IL_0000: ldarg.1 IL_0001: stloc.3 IL_0002: ldloc.3 IL_0003: conv.i IL_0004: call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData() IL_0009: add IL_000a: stloc.0 IL_000b: call string [mscorlib]System.Environment::get_UserName() IL_0010: stloc.s 4 IL_0012: ldloc.s 4 IL_0014: conv.i IL_0015: call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData() IL_001a: add IL_001b: stloc.1 IL_001c: ldarg.1 IL_001d: stloc.s 5 IL_001f: ldloc.s 5 IL_0021: conv.i IL_0022: call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData() IL_0027: add IL_0028: stloc.2 IL_0029: ldloc.0 IL_002a: ldc.i4.s 99 IL_002c: stind.i2 IL_002d: ldloc.1 IL_002e: ldc.i4.s 100 IL_0030: stind.i2 IL_0031: ldloc.2 IL_0032: ldc.i4.s 101 IL_0034: stind.i2 IL_0035: ldnull IL_0036: stloc.3 IL_0037: ldnull IL_0038: stloc.s 4 IL_003a: ldnull IL_003b: stloc.s 5 IL_003d: ret } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/SequenceOfNestedIfs.cs ================================================ using System; [Serializable] public class Material { public static implicit operator bool(Material m) { return m == null; } } [Serializable] public class SequenceOfNestedIfs { public bool _clear; public Material _material; public virtual bool CheckShader() { return false; } public virtual void CreateMaterials() { if (!_clear) { if (!CheckShader()) { return; } _material = new Material(); } if (!_material) { if (!CheckShader()) { return; } _material = new Material(); } if (!_material) { if (!CheckShader()) { return; } _material = new Material(); } if (!_material && CheckShader()) { _material = new Material(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/SequenceOfNestedIfs.il ================================================ // Metadata version: v2.0.50727 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 2:0:0:0 } .assembly SequenceOfNestedIfs { .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .hash algorithm 0x00008004 .ver 0:0:0:0 } .module SequenceOfNestedIfs // MVID: {DCEC8A87-5679-4EBE-89A3-51274D8B5446} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x01D60000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi serializable beforefieldinit Material extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Material::.ctor .method public hidebysig specialname static bool op_Implicit(class Material m) cil managed { // Code size 11 (0xb) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldnull IL_0008: ceq IL_000a: ret } // end of method Material::op_Implicit } // end of class Material .class public auto ansi serializable beforefieldinit SequenceOfNestedIfs extends [mscorlib]System.Object { .field public bool _clear .field public class Material _material .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method SequenceOfNestedIfs::.ctor .method public hidebysig virtual instance bool CheckShader() cil managed { // Code size 2 (0x2) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: ret } // end of method SequenceOfNestedIfs::CheckShader .method public hidebysig virtual instance void CreateMaterials() cil managed { // Code size 168 (0xa8) .maxstack 13 IL_0000: ldarg.0 IL_0001: ldfld bool SequenceOfNestedIfs::_clear IL_0006: brtrue IL_0026 IL_000b: ldarg.0 IL_000c: callvirt instance bool SequenceOfNestedIfs::CheckShader() IL_0011: brtrue IL_001b IL_0016: br IL_00a7 IL_001b: ldarg.0 IL_001c: newobj instance void Material::.ctor() IL_0021: stfld class Material SequenceOfNestedIfs::_material IL_0026: ldarg.0 IL_0027: ldfld class Material SequenceOfNestedIfs::_material IL_002c: call bool Material::op_Implicit(class Material) IL_0031: brtrue IL_0051 IL_0036: ldarg.0 IL_0037: callvirt instance bool SequenceOfNestedIfs::CheckShader() IL_003c: brtrue IL_0046 IL_0041: br IL_00a7 IL_0046: ldarg.0 IL_0047: newobj instance void Material::.ctor() IL_004c: stfld class Material SequenceOfNestedIfs::_material IL_0051: ldarg.0 IL_0052: ldfld class Material SequenceOfNestedIfs::_material IL_0057: call bool Material::op_Implicit(class Material) IL_005c: brtrue IL_007c IL_0061: ldarg.0 IL_0062: callvirt instance bool SequenceOfNestedIfs::CheckShader() IL_0067: brtrue IL_0071 IL_006c: br IL_00a7 IL_0071: ldarg.0 IL_0072: newobj instance void Material::.ctor() IL_0077: stfld class Material SequenceOfNestedIfs::_material IL_007c: ldarg.0 IL_007d: ldfld class Material SequenceOfNestedIfs::_material IL_0082: call bool Material::op_Implicit(class Material) IL_0087: brtrue IL_00a7 IL_008c: ldarg.0 IL_008d: callvirt instance bool SequenceOfNestedIfs::CheckShader() IL_0092: brtrue IL_009c IL_0097: br IL_00a7 IL_009c: ldarg.0 IL_009d: newobj instance void Material::.ctor() IL_00a2: stfld class Material SequenceOfNestedIfs::_material IL_00a7: ret } // end of method SequenceOfNestedIfs::CreateMaterials } // end of class SequenceOfNestedIfs ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.cs ================================================ internal class UnknownTypes { private readonly IInterface memberField; public virtual bool CanExecute(CallbackQuery message) { return ((IInterface)(object)memberField).Execute(new SomeClass { ChatId = StaticClass.GetChatId(message), MessageId = StaticClass.GetMessageId(message) }); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .assembly extern UnknownAssembly { .publickeytoken = (01 02 03 04 05 06 07 08 ) .ver 1:0:0:0 } .class private auto ansi beforefieldinit UnknownTypes extends [System.Private.CoreLib]System.Object { .field private initonly class [UnknownAssembly]IInterface memberField // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206e // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [CORE_ASSEMBLY]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method UnknownTypes::.ctor .method public hidebysig virtual instance bool CanExecute ( class [UnknownAssembly]CallbackQuery message ) cil managed { // Method begins at RVA 0x4dbc // Code size 44 (0x2c) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld class [UnknownAssembly]IInterface UnknownTypes::memberField IL_0006: newobj instance void [UnknownAssembly]SomeClass::.ctor() IL_000b: dup IL_000d: ldarg.1 IL_000e: call int64 [UnknownAssembly]StaticClass::GetChatId(class [UnknownAssembly]CallbackQuery) IL_0013: stfld int64 [UnknownAssembly]SomeClass::ChatId IL_0018: dup IL_001a: ldarg.1 IL_001b: call int32 [UnknownAssembly]StaticClass::GetMessageId(class [UnknownAssembly]CallbackQuery) IL_0020: conv.i8 IL_0021: stfld int64 [UnknownAssembly]SomeClass::MessageId IL_0026: callvirt instance !1 class [UnknownAssembly]IInterface`2::Execute(!0) IL_002b: ret } // end of method CanExecute } // end of class UnknownTypes ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs ================================================ using System; using System.Reflection; using System.Runtime.CompilerServices; [assembly: AssemblyMetadata(".NETFrameworkAssembly", "")] [assembly: CLSCompliant(false)] [assembly: AssemblyFileVersion("4.0.0.0")] [assembly: AssemblyInformationalVersion("4.0.0.0")] [assembly: AssemblyTitle("System.Runtime.CompilerServices.Unsafe")] [assembly: AssemblyDescription("System.Runtime.CompilerServices.Unsafe")] [assembly: AssemblyMetadata("Serviceable", "True")] [assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyProduct("Microsoft® .NET Framework")] internal sealed class ExtraUnsafeTests { public unsafe static void PinWithTypeMismatch(ref uint managedPtr) { fixed (ushort* ptr = &Unsafe.As(ref managedPtr)) { } } public unsafe static uint* RefToPointerWithoutPinning(ref uint managedPtr) { return (uint*)Unsafe.AsPointer(ref managedPtr); } public static ref ulong RefAssignTypeMismatch(ref uint a, ref uint b) { ref ushort reference = ref Unsafe.As(ref a); if (a != 0) { reference = ref Unsafe.As(ref b); } Console.WriteLine(reference); return ref Unsafe.As(ref reference); } public unsafe static byte[] Issue1292(int val, byte[] arr) { fixed (byte* ptr = arr) { *(int*)ptr = val; } return arr; } public unsafe void pin_ptr_test(int[] a, int[] b) { //The blocks IL_0016 are reachable both inside and outside the pinned region starting at IL_0007. ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement. ref int reference; fixed (int* ptr = &a[0]) { if (*ptr <= 0) { Unsafe.AddByteOffset(ref *ptr, 4 * 0) = 1; return; } reference = ref *ptr; } fixed (int* ptr = &b[reference]) { Unsafe.AddByteOffset(ref *ptr, 4 * 0) = 1; } } private static void Issue2148(string[] args) { for (int/*pinned*/ i = 0; i < 100; i++) { Console.WriteLine("Hello World!"); } } private unsafe static void Issue2189() { fixed (int* ptr = &Unsafe.AsRef((int*)Unsafe.AsPointer(ref SomeStruct.instance.mtfhist))) { int num = *ptr; } } private unsafe static void PinUnmanagedPtr(int* A_0) { fixed (int* ptr = &Unsafe.AsRef(A_0)) { int num = *ptr; } } private static ref float AddressTypeMismatch(ref int A_0) { return ref Unsafe.As(ref A_0); } private unsafe static ref float AddressTypeMismatch(int* A_0) { return ref *(float*)A_0; } private static float LoadWithTypeMismatch(ref int A_0) { return Unsafe.As(ref A_0); } private unsafe static float LoadWithTypeMismatch(int* A_0) { return *(float*)A_0; } private static void StoreWithTypeMismatch(ref int A_0) { Unsafe.As(ref A_0) = 1f; } private unsafe static void StoreWithTypeMismatch(int* A_0) { *(float*)A_0 = 1f; } private static ref float AddressOfFieldTypeMismatch(ref int A_0) { return ref Unsafe.As(ref A_0).float_field; } private unsafe static ref float AddressOfFieldTypeMismatch(int* A_0) { return ref ((SomeStruct*)A_0)->float_field; } private static float LoadOfFieldTypeMismatch(ref int A_0) { return Unsafe.As(ref A_0).float_field; } private unsafe static float LoadOfFieldTypeMismatch(int* A_0) { return ((SomeStruct*)A_0)->float_field; } private static void StoreOfFieldTypeMismatch(ref int A_0) { Unsafe.As(ref A_0).float_field = 1f; } private unsafe static void StoreOfFieldTypeMismatch(int* A_0) { ((SomeStruct*)A_0)->float_field = 1f; } } internal struct SomeStruct { public int int_field; public float float_field; } namespace System.Runtime.CompilerServices { public static class Unsafe { [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static T Read(void* source) { return Unsafe.Read(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static T ReadUnaligned(void* source) { return Unsafe.ReadUnaligned(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ReadUnaligned(ref byte source) { return Unsafe.ReadUnaligned(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void Write(void* destination, T value) { Unsafe.Write(destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteUnaligned(void* destination, T value) { Unsafe.WriteUnaligned(destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUnaligned(ref byte destination, T value) { Unsafe.WriteUnaligned(ref destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void Copy(void* destination, ref T source) { Unsafe.Write(destination, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void Copy(ref T destination, void* source) { destination = Unsafe.Read(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void* AsPointer(ref T value) { return Unsafe.AsPointer(ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SkipInit(out T value) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SizeOf() { return Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void CopyBlock(void* destination, void* source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlock(destination, source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyBlock(ref byte destination, ref byte source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlock(ref destination, ref source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(destination, source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(ref destination, ref source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void InitBlock(void* startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlock(startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InitBlock(ref byte startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlock(ref startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlockUnaligned(startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlockUnaligned(ref startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T As(object o) where T : class { return (T)o; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static ref T AsRef(void* source) { return ref *(T*)source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AsRef(in T source) { return ref source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TTo As(ref TFrom source) { return ref Unsafe.As(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Unbox(object box) where T : struct { return ref (T)box; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, int elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void* Add(void* source, int elementOffset) { return (byte*)source + (nint)elementOffset * (nint)Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, IntPtr elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, UIntPtr elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AddByteOffset(ref T source, IntPtr byteOffset) { return ref Unsafe.AddByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AddByteOffset(ref T source, UIntPtr byteOffset) { return ref Unsafe.AddByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, int elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void* Subtract(void* source, int elementOffset) { return (byte*)source - (nint)elementOffset * (nint)Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, IntPtr elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, UIntPtr elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T SubtractByteOffset(ref T source, IntPtr byteOffset) { return ref Unsafe.SubtractByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T SubtractByteOffset(ref T source, UIntPtr byteOffset) { return ref Unsafe.SubtractByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IntPtr ByteOffset(ref T origin, ref T target) { return Unsafe.ByteOffset(target: ref target, origin: ref origin); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool AreSame(ref T left, ref T right) { return Unsafe.AreSame(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAddressGreaterThan(ref T left, ref T right) { return Unsafe.IsAddressGreaterThan(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAddressLessThan(ref T left, ref T right) { return Unsafe.IsAddressLessThan(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static bool IsNullRef(ref T source) { return Unsafe.AsPointer(ref source) == null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static ref T NullRef() { return ref *(T*)null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il ================================================ #define CORE_ASSEMBLY "System.Runtime" .assembly extern CORE_ASSEMBLY { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 4:0:0:0 } .assembly extern System.Console { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) .ver 4:0:0:0 } .assembly System.Runtime.CompilerServices.Unsafe { .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0.. .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0.. .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim 65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic 65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe.. .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim 65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic 65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe.. .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = ( 01 00 15 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b 41 73 73 65 6d 62 6c 79 00 00 00 ) // ".NETFrameworkAssembly", "" .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = ( 01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54 72 75 65 00 00 ) // "Serviceable", "True" .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20 // ../.. Microsoft 43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C // Corporation. Al 6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 // l rights reserve 64 2E 00 00 ) // d... .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 15 4D 69 63 72 6F 73 6F 66 74 20 43 6F 72 // ...Microsoft Cor 70 6F 72 61 74 69 6F 6E 00 00 ) // poration.. .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1A 4D 69 63 72 6F 73 6F 66 74 C2 AE 20 2E // ...Microsoft.. . 4E 45 54 20 46 72 61 6D 65 77 6F 72 6B 00 00 ) // NET Framework.. .custom instance void [CORE_ASSEMBLY]System.CLSCompliantAttribute::.ctor(bool) = ( 01 00 00 00 00 ) // false .hash algorithm 0x00008004 .ver 4:0:5:0 } .module System.Runtime.CompilerServices.Unsafe.dll // MVID: {1E97D84A-565B-49C5-B60A-F31A1A4ACE13} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x02ED0000 // =============== CLASS MEMBERS DECLARATION =================== .class public abstract auto ansi sealed beforefieldinit System.Runtime.CompilerServices.Unsafe extends [CORE_ASSEMBLY]System.Object { .method public hidebysig static !!T Read(void* source) cil managed aggressiveinlining { .maxstack 1 ldarg.0 ldobj !!T ret } // end of method Unsafe::Read .method public hidebysig static !!T ReadUnaligned(void* source) cil managed aggressiveinlining { .maxstack 1 ldarg.0 unaligned. 0x1 ldobj !!T ret } // end of method Unsafe::ReadUnaligned .method public hidebysig static !!T ReadUnaligned(uint8& source) cil managed aggressiveinlining { .maxstack 1 ldarg.0 unaligned. 0x1 ldobj !!T ret } // end of method Unsafe::ReadUnaligned .method public hidebysig static void Write(void* destination, !!T 'value') cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 stobj !!T ret } // end of method Unsafe::Write .method public hidebysig static void WriteUnaligned(void* destination, !!T 'value') cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 unaligned. 0x01 stobj !!T ret } // end of method Unsafe::WriteUnaligned .method public hidebysig static void WriteUnaligned(uint8& destination, !!T 'value') cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 unaligned. 0x01 stobj !!T ret } // end of method Unsafe::WriteUnaligned .method public hidebysig static void Copy(void* destination, !!T& source) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 ldobj !!T stobj !!T ret } // end of method Unsafe::Copy .method public hidebysig static void Copy(!!T& destination, void* source) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 ldobj !!T stobj !!T ret } // end of method Unsafe::Copy .method public hidebysig static void* AsPointer(!!T& 'value') cil managed aggressiveinlining { .maxstack 1 ldarg.0 conv.u ret } // end of method Unsafe::AsPointer .method public hidebysig static void SkipInit ([out] !!T& 'value') cil managed aggressiveinlining { .maxstack 0 ret } // end of method Unsafe::SkipInit .method public hidebysig static int32 SizeOf() cil managed aggressiveinlining { .maxstack 1 sizeof !!T ret } // end of method Unsafe::SizeOf .method public hidebysig static void CopyBlock(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 cpblk ret } // end of method Unsafe::CopyBlock .method public hidebysig static void CopyBlock(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 cpblk ret } // end of method Unsafe::CopyBlock .method public hidebysig static void CopyBlockUnaligned(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 unaligned. 0x1 cpblk ret } // end of method Unsafe::CopyBlockUnaligned .method public hidebysig static void CopyBlockUnaligned(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 unaligned. 0x1 cpblk ret } // end of method Unsafe::CopyBlockUnaligned .method public hidebysig static void InitBlock(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 initblk ret } // end of method Unsafe::InitBlock .method public hidebysig static void InitBlock(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 initblk ret } // end of method Unsafe::InitBlock .method public hidebysig static void InitBlockUnaligned(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 unaligned. 0x1 initblk ret } // end of method Unsafe::InitBlockUnaligned .method public hidebysig static void InitBlockUnaligned(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 ldarg.2 unaligned. 0x1 initblk ret } // end of method Unsafe::InitBlockUnaligned .method public hidebysig static !!T As(object o) cil managed aggressiveinlining { .maxstack 1 ldarg.0 ret } // end of method Unsafe::As .method public hidebysig static !!T& AsRef(void* source) cil managed aggressiveinlining { // For .NET Core the roundtrip via a local is no longer needed see: // https://github.com/dotnet/runtime/issues/8730 // and // https://github.com/dotnet/coreclr/pull/11218 #ifdef netcoreapp .maxstack 1 ldarg.0 ret #else .locals (int32&) .maxstack 1 ldarg.0 // Roundtrip via a local to avoid type mismatch on return that the JIT inliner chokes on. stloc.0 ldloc.0 ret #endif } // end of method Unsafe::AsRef .method public hidebysig static !!T& AsRef(!!T& source) cil managed aggressiveinlining { .param [1] #ifdef netcoreapp .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) #else .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) #endif .maxstack 1 ldarg.0 ret } // end of method Unsafe::AsRef .method public hidebysig static !!TTo& As(!!TFrom& source) cil managed aggressiveinlining { .maxstack 1 ldarg.0 ret } // end of method Unsafe::As .method public hidebysig static !!T& Unbox (object 'box') cil managed aggressiveinlining { .maxstack 1 ldarg.0 unbox !!T ret } // end of method Unsafe::Unbox .method public hidebysig static !!T& Add(!!T& source, int32 elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T conv.i mul add ret } // end of method Unsafe::Add .method public hidebysig static void* Add(void* source, int32 elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T conv.i mul add ret } // end of method Unsafe::Add .method public hidebysig static !!T& Add(!!T& source, native int elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T mul add ret } // end of method Unsafe::Add .method public hidebysig static !!T& Add(!!T& source, native uint elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T mul add ret } // end of method Unsafe::Add .method public hidebysig static !!T& AddByteOffset(!!T& source, native int byteOffset) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 add ret } // end of method Unsafe::AddByteOffset .method public hidebysig static !!T& AddByteOffset(!!T& source, native uint byteOffset) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 add ret } // end of method Unsafe::AddByteOffset .method public hidebysig static !!T& Subtract(!!T& source, int32 elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T conv.i mul sub ret } // end of method Unsafe::Subtract .method public hidebysig static void* Subtract(void* source, int32 elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T conv.i mul sub ret } // end of method Unsafe::Subtract .method public hidebysig static !!T& Subtract(!!T& source, native int elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T mul sub ret } // end of method Unsafe::Subtract .method public hidebysig static !!T& Subtract(!!T& source, native uint elementOffset) cil managed aggressiveinlining { .maxstack 3 ldarg.0 ldarg.1 sizeof !!T mul sub ret } // end of method Unsafe::Subtract .method public hidebysig static !!T& SubtractByteOffset(!!T& source, native int byteOffset) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 sub ret } // end of method Unsafe::SubtractByteOffset .method public hidebysig static !!T& SubtractByteOffset(!!T& source, native uint byteOffset) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 sub ret } // end of method Unsafe::SubtractByteOffset .method public hidebysig static native int ByteOffset(!!T& origin, !!T& target) cil managed aggressiveinlining { .maxstack 2 ldarg.1 ldarg.0 sub ret } // end of method Unsafe::ByteOffset .method public hidebysig static bool AreSame(!!T& left, !!T& right) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 ceq ret } // end of method Unsafe::AreSame .method public hidebysig static bool IsAddressGreaterThan(!!T& left, !!T& right) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 cgt.un ret } // end of method Unsafe::IsAddressGreaterThan .method public hidebysig static bool IsAddressLessThan(!!T& left, !!T& right) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldarg.1 clt.un ret } // end of method Unsafe::IsAddressLessThan .method public hidebysig static bool IsNullRef(!!T& source) cil managed aggressiveinlining { .maxstack 2 ldarg.0 ldc.i4.0 conv.u ceq ret } // end of method Unsafe::IsNullRef .method public hidebysig static !!T& NullRef() cil managed aggressiveinlining { .maxstack 1 ldc.i4.0 conv.u ret } // end of method Unsafe::NullRef } // end of class System.Runtime.CompilerServices.Unsafe #ifdef netcoreapp #else .class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute extends [CORE_ASSEMBLY]System.Attribute { .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [CORE_ASSEMBLY]System.Attribute::.ctor() IL_0006: ret } // end of method EmbeddedAttribute::.ctor } // end of class Microsoft.CodeAnalysis.EmbeddedAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute extends [CORE_ASSEMBLY]System.Attribute { .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 1 ldarg.0 call instance void [CORE_ASSEMBLY]System.Attribute::.ctor() ret } // end of method IsReadOnlyAttribute::.ctor } // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute #endif .class private auto ansi sealed beforefieldinit ExtraUnsafeTests extends [CORE_ASSEMBLY]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 1 ldarg.0 call instance void [CORE_ASSEMBLY]System.Object::.ctor() ret } // end of method ExtraUnsafeTests::.ctor .method public hidebysig static void PinWithTypeMismatch(uint32& managedPtr) { .maxstack 8 .locals ( [0] uint16& pinned ) // Pin: ldarg.0 stloc.0 // Unpin: ldc.i4 0 stloc.0 ret } .method public hidebysig static uint32* RefToPointerWithoutPinning(uint32& managedPtr) { .maxstack 8 ldarg.0 ret } .method public hidebysig static uint64& RefAssignTypeMismatch(uint32& a, uint32& b) { .maxstack 8 .locals ( [0] uint16& ) ldarg.0 stloc.0 ldarg.0 ldind.i4 brfalse lbl ldarg.1 stloc.0 lbl: ldloc.0 ldind.i2 call void [System.Console]System.Console::WriteLine(uint16) ldloc.0 ret } .method public hidebysig static uint8[] Issue1292 ( int32 val, uint8[] arr ) cil managed { .maxstack 2 .locals init ( [0] uint8[], [1] uint8[], [2] uint8& pinned ) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: dup IL_0004: stloc.1 IL_0005: brfalse.s IL_0016 IL_0007: ldloc.1 IL_0008: ldlen IL_0009: conv.i4 IL_000a: brfalse.s IL_0016 IL_000c: ldloc.1 IL_000d: ldc.i4.0 IL_000e: ldelema [System.Runtime]System.Byte IL_0013: stloc.2 IL_0014: br.s IL_0019 IL_0016: ldc.i4.0 IL_0017: conv.u IL_0018: stloc.2 IL_0019: ldloc.2 IL_001a: conv.i IL_001b: ldarg.0 IL_001c: stind.i4 IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.2 IL_0020: ldloc.0 IL_0021: ret } // end of method Issue1292 .method /* 06000066 */ public hidebysig instance void pin_ptr_test ( int32[] a, int32[] b ) cil managed { /* From C++/CLI: void pin_ptr_test(array^ a, array^ b) { pin_ptr p = &a[0]; if (*p > 0) { p = &b[*p]; } p[0] = 1; } */ .maxstack 3 .locals /* 11000004 */ ( [0] int32& pinned modopt([System.Runtime]System.Runtime.CompilerServices.IsExplicitlyDereferenced) p ) IL_0000: ldarg.1 IL_0001: ldc.i4.0 IL_0002: ldelema [System.Runtime]System.Int32 /* 01000016 */ IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldind.i4 IL_000a: ldc.i4.0 IL_000b: ble.s IL_0016 IL_000d: ldarg.2 IL_000e: ldloc.0 IL_000f: ldind.i4 IL_0010: ldelema [System.Runtime]System.Int32 /* 01000016 */ IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ldc.i4.4 IL_0018: ldc.i4.0 IL_0019: mul IL_001a: add IL_001b: ldc.i4.1 IL_001c: stind.i4 IL_001d: ret } // end of method pin_ptr_test .method private hidebysig static void Issue2148 ( string[] args ) cil managed { // Header Size: 12 bytes // Code Size: 24 (0x18) bytes // LocalVarSig Token: 0x11000001 RID: 1 .maxstack 2 .locals init ( [0] int32 pinned ) /* 0x00000264 16 */ IL_0000: ldc.i4.0 /* 0x00000265 0A */ IL_0001: stloc.0 /* 0x00000266 2B0E */ IL_0002: br.s IL_0012 // loop start (head: IL_0012) /* 0x00000268 7201000070 */ IL_0004: ldstr "Hello World!" /* 0x0000026D 280B00000A */ IL_0009: call void [System.Console]System.Console::WriteLine(string) /* 0x00000272 06 */ IL_000E: ldloc.0 /* 0x00000273 17 */ IL_000F: ldc.i4.1 /* 0x00000274 58 */ IL_0010: add /* 0x00000275 0A */ IL_0011: stloc.0 /* 0x00000276 06 */ IL_0012: ldloc.0 /* 0x00000277 1F64 */ IL_0013: ldc.i4.s 100 /* 0x00000279 32ED */ IL_0015: blt.s IL_0004 // end loop /* 0x0000027B 2A */ IL_0017: ret } // end of method Issue2148 .method private hidebysig static void Issue2189 () cil managed { .maxstack 2 .locals init ( [0] int32, [1] int32& pinned ) IL_0000: ldsflda valuetype [CORE_ASSEMBLY]SomeStruct [CORE_ASSEMBLY]SomeStruct::'instance' IL_0005: ldflda uint32 [CORE_ASSEMBLY]SomeStruct::mtfhist IL_000a: conv.u IL_000b: stloc.1 ldloc.1 ldind.i4 stloc.0 IL_0185: ldc.i4.0 IL_0186: conv.i IL_0187: stloc.1 IL_0188: ret } // end of method Issue2189 .method private hidebysig static void PinUnmanagedPtr (int32*) cil managed { .maxstack 2 .locals init ( [0] int32, [1] int32& pinned ) ldarg.0 stloc.1 ldloc.1 ldind.i4 stloc.0 ldc.i4.0 conv.i stloc.1 ret } // end of method Issue2189 .method private hidebysig static float32& AddressTypeMismatch (int32&) cil managed { ldarg.0 ret } .method private hidebysig static float32& AddressTypeMismatch (int32*) cil managed { ldarg.0 ret } .method private hidebysig static float32 LoadWithTypeMismatch (int32&) cil managed { ldarg.0 ldind.r4 ret } .method private hidebysig static float32 LoadWithTypeMismatch (int32*) cil managed { ldarg.0 ldind.r4 ret } .method private hidebysig static void StoreWithTypeMismatch (int32&) cil managed { ldarg.0 ldc.r4 1 stind.r4 ret } .method private hidebysig static void StoreWithTypeMismatch (int32*) cil managed { ldarg.0 ldc.r4 1 stind.r4 ret } .method private hidebysig static float32& AddressOfFieldTypeMismatch (int32&) cil managed { ldarg.0 ldflda float32 SomeStruct::float_field ret } .method private hidebysig static float32& AddressOfFieldTypeMismatch (int32*) cil managed { ldarg.0 ldflda float32 SomeStruct::float_field ret } .method private hidebysig static float32 LoadOfFieldTypeMismatch (int32&) cil managed { ldarg.0 ldfld float32 SomeStruct::float_field ret } .method private hidebysig static float32 LoadOfFieldTypeMismatch (int32*) cil managed { ldarg.0 ldfld float32 SomeStruct::float_field ret } .method private hidebysig static void StoreOfFieldTypeMismatch (int32&) cil managed { ldarg.0 ldc.r4 1 stfld float32 SomeStruct::float_field ret } .method private hidebysig static void StoreOfFieldTypeMismatch (int32*) cil managed { ldarg.0 ldc.r4 1 stfld float32 SomeStruct::float_field ret } } // class ExtraUnsafeTests .class private sequential ansi sealed beforefieldinit SomeStruct extends [mscorlib]System.ValueType { // Fields .field public int32 int_field .field public float32 float_field } // end of class SomeStruct ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.cs ================================================ using System; namespace TestEnum { public enum BooleanEnum : bool { Min = false, Zero = false, One = true, Max = byte.MaxValue } public enum EnumWithNestedClass { #pragma warning disable format // error: nested types are not permitted in C#. public class NestedClass { } , #pragma warning enable format Zero, One } public enum NativeIntEnum : IntPtr { Zero = 0L, One = 1L, FortyTwo = 42L } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly BoolEnum { .ver 1:0:0:0 } .module BoolEnum.exe .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00020003 // ILONLY 32BITPREFERRED // Image base: 0x000001C4B6C90000 .class public auto ansi sealed TestEnum.BooleanEnum extends [mscorlib]System.Enum { .field public specialname rtspecialname bool value__ .field public static literal valuetype TestEnum.BooleanEnum Min = bool(false) .field public static literal valuetype TestEnum.BooleanEnum Zero = bool(false) .field public static literal valuetype TestEnum.BooleanEnum One = bool(true) .field public static literal valuetype TestEnum.BooleanEnum Max = uint8(255) } .class public auto ansi sealed TestEnum.NativeIntEnum extends [mscorlib]System.Enum { .field public specialname rtspecialname native int value__ .field public static literal valuetype TestEnum.NativeIntEnum Zero = int64(0) .field public static literal valuetype TestEnum.NativeIntEnum One = int64(1) .field public static literal valuetype TestEnum.NativeIntEnum FortyTwo = int64(42) } .class nested public auto ansi sealed TestEnum.EnumWithNestedClass extends [System.Runtime]System.Enum { // Nested Types .class nested public auto ansi beforefieldinit NestedClass extends [mscorlib]System.Object { // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206c // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method TestEnum.NestedClass::.ctor } // end of class NestedClass // Fields .field public specialname rtspecialname int32 value__ .field public static literal valuetype TestEnum.EnumWithNestedClass Zero = int32(0) .field public static literal valuetype TestEnum.EnumWithNestedClass One = int32(1) } // end of class EnumWithNestedClass ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore ================================================ /*.dll /*.pdb /*.expected.xml /*.generated.xml ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml ================================================ ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml ================================================ ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml ================================================ ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml ================================================ a + b + captured); } private static void Test(Func p) { p(1, 2); } } ]]> ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml ================================================ 42; private int Property { get { return 0; } set { } } private C this[int index] => null; private C this[string s] { get { return null; } set { } } public event Action Event { add { } remove { } } public static implicit operator C(int i) { return null; } static C() { } public C() { Console.WriteLine(); } ~C() { } void IDisposable.Dispose() { } private static void Main() { C c = new C(); c.Event += delegate { }; _ = c.Property; _ = c.ExpressionProperty; _ = c[0]; _ = c[""]; _ = (C)1; Local(); static void Local() { Console.WriteLine(); } } } ]]> ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ProgressReporting.xml ================================================ ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/.gitignore ================================================ /*.res /*.dll /*.exe /*.pdb /*.xml ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AnonymousTypes.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class AnonymousTypes { private void SimpleTypes() { var value = new { }; var anon = new { X = 5 }; var anon2 = new { X = 5, Y = 10 }; Console.WriteLine(value); Console.WriteLine(anon.X); Console.WriteLine(anon2.Y + anon2.X); } private void SimpleArray() { #if ROSLYN && OPT var obj = new[] { #else var array = new[] { #endif new { X = 5, Y = 2, Z = -1 }, new { X = 3, Y = 6, Z = -6 } }; #if ROSLYN && OPT Console.WriteLine(obj[0].X); Console.WriteLine(obj[1].X); #else Console.WriteLine(array[0].X); Console.WriteLine(array[1].X); #endif } #if !MCS private void JaggedArray() { var array = new[] { new { X = 5, Y = 2, Z = -1 }, new { X = 3, Y = 6, Z = -6 } }; #if ROSLYN && OPT var obj = new[] { array, array }; Console.WriteLine(array[0].X); Console.WriteLine(array[1].X); Console.WriteLine(obj.Length); #else var array2 = new[] { array, array }; Console.WriteLine(array[0].X); Console.WriteLine(array[1].X); Console.WriteLine(array2.Length); #endif } #endif #if CS70 private void AnonymousTypeOutVar() { InlineVarDecl(out var v, new { X = 1, Y = 2 }); Console.WriteLine(v.X); } #endif private static void InlineVarDecl(out T v, T init) { v = init; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AssemblyCustomAttributes.cs ================================================ // Copyright (c) 2014 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 // 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; [assembly: CLSCompliant(false)] //[module: CLSCompliant(false)] ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs ================================================ // Copyright (c) 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 // 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. #pragma warning disable 1998 using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Async { private int memberField; private static bool True() { return true; } public async void SimpleVoidMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); } public async void VoidMethodWithoutAwait() { Console.WriteLine("No Await"); } public async void EmptyVoidMethod() { } public async void AwaitYield() { await Task.Yield(); } public async void AwaitDefaultYieldAwaitable() { await default(YieldAwaitable); } public async void AwaitDefaultHopToThreadPool() { // unlike YieldAwaitable which implements ICriticalNotifyCompletion, // the HopToThreadPoolAwaitable struct only implements // INotifyCompletion, so this results in different codegen await default(HopToThreadPoolAwaitable); } public async Task SimpleVoidTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); } public async Task TaskMethodWithoutAwait() { Console.WriteLine("No Await"); } public async Task CapturingThis() { await Task.Delay(memberField); } public async Task CapturingThisWithoutAwait() { Console.WriteLine(memberField); } public async Task SimpleBoolTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); return true; } public async void TwoAwaitsWithDifferentAwaiterTypes() { Console.WriteLine("Before"); if (await SimpleBoolTaskMethod()) { await Task.Delay(TimeSpan.FromSeconds(1.0)); } Console.WriteLine("After"); } public async void AwaitInLoopCondition() { while (await SimpleBoolTaskMethod()) { Console.WriteLine("Body"); } } #if CS60 public async Task AwaitInCatch(bool b, Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception) { if (!b) { await task2; } else { Console.WriteLine("No await"); } } } public async Task AwaitInFinally(bool b, Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } finally { if (!b) { await task2; } else { Console.WriteLine("No await"); } } } public async Task AnonymousThrow() { try { await Task.Delay(0); } catch { await Task.Delay(0); throw; } } public async Task DeclaredException() { try { await Task.Delay(0); } catch (Exception) { await Task.Delay(0); throw; } } public async Task RethrowDeclared() { try { await Task.Delay(0); } catch (Exception ex) { await Task.Delay(0); #pragma warning disable CA2200 // Rethrow to preserve stack details throw ex; #pragma warning restore CA2200 // Rethrow to preserve stack details } } public async Task RethrowDeclaredWithFilter() { try { await Task.Delay(0); } catch (Exception ex) when (ex.GetType().FullName.Contains("asdf")) { await Task.Delay(0); throw; } } public async Task ComplexCatchBlock() { try { await Task.Delay(0); } catch (Exception ex) { if (ex.GetHashCode() != 0) { throw; } await Task.Delay(0); } } public async Task ComplexCatchBlockWithFilter() { try { await Task.Delay(0); } catch (Exception ex) when (ex.GetType().FullName.Contains("asdf")) { if (ex.GetHashCode() != 0) { throw; } await Task.Delay(0); } } public async Task LoadsToCatch(int i) { try { throw null; } catch (Exception ex) when (i == 0) { Console.WriteLine("First!"); if (i == 1) { throw; } await Task.Yield(); Console.WriteLine(ex.StackTrace); } catch (Exception ex2) when (True()) { Console.WriteLine("Second!"); if (i == 1) { throw; } await Task.Yield(); Console.WriteLine(ex2.StackTrace); } catch (Exception ex3) { Console.WriteLine("Third!"); if (i == 1) { throw; } await Task.Yield(); Console.WriteLine(ex3.StackTrace); } catch when (i == 0) { Console.WriteLine("Fourth!"); if (i == 1) { throw; } await Task.Yield(); } catch when (True()) { Console.WriteLine("Fifth!"); if (i == 1) { throw; } await Task.Yield(); } catch { Console.WriteLine("Sixth!"); if (i == 1) { throw; } await Task.Yield(); } } public async Task Issue2366a() { while (true) { try { await Task.CompletedTask; } catch { } } } public async Task Issue2366b() { try { await Task.CompletedTask; } catch (NullReferenceException) { await Task.CompletedTask; } catch (InvalidOperationException) { await Task.CompletedTask; } catch (ArgumentException) { await Task.CompletedTask; } } public async Task Issue2436() { try { Console.WriteLine(); } catch (Exception result) { return result; } finally { await Task.CompletedTask; } return new object(); } #endif public static async Task GetIntegerSumAsync(IEnumerable items) { await Task.Delay(100); int num = 0; foreach (int item in items) { num += item; } return num; } public static Func> AsyncLambda() { return async () => await GetIntegerSumAsync(new int[3] { 1, 2, 3 }); } public static Func> AsyncDelegate() { return async delegate { await Task.Delay(10); return 2; }; } public static async Task AlwaysThrow() { throw null; } public static async Task InfiniteLoop() { while (true) { } } public static async Task InfiniteLoopWithAwait() { while (true) { await Task.Delay(10); } } public async Task AsyncWithLocalVar() { object a = new object(); #if CS70 (object, string) tuple = (new object(), "abc"); #endif await UseObj(a); await UseObj(a); #if CS70 await UseObj(tuple); #endif } public static async Task UseObj(object a) { } #if CS70 public static async Task AsyncLocalFunctions() { return await Nested(1) + await Nested(2); #if CS80 static async Task Nested(int i) #else async Task Nested(int i) #endif { await Task.Delay(i); return i; } } #endif } public struct AsyncInStruct { private int i; public async Task Test(AsyncInStruct xx) { xx.i++; i++; await Task.Yield(); return i + xx.i; } } public struct HopToThreadPoolAwaitable : INotifyCompletion { public bool IsCompleted { get; set; } public HopToThreadPoolAwaitable GetAwaiter() { return this; } public void OnCompleted(Action continuation) { Task.Run(continuation); } public void GetResult() { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncForeach.cs ================================================ // Copyright (c) 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 // 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.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class AsyncForeach { public async Task SumIntegers(IAsyncEnumerable items, CancellationToken token) { int sum = 0; await foreach (int item in items.WithCancellation(token)) { if (!token.IsCancellationRequested) { sum += item; continue; } break; } return sum; } public async Task MaxInteger(IAsyncEnumerable items) { int max = int.MinValue; await foreach (int item in items) { if (item > max) { max = item; } } return max; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncMain.cs ================================================ using System; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class AsyncMain { public static async Task Main(string[] args) { await Task.Delay(1000); Console.WriteLine("Hello Wolrd!"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class AsyncStreams { public static async IAsyncEnumerable CountTo(int until) { for (int i = 0; i < until; i++) { yield return i; await Task.Delay(10); } } public static async IAsyncEnumerable AlwaysThrow() { throw null; yield break; } public static async IAsyncEnumerator InfiniteLoop() { while (true) { } yield break; } public static async IAsyncEnumerable InfiniteLoopWithAwait() { while (true) { await Task.Delay(10); } yield break; } public async IAsyncEnumerable AwaitInFinally() { try { Console.WriteLine("try"); yield return 1; Console.WriteLine("end try"); } finally { Console.WriteLine("finally"); await Task.Yield(); Console.WriteLine("end finally"); } } public static async IAsyncEnumerable SimpleCancellation([EnumeratorCancellation] CancellationToken cancellationToken) { yield return 1; await Task.Delay(100, cancellationToken); yield return 2; } } public struct TestStruct { private int i; public async IAsyncEnumerable AwaitInStruct(TestStruct xx) { xx.i++; i++; await Task.Yield(); yield return i; yield return xx.i; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncUsing.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class AsyncUsing { internal class AsyncDisposableClass : IAsyncDisposable { public ValueTask DisposeAsync() { throw new NotImplementedException(); } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct AsyncDisposableStruct : IAsyncDisposable { public ValueTask DisposeAsync() { throw new NotImplementedException(); } } public static async void TestAsyncUsing(IAsyncDisposable disposable) { await using (disposable) { Console.WriteLine("Hello"); } } public static async void TestAsyncUsingClass() { await using (AsyncDisposableClass test = new AsyncDisposableClass()) { Use(test); } } public static async void TestAsyncUsingStruct() { await using (AsyncDisposableStruct asyncDisposableStruct = default(AsyncDisposableStruct)) { Use(asyncDisposableStruct); } } public static async void TestAsyncUsingNullableStruct() { await using (AsyncDisposableStruct? asyncDisposableStruct = new AsyncDisposableStruct?(default(AsyncDisposableStruct))) { Use(asyncDisposableStruct); } } private static void Use(IAsyncDisposable test) { throw new NotImplementedException(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/AutoProperties.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class AutoProperties { #if CS110 public required int RequiredField; #endif public int A { get; } = 1; public int B { get; set; } = 2; public static int C { get; } = 3; public static int D { get; set; } = 4; public string value { get; set; } [Obsolete("Property")] #if CS70 [field: Obsolete("Field")] #endif public int PropertyWithAttributeOnBackingField { get; set; } public int issue1319 { get; } #if CS110 public required int RequiredProperty { get; set; } #endif public AutoProperties(int issue1319) { this.issue1319 = issue1319; #if CS110 RequiredProperty = 42; RequiredField = 42; #endif } } #if !NET70 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute { } #endif } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS72_PrivateProtected.cs ================================================ // Copyright (c) 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 // 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. namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class CS72_PrivateProtected { private protected int Property { get; } private protected void Method() { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs ================================================ #pragma warning disable format // 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.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class CS73_StackAllocInitializers { #if CS120 [StructLayout(LayoutKind.Sequential, Size = 5)] private struct StructWithSize5(byte a, byte b, byte c, byte d, byte e) { public byte a = a; public byte b = b; public byte c = c; public byte d = d; public byte e = e; } #else [StructLayout(LayoutKind.Sequential, Size = 5)] private struct StructWithSize5 { public byte a; public byte b; public byte c; public byte d; public byte e; public StructWithSize5(byte a, byte b, byte c, byte d, byte e) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; } } #endif #if CS80 private class NestedContext1 { public NestedContext1(object result) { } public NestedContext1() : this(UseNested(GetInt(), stackalloc int[2] { GetInt(), GetInt() })) { } } public static object UseNested(object a, Span span) { return null; } public static int GetInt() { return 42; } #endif public unsafe string SimpleStackAllocStruct1() { StructWithSize5* ptr = stackalloc StructWithSize5[4] { new StructWithSize5(1, 2, 3, 4, 5), new StructWithSize5(11, 22, 33, 44, 55), new StructWithSize5(1, 4, 8, 6, 2), new StructWithSize5(12, 23, 34, 45, 56) }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocBool() { bool* ptr = stackalloc bool[4] { false, true, false, true }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string DoNotInlineTest() { bool* ptr = stackalloc bool[4] { false, true, false, true }; return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocByte() { byte* ptr = stackalloc byte[2] { 0, 1 }; Console.WriteLine(*ptr); return UsePointer(ptr); } public unsafe string SimpleStackAllocPrimesAsBytes() { byte* ptr = stackalloc byte[55] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251 }; Console.WriteLine(*ptr); return UsePointer(ptr); } public unsafe string SimpleStackAllocChar() { char* ptr = stackalloc char[4] { '1', '2', '3', '4' }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocCharAlphabet() { char* ptr = stackalloc char[26] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocSByte() { sbyte* ptr = stackalloc sbyte[3] { 1, 2, 3 }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt16() { short* ptr = stackalloc short[3] { 1, 2, 3 }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocUInt16() { ushort* ptr = stackalloc ushort[3] { 1, 2, 3 }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt32() { int* ptr = stackalloc int[3] { 1, 2, 3 }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt32(int a, int b, int c) { int* ptr = stackalloc int[6] { 1, a, 2, b, 3, c }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt32Fibonacci() { int* ptr = stackalloc int[17] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocUInt32() { uint* ptr = stackalloc uint[3] { 1u, 2u, 3u }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt64() { long* ptr = stackalloc long[3] { 1L, 2L, 3L }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocUInt64() { ulong* ptr = stackalloc ulong[3] { 1uL, 2uL, 3uL }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string SimpleStackAllocInt32NonConstant(int a, int b, int c) { int* ptr = stackalloc int[6] { 0, 1, 0, a, b, c }; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string NotAnInitializer(int a, int b, int c) { int* ptr = stackalloc int[6]; ptr[1] = a; ptr[3] = b; ptr[5] = c; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); } public unsafe string NegativeOffsets(int a, int b, int c) { #if OPT byte* num = stackalloc byte[12]; *(int*)num = 1; *((int*)num - 1) = 2; *((int*)num - 2) = 3; int* ptr = (int*)num; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); #else byte* ptr = stackalloc byte[12]; *(int*)ptr = 1; *((int*)ptr - 1) = 2; *((int*)ptr - 2) = 3; int* ptr2 = (int*)ptr; Console.WriteLine(*ptr2); return UsePointer((byte*)ptr2); #endif } public unsafe string UsePointer(byte* ptr) { return ptr->ToString(); } public string GetSpan() { Span span = stackalloc int[GetSize()]; return UseSpan(span); } public string GetSpan2() { Span span = stackalloc int[4] { 1, 2, 3, 4 }; return UseSpan(span); } public string GetSpan3() { Span span = stackalloc decimal[GetSize()]; return UseSpan(span); } public string GetSpan4() { Span span = stackalloc decimal[4] { 1m, 2m, 3m, 4m }; return UseSpan(span); } public void Issue2103a() { Span span = stackalloc byte[3] { 1, 2, 3 }; Console.WriteLine(span[2] + span[0]); } public void Issue2103b() { Span span = stackalloc byte[3]; Console.WriteLine(span[0] + span[1]); } public void Issue2103c() { Console.WriteLine((stackalloc byte[3] { 1, 2, 3 })[2]); } public void Issue2103d() { Console.WriteLine((stackalloc byte[3])[1]); } public string UseSpan(Span span) { throw new NotImplementedException(); } public string UseSpan(Span span) { throw new NotImplementedException(); } public int GetSize() { throw new NotImplementedException(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS9_ExtensionGetEnumerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class CS9_ExtensionGetEnumerator { public class NonGeneric { } public class Generic { } public void Test(NonGeneric c) { foreach (object item in c) { Console.WriteLine(item); } } public void Test(Generic c) { foreach (int item in c) { Console.WriteLine(item); } } #if !NET40 public async void TestAsync(Generic c) { await foreach (int item in c) { Console.WriteLine(item); } } #endif } public static class CS9_ExtensionGetEnumerator_Ext { public static IEnumerator GetEnumerator(this CS9_ExtensionGetEnumerator.NonGeneric c) { throw null; } public static IEnumerator GetEnumerator(this CS9_ExtensionGetEnumerator.Generic c) { throw null; } #if !NET40 public static IAsyncEnumerator GetAsyncEnumerator(this CS9_ExtensionGetEnumerator.Generic c) { throw null; } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CheckedUnchecked.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Box { public readonly T Value; } public class CheckedUnchecked { public int Operators(int a, int b) { int num = checked(a + b); int num2 = a + b; int num3 = checked(a - b); int num4 = a - b; int num5 = checked(a * b); int num6 = a * b; int num7 = a / b; int num8 = a % b; // The division operators / and % only exist in one form (checked vs. unchecked doesn't matter for them) return num * num2 * num3 * num4 * num5 * num6 * num7 * num8; } public int Cast(int a) { short num = checked((short)a); short num2 = (short)a; byte b = checked((byte)a); byte b2 = (byte)a; return num * num2 * b * b2; } public void ForWithCheckedIteratorAndUncheckedBody(int n) { checked { for (int i = n + 1; i < n + 1; i++) { n = unchecked(i * i); } } } public void ForWithCheckedInitializerAndUncheckedIterator(int n) { int num = n; for (num = checked(num - 10); num < n; num++) { n--; } } public void ObjectCreationInitializerChecked() { TestHelp(new { x = 0, l = 0 }, n => checked(new { x = n.x + 1, l = n.l + 1 })); } public void ObjectCreationWithOneFieldChecked() { TestHelp(new { x = 0, l = 0 }, n => new { x = checked(n.x + 1), l = n.l + 1 }); } public void ArrayInitializerChecked() { TestHelp(new int[2] { 1, 2 }, (int[] n) => checked(new int[2] { n[0] + 1, n[1] + 1 })); } public T TestHelp(T t, Func f) { return f(t); } public void CheckedInArrayCreationArgument(int a, int b) { Console.WriteLine(new int[checked(a + b)]); } public short Unbox(TypeCode c, object b) { checked { switch (c) { case TypeCode.Int32: return (short)((Box)b).Value; case TypeCode.UInt32: return (short)((Box)b).Value; case TypeCode.Double: { float num = (float)((Box)b).Value; Console.WriteLine(num); return (short)num; } default: throw new Exception(); } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Comparisons.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Comparisons { private class A { } private class B { } private bool CompareUnrelatedNeedsCast(A a, B b) { return (object)a == b; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class CompoundAssignmentTest { [Flags] private enum MyEnum { None = 0, One = 1, Two = 2, Four = 4 } public enum ShortEnum : short { None = 0, One = 1, Two = 2, Four = 4 } private struct StructContainer { public bool HasIndex; public int Field; } public class MutableClass { public int Field; public short ShortField; public int Property { get; set; } public byte ByteProperty { get; set; } public bool BoolProperty { get; set; } public uint this[string name] { get { return 0u; } set { } } } private class Item { public Item Self; } public class CustomClass { public byte ByteField; public sbyte SbyteField; public short ShortField; public ushort UshortField; public int IntField; public uint UintField; public long LongField; public ulong UlongField; public CustomClass CustomClassField; public CustomStruct CustomStructField; public byte ByteProp { get; set; } public sbyte SbyteProp { get; set; } public short ShortProp { get; set; } public ushort UshortProp { get; set; } public int IntProp { get; set; } public uint UintProp { get; set; } public long LongProp { get; set; } public ulong UlongProp { get; set; } public string StringProp { get; set; } public CustomClass CustomClassProp { get; set; } public CustomStruct CustomStructProp { get; set; } public static CustomClass operator +(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator +(CustomClass lhs, int rhs) { throw new NotImplementedException(); } public static CustomClass operator -(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator *(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator /(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator %(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator <<(CustomClass lhs, int rhs) { throw new NotImplementedException(); } public static CustomClass operator >>(CustomClass lhs, int rhs) { throw new NotImplementedException(); } public static CustomClass operator &(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator |(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator ^(CustomClass lhs, CustomClass rhs) { throw new NotImplementedException(); } public static CustomClass operator ++(CustomClass lhs) { throw new NotImplementedException(); } public static CustomClass operator --(CustomClass lhs) { throw new NotImplementedException(); } } public struct CustomStruct { public byte ByteField; public sbyte SbyteField; public short ShortField; public ushort UshortField; public int IntField; public uint UintField; public long LongField; public ulong UlongField; public CustomClass CustomClassField; public CustomClass CustomClassProp { get; set; } public byte ByteProp { get; set; } public sbyte SbyteProp { get; set; } public short ShortProp { get; set; } public ushort UshortProp { get; set; } public int IntProp { get; set; } public uint UintProp { get; set; } public long LongProp { get; set; } public ulong UlongProp { get; set; } public static CustomStruct operator +(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator -(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator *(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator /(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator %(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator <<(CustomStruct lhs, int rhs) { throw new NotImplementedException(); } public static CustomStruct operator >>(CustomStruct lhs, int rhs) { throw new NotImplementedException(); } #if CS110 public static CustomStruct operator >>>(CustomStruct lhs, int rhs) { throw new NotImplementedException(); } #endif public static CustomStruct operator &(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator |(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator ^(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); } public static CustomStruct operator ++(CustomStruct lhs) { throw new NotImplementedException(); } public static CustomStruct operator --(CustomStruct lhs) { throw new NotImplementedException(); } } public struct CustomStruct2 { public CustomClass CustomClassField; public CustomStruct CustomStructField; public byte ByteField; public sbyte SbyteField; public short ShortField; public ushort UshortField; public int IntField; public uint UintField; public long LongField; public ulong UlongField; public CustomClass CustomClassProp { get; set; } public CustomStruct CustomStructProp { get; set; } public byte ByteProp { get; set; } public sbyte SbyteProp { get; set; } public short ShortProp { get; set; } public ushort UshortProp { get; set; } public int IntProp { get; set; } public uint UintProp { get; set; } public long LongProp { get; set; } public ulong UlongProp { get; set; } } private int test1; private int[] array1; private StructContainer field1; private MyEnum enumField; private Dictionary ushortDict = new Dictionary(); private ShortEnum shortEnumField; public static int StaticField; public static short StaticShortField; private static CustomClass customClassField; private static CustomStruct customStructField; private static CustomStruct2 otherCustomStructField; private static byte byteField; private static sbyte sbyteField; private static short shortField; private static ushort ushortField; private static int intField; private static uint uintField; private static long longField; private static ulong ulongField; private static CustomClass CustomClassProp { get; set; } private static CustomStruct CustomStructProp { get; set; } private static byte ByteProp { get; set; } private static sbyte SbyteProp { get; set; } private static short ShortProp { get; set; } private static ushort UshortProp { get; set; } private static int IntProp { get; set; } private static uint UintProp { get; set; } private static long LongProp { get; set; } private static ulong UlongProp { get; set; } public static int StaticProperty { get; set; } public static ShortEnum StaticShortProperty { get; set; } public static string StaticStringProperty { get; set; } private static void Use(ref byte b) { } private static void Use(ref sbyte b) { } private static void Use(ref T num) { } private static CustomStruct2 GetStruct() { throw new NotImplementedException(); } #if CS70 private static ref CustomStruct2 GetRefStruct() { throw new NotImplementedException(); } private static ref CustomStruct GetRefCustomStruct() { throw new NotImplementedException(); } private static ref CustomClass GetRefCustomClass() { throw new NotImplementedException(); } private static ref byte GetRefByte() { throw new NotImplementedException(); } private static ref sbyte GetRefSbyte() { throw new NotImplementedException(); } private static ref short GetRefShort() { throw new NotImplementedException(); } private static ref int GetRefInt() { throw new NotImplementedException(); } private static ref long GetRefLong() { throw new NotImplementedException(); } private static ref ushort GetRefUshort() { throw new NotImplementedException(); } private static ref uint GetRefUint() { throw new NotImplementedException(); } private static ref ulong GetRefUlong() { throw new NotImplementedException(); } #endif private static CustomClass GetClass() { throw new NotImplementedException(); } private static void X(T result) { } private MutableClass M() { return new MutableClass(); } private int[,] Array() { return null; } private unsafe int* GetPointer() { return null; } public int GetIndex() { return new Random().Next(0, 100); } public int[] GetArray() { throw new NotImplementedException(); } public int GetValue(int value) { return value; } public bool IsUpperCaseA(char a) { return a == 'A'; } public void Int32_Local_Add(int i) { i++; Console.WriteLine(i++); Console.WriteLine(++i); i += 5; Console.WriteLine(i += 5); } public void Int32_Local_Sub(int i) { i--; Console.WriteLine(i--); Console.WriteLine(--i); i -= 5; Console.WriteLine(i -= 5); } public void Int32_Local_Mul(int i) { i *= 5; Console.WriteLine(i *= 5); } public void Int32_Local_Div(int i) { i /= 5; Console.WriteLine(i /= 5); } public void Int32_Local_Rem(int i) { i %= 5; Console.WriteLine(i %= 5); } public void Int32_Local_BitAnd(int i) { i &= 5; Console.WriteLine(i &= 5); } public void Int32_Local_BitOr(int i) { i |= 5; Console.WriteLine(i |= 5); } public void Int32_Local_BitXor(int i) { i ^= 5; Console.WriteLine(i ^= 5); } public void Int32_Local_ShiftLeft(int i) { i <<= 5; Console.WriteLine(i <<= 5); } public void Int32_Local_ShiftRight(int i) { i >>= 5; Console.WriteLine(i >>= 5); } public void IntegerWithInline(int i) { Console.WriteLine(i += 5); Console.WriteLine(i); } public void IntegerField(int i) { Console.WriteLine(test1 += i); Console.WriteLine(test1); Console.WriteLine(test1 -= i); Console.WriteLine(test1); } public void Array(int i) { Console.WriteLine(array1[i] += i); Console.WriteLine(array1[i * 2] += i * 2); } public int ArrayUsageWithMethods() { return GetArray()[GetIndex()]++; } public void NestedField() { if (field1.HasIndex) { Console.WriteLine(field1.Field *= 2); field1.Field++; Console.WriteLine(field1.Field++); } } public void Enum() { enumField |= MyEnum.Two; enumField &= ~MyEnum.Four; enumField += 2; enumField -= 3; } public void ShortEnumTest() { shortEnumField |= ShortEnum.Two; shortEnumField &= ShortEnum.Four; shortEnumField += 2; shortEnumField -= 3; } public int PreIncrementInAddition(int i, int j) { return i + ++j; } public int PreIncrementArrayElement(int[] array, int pos) { return --array[pos]; } public int PostIncrementArrayElement(int[] array, int pos) { return array[pos]++; } public void IncrementArrayElement(int[] array, int pos) { array[pos]++; } public void DoubleArrayElement(int[] array, int pos) { array[pos] *= 2; } public int DoubleArrayElementAndReturn(int[] array, int pos) { return array[pos] *= 2; } public int PreIncrementArrayElementShort(short[] array, int pos) { return --array[pos]; } public int PostIncrementArrayElementShort(short[] array, int pos) { return array[pos]++; } public void IncrementArrayElementShort(short[] array, int pos) { array[pos]++; } public void DoubleArrayElementShort(short[] array, int pos) { array[pos] *= 2; } public short DoubleArrayElementShortAndReturn(short[] array, int pos) { return array[pos] *= 2; } public int PreIncrementInstanceField() { return ++M().Field; } public int PostIncrementInstanceField() { return M().Field++; } public void IncrementInstanceField() { M().Field++; } public void DoubleInstanceField() { M().Field *= 2; } public int DoubleInstanceFieldAndReturn() { return M().Field *= 2; } public int PreIncrementInstanceField2(MutableClass m) { return ++m.Field; } public int PostIncrementInstanceField2(MutableClass m) { return m.Field++; } public void IncrementInstanceField2(MutableClass m) { m.Field++; } public int PreIncrementInstanceFieldShort() { return ++M().ShortField; } public int PostIncrementInstanceFieldShort() { return M().ShortField++; } public void IncrementInstanceFieldShort() { M().ShortField++; } public int PreIncrementInstanceProperty() { return ++M().Property; } public int PostIncrementInstanceProperty() { return M().Property++; } public void IncrementInstanceProperty() { M().Property++; } public void DoubleInstanceProperty() { M().Property *= 2; } public int DoubleInstancePropertyAndReturn() { return M().Property *= 2; } public int PreIncrementInstancePropertyByte() { return ++M().ByteProperty; } public int PostIncrementInstancePropertyByte() { return M().ByteProperty++; } public void IncrementInstancePropertyByte() { M().ByteProperty++; } public void DoubleInstancePropertyByte() { M().ByteProperty *= 2; } public int DoubleInstancePropertyByteAndReturn() { return M().ByteProperty *= 2; } public void BitManipBoolProperty(bool b) { M().BoolProperty |= b; M().BoolProperty &= b; M().BoolProperty ^= b; } public bool BitOrBoolPropertyAndReturn(bool b) { return M().BoolProperty |= b; } public bool BitAndBoolPropertyAndReturn(bool b) { return M().BoolProperty &= b; } public int PreIncrementStaticField() { return ++StaticField; } public int PostIncrementStaticField() { return StaticField++; } public void IncrementStaticField() { StaticField++; } public void DoubleStaticField() { StaticField *= 2; } public int DoubleStaticFieldAndReturn() { return StaticField *= 2; } public int PreIncrementStaticFieldShort() { return ++StaticShortField; } public int PostIncrementStaticFieldShort() { return StaticShortField++; } public void IncrementStaticFieldShort() { StaticShortField++; } public void DoubleStaticFieldShort() { StaticShortField *= 2; } public short DoubleStaticFieldAndReturnShort() { return StaticShortField *= 2; } public int PreIncrementStaticProperty() { return ++StaticProperty; } public int PostIncrementStaticProperty() { return StaticProperty++; } public void IncrementStaticProperty() { StaticProperty++; } public void DoubleStaticProperty() { StaticProperty *= 2; } public int DoubleStaticPropertyAndReturn() { return StaticProperty *= 2; } public ShortEnum PreIncrementStaticPropertyShort() { return ++StaticShortProperty; } public ShortEnum PostIncrementStaticPropertyShort() { return StaticShortProperty++; } public void IncrementStaticPropertyShort() { StaticShortProperty++; } #region Generated Tests public static void ByteAddTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p += 5; b += 5; Use(ref b); byteField += 5; ByteProp += 5; c.ByteField += 5; c.ByteProp += 5; s.ByteField += 5; s.ByteProp += 5; customClassField.ByteField += 5; customClassField.ByteProp += 5; otherCustomStructField.ByteField += 5; otherCustomStructField.ByteProp += 5; CustomClassProp.ByteField += 5; CustomClassProp.ByteProp += 5; GetClass().ByteField += 5; GetClass().ByteProp += 5; #if CS70 GetRefStruct().ByteField += 5; GetRefStruct().ByteProp += 5; GetRefByte() += 5; #endif } public static void ByteSubtractTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p -= 5; b -= 5; Use(ref b); byteField -= 5; ByteProp -= 5; c.ByteField -= 5; c.ByteProp -= 5; s.ByteField -= 5; s.ByteProp -= 5; customClassField.ByteField -= 5; customClassField.ByteProp -= 5; otherCustomStructField.ByteField -= 5; otherCustomStructField.ByteProp -= 5; CustomClassProp.ByteField -= 5; CustomClassProp.ByteProp -= 5; GetClass().ByteField -= 5; GetClass().ByteProp -= 5; #if CS70 GetRefStruct().ByteField -= 5; GetRefStruct().ByteProp -= 5; GetRefByte() -= 5; #endif } public static void ByteMultiplyTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p *= 5; b *= 5; Use(ref b); byteField *= 5; ByteProp *= 5; c.ByteField *= 5; c.ByteProp *= 5; s.ByteField *= 5; s.ByteProp *= 5; customClassField.ByteField *= 5; customClassField.ByteProp *= 5; otherCustomStructField.ByteField *= 5; otherCustomStructField.ByteProp *= 5; CustomClassProp.ByteField *= 5; CustomClassProp.ByteProp *= 5; GetClass().ByteField *= 5; GetClass().ByteProp *= 5; #if CS70 GetRefStruct().ByteField *= 5; GetRefStruct().ByteProp *= 5; GetRefByte() *= 5; #endif } public static void ByteDivideTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p /= 5; b /= 5; Use(ref b); byteField /= 5; ByteProp /= 5; c.ByteField /= 5; c.ByteProp /= 5; s.ByteField /= 5; s.ByteProp /= 5; customClassField.ByteField /= 5; customClassField.ByteProp /= 5; otherCustomStructField.ByteField /= 5; otherCustomStructField.ByteProp /= 5; CustomClassProp.ByteField /= 5; CustomClassProp.ByteProp /= 5; GetClass().ByteField /= 5; GetClass().ByteProp /= 5; #if CS70 GetRefStruct().ByteField /= 5; GetRefStruct().ByteProp /= 5; GetRefByte() /= 5; #endif } public static void ByteModulusTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p %= 5; b %= 5; Use(ref b); byteField %= 5; ByteProp %= 5; c.ByteField %= 5; c.ByteProp %= 5; s.ByteField %= 5; s.ByteProp %= 5; customClassField.ByteField %= 5; customClassField.ByteProp %= 5; otherCustomStructField.ByteField %= 5; otherCustomStructField.ByteProp %= 5; CustomClassProp.ByteField %= 5; CustomClassProp.ByteProp %= 5; GetClass().ByteField %= 5; GetClass().ByteProp %= 5; #if CS70 GetRefStruct().ByteField %= 5; GetRefStruct().ByteProp %= 5; GetRefByte() %= 5; #endif } public static void ByteLeftShiftTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p <<= 5; b <<= 5; Use(ref b); byteField <<= 5; ByteProp <<= 5; c.ByteField <<= 5; c.ByteProp <<= 5; s.ByteField <<= 5; s.ByteProp <<= 5; customClassField.ByteField <<= 5; customClassField.ByteProp <<= 5; otherCustomStructField.ByteField <<= 5; otherCustomStructField.ByteProp <<= 5; CustomClassProp.ByteField <<= 5; CustomClassProp.ByteProp <<= 5; GetClass().ByteField <<= 5; GetClass().ByteProp <<= 5; #if CS70 GetRefStruct().ByteField <<= 5; GetRefStruct().ByteProp <<= 5; GetRefByte() <<= 5; #endif } public static void ByteRightShiftTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p >>= 5; b >>= 5; Use(ref b); byteField >>= 5; ByteProp >>= 5; c.ByteField >>= 5; c.ByteProp >>= 5; s.ByteField >>= 5; s.ByteProp >>= 5; customClassField.ByteField >>= 5; customClassField.ByteProp >>= 5; otherCustomStructField.ByteField >>= 5; otherCustomStructField.ByteProp >>= 5; CustomClassProp.ByteField >>= 5; CustomClassProp.ByteProp >>= 5; GetClass().ByteField >>= 5; GetClass().ByteProp >>= 5; #if CS70 GetRefStruct().ByteField >>= 5; GetRefStruct().ByteProp >>= 5; GetRefByte() >>= 5; #endif } public static void ByteBitAndTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p &= c.ByteField; b &= c.ByteField; Use(ref b); byteField &= 5; ByteProp &= 5; c.ByteField &= 5; c.ByteProp &= 5; s.ByteField &= 5; s.ByteProp &= 5; customClassField.ByteField &= 5; customClassField.ByteProp &= 5; otherCustomStructField.ByteField &= 5; otherCustomStructField.ByteProp &= 5; CustomClassProp.ByteField &= 5; CustomClassProp.ByteProp &= 5; GetClass().ByteField &= 5; GetClass().ByteProp &= 5; #if CS70 GetRefStruct().ByteField &= 5; GetRefStruct().ByteProp &= 5; GetRefByte() &= 5; #endif } public static void ByteBitOrTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p |= c.ByteField; b |= c.ByteField; Use(ref b); byteField |= 5; ByteProp |= 5; c.ByteField |= 5; c.ByteProp |= 5; s.ByteField |= 5; s.ByteProp |= 5; customClassField.ByteField |= 5; customClassField.ByteProp |= 5; otherCustomStructField.ByteField |= 5; otherCustomStructField.ByteProp |= 5; CustomClassProp.ByteField |= 5; CustomClassProp.ByteProp |= 5; GetClass().ByteField |= 5; GetClass().ByteProp |= 5; #if CS70 GetRefStruct().ByteField |= 5; GetRefStruct().ByteProp |= 5; GetRefByte() |= 5; #endif } public static void ByteBitXorTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; p ^= c.ByteField; b ^= c.ByteField; Use(ref b); byteField ^= 5; ByteProp ^= 5; c.ByteField ^= 5; c.ByteProp ^= 5; s.ByteField ^= 5; s.ByteProp ^= 5; customClassField.ByteField ^= 5; customClassField.ByteProp ^= 5; otherCustomStructField.ByteField ^= 5; otherCustomStructField.ByteProp ^= 5; CustomClassProp.ByteField ^= 5; CustomClassProp.ByteProp ^= 5; GetClass().ByteField ^= 5; GetClass().ByteProp ^= 5; #if CS70 GetRefStruct().ByteField ^= 5; GetRefStruct().ByteProp ^= 5; GetRefByte() ^= 5; #endif } public static void BytePostIncTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; X(p++); X(b++); Use(ref b); X(byteField++); X(ByteProp++); X(c.ByteField++); X(c.ByteProp++); X(s.ByteField++); X(s.ByteProp++); X(customClassField.ByteField++); X(customClassField.ByteProp++); X(otherCustomStructField.ByteField++); X(otherCustomStructField.ByteProp++); X(CustomClassProp.ByteField++); X(CustomClassProp.ByteProp++); X(GetClass().ByteField++); X(GetClass().ByteProp++); #if CS70 X(GetRefStruct().ByteField++); X(GetRefStruct().ByteProp++); X(GetRefByte()++); #endif } public static void BytePreIncTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; X(++p); X(++b); Use(ref b); X(++byteField); X(++ByteProp); X(++c.ByteField); X(++c.ByteProp); X(++s.ByteField); X(++s.ByteProp); X(++customClassField.ByteField); X(++customClassField.ByteProp); X(++otherCustomStructField.ByteField); X(++otherCustomStructField.ByteProp); X(++CustomClassProp.ByteField); X(++CustomClassProp.ByteProp); X(++GetClass().ByteField); X(++GetClass().ByteProp); #if CS70 X(++GetRefStruct().ByteField); X(++GetRefStruct().ByteProp); X(++GetRefByte()); #endif } public static void BytePostDecTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; X(p--); X(b--); Use(ref b); X(byteField--); X(ByteProp--); X(c.ByteField--); X(c.ByteProp--); X(s.ByteField--); X(s.ByteProp--); X(customClassField.ByteField--); X(customClassField.ByteProp--); X(otherCustomStructField.ByteField--); X(otherCustomStructField.ByteProp--); X(CustomClassProp.ByteField--); X(CustomClassProp.ByteProp--); X(GetClass().ByteField--); X(GetClass().ByteProp--); #if CS70 X(GetRefStruct().ByteField--); X(GetRefStruct().ByteProp--); X(GetRefByte()--); #endif } public static void BytePreDecTest(byte p, CustomClass c, CustomStruct2 s) { byte b = 0; X(--p); X(--b); Use(ref b); X(--byteField); X(--ByteProp); X(--c.ByteField); X(--c.ByteProp); X(--s.ByteField); X(--s.ByteProp); X(--customClassField.ByteField); X(--customClassField.ByteProp); X(--otherCustomStructField.ByteField); X(--otherCustomStructField.ByteProp); X(--CustomClassProp.ByteField); X(--CustomClassProp.ByteProp); X(--GetClass().ByteField); X(--GetClass().ByteProp); #if CS70 X(--GetRefStruct().ByteField); X(--GetRefStruct().ByteProp); X(--GetRefByte()); #endif } public static void SbyteAddTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p += 5; b += 5; Use(ref b); sbyteField += 5; SbyteProp += 5; c.SbyteField += 5; c.SbyteProp += 5; s.SbyteField += 5; s.SbyteProp += 5; customClassField.SbyteField += 5; customClassField.SbyteProp += 5; otherCustomStructField.SbyteField += 5; otherCustomStructField.SbyteProp += 5; CustomClassProp.SbyteField += 5; CustomClassProp.SbyteProp += 5; GetClass().SbyteField += 5; GetClass().SbyteProp += 5; #if CS70 GetRefStruct().SbyteField += 5; GetRefStruct().SbyteProp += 5; GetRefSbyte() += 5; #endif } public static void SbyteSubtractTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p -= 5; b -= 5; Use(ref b); sbyteField -= 5; SbyteProp -= 5; c.SbyteField -= 5; c.SbyteProp -= 5; s.SbyteField -= 5; s.SbyteProp -= 5; customClassField.SbyteField -= 5; customClassField.SbyteProp -= 5; otherCustomStructField.SbyteField -= 5; otherCustomStructField.SbyteProp -= 5; CustomClassProp.SbyteField -= 5; CustomClassProp.SbyteProp -= 5; GetClass().SbyteField -= 5; GetClass().SbyteProp -= 5; #if CS70 GetRefStruct().SbyteField -= 5; GetRefStruct().SbyteProp -= 5; GetRefSbyte() -= 5; #endif } public static void SbyteMultiplyTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p *= 5; b *= 5; Use(ref b); sbyteField *= 5; SbyteProp *= 5; c.SbyteField *= 5; c.SbyteProp *= 5; s.SbyteField *= 5; s.SbyteProp *= 5; customClassField.SbyteField *= 5; customClassField.SbyteProp *= 5; otherCustomStructField.SbyteField *= 5; otherCustomStructField.SbyteProp *= 5; CustomClassProp.SbyteField *= 5; CustomClassProp.SbyteProp *= 5; GetClass().SbyteField *= 5; GetClass().SbyteProp *= 5; #if CS70 GetRefStruct().SbyteField *= 5; GetRefStruct().SbyteProp *= 5; GetRefSbyte() *= 5; #endif } public static void SbyteDivideTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p /= 5; b /= 5; Use(ref b); sbyteField /= 5; SbyteProp /= 5; c.SbyteField /= 5; c.SbyteProp /= 5; s.SbyteField /= 5; s.SbyteProp /= 5; customClassField.SbyteField /= 5; customClassField.SbyteProp /= 5; otherCustomStructField.SbyteField /= 5; otherCustomStructField.SbyteProp /= 5; CustomClassProp.SbyteField /= 5; CustomClassProp.SbyteProp /= 5; GetClass().SbyteField /= 5; GetClass().SbyteProp /= 5; #if CS70 GetRefStruct().SbyteField /= 5; GetRefStruct().SbyteProp /= 5; GetRefSbyte() /= 5; #endif } public static void SbyteModulusTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p %= 5; b %= 5; Use(ref b); sbyteField %= 5; SbyteProp %= 5; c.SbyteField %= 5; c.SbyteProp %= 5; s.SbyteField %= 5; s.SbyteProp %= 5; customClassField.SbyteField %= 5; customClassField.SbyteProp %= 5; otherCustomStructField.SbyteField %= 5; otherCustomStructField.SbyteProp %= 5; CustomClassProp.SbyteField %= 5; CustomClassProp.SbyteProp %= 5; GetClass().SbyteField %= 5; GetClass().SbyteProp %= 5; #if CS70 GetRefStruct().SbyteField %= 5; GetRefStruct().SbyteProp %= 5; GetRefSbyte() %= 5; #endif } public static void SbyteLeftShiftTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p <<= 5; b <<= 5; Use(ref b); sbyteField <<= 5; SbyteProp <<= 5; c.SbyteField <<= 5; c.SbyteProp <<= 5; s.SbyteField <<= 5; s.SbyteProp <<= 5; customClassField.SbyteField <<= 5; customClassField.SbyteProp <<= 5; otherCustomStructField.SbyteField <<= 5; otherCustomStructField.SbyteProp <<= 5; CustomClassProp.SbyteField <<= 5; CustomClassProp.SbyteProp <<= 5; GetClass().SbyteField <<= 5; GetClass().SbyteProp <<= 5; #if CS70 GetRefStruct().SbyteField <<= 5; GetRefStruct().SbyteProp <<= 5; GetRefSbyte() <<= 5; #endif } public static void SbyteRightShiftTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p >>= 5; b >>= 5; Use(ref b); sbyteField >>= 5; SbyteProp >>= 5; c.SbyteField >>= 5; c.SbyteProp >>= 5; s.SbyteField >>= 5; s.SbyteProp >>= 5; customClassField.SbyteField >>= 5; customClassField.SbyteProp >>= 5; otherCustomStructField.SbyteField >>= 5; otherCustomStructField.SbyteProp >>= 5; CustomClassProp.SbyteField >>= 5; CustomClassProp.SbyteProp >>= 5; GetClass().SbyteField >>= 5; GetClass().SbyteProp >>= 5; #if CS70 GetRefStruct().SbyteField >>= 5; GetRefStruct().SbyteProp >>= 5; GetRefSbyte() >>= 5; #endif } public static void SbyteBitAndTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p &= 5; b &= 5; Use(ref b); sbyteField &= 5; SbyteProp &= 5; c.SbyteField &= 5; c.SbyteProp &= 5; s.SbyteField &= 5; s.SbyteProp &= 5; customClassField.SbyteField &= 5; customClassField.SbyteProp &= 5; otherCustomStructField.SbyteField &= 5; otherCustomStructField.SbyteProp &= 5; CustomClassProp.SbyteField &= 5; CustomClassProp.SbyteProp &= 5; GetClass().SbyteField &= 5; GetClass().SbyteProp &= 5; #if CS70 GetRefStruct().SbyteField &= 5; GetRefStruct().SbyteProp &= 5; GetRefSbyte() &= 5; #endif } public static void SbyteBitOrTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p |= 5; b |= 5; Use(ref b); sbyteField |= 5; SbyteProp |= 5; c.SbyteField |= 5; c.SbyteProp |= 5; s.SbyteField |= 5; s.SbyteProp |= 5; customClassField.SbyteField |= 5; customClassField.SbyteProp |= 5; otherCustomStructField.SbyteField |= 5; otherCustomStructField.SbyteProp |= 5; CustomClassProp.SbyteField |= 5; CustomClassProp.SbyteProp |= 5; GetClass().SbyteField |= 5; GetClass().SbyteProp |= 5; #if CS70 GetRefStruct().SbyteField |= 5; GetRefStruct().SbyteProp |= 5; GetRefSbyte() |= 5; #endif } public static void SbyteBitXorTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; p ^= 5; b ^= 5; Use(ref b); sbyteField ^= 5; SbyteProp ^= 5; c.SbyteField ^= 5; c.SbyteProp ^= 5; s.SbyteField ^= 5; s.SbyteProp ^= 5; customClassField.SbyteField ^= 5; customClassField.SbyteProp ^= 5; otherCustomStructField.SbyteField ^= 5; otherCustomStructField.SbyteProp ^= 5; CustomClassProp.SbyteField ^= 5; CustomClassProp.SbyteProp ^= 5; GetClass().SbyteField ^= 5; GetClass().SbyteProp ^= 5; #if CS70 GetRefStruct().SbyteField ^= 5; GetRefStruct().SbyteProp ^= 5; GetRefSbyte() ^= 5; #endif } public static void SbytePostIncTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; X(p++); X(b++); Use(ref b); X(sbyteField++); X(SbyteProp++); X(c.SbyteField++); X(c.SbyteProp++); X(s.SbyteField++); X(s.SbyteProp++); X(customClassField.SbyteField++); X(customClassField.SbyteProp++); X(otherCustomStructField.SbyteField++); X(otherCustomStructField.SbyteProp++); X(CustomClassProp.SbyteField++); X(CustomClassProp.SbyteProp++); X(GetClass().SbyteField++); X(GetClass().SbyteProp++); #if CS70 X(GetRefStruct().SbyteField++); X(GetRefStruct().SbyteProp++); X(GetRefSbyte()++); #endif } public static void SbytePreIncTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; X(++p); X(++b); Use(ref b); X(++sbyteField); X(++SbyteProp); X(++c.SbyteField); X(++c.SbyteProp); X(++s.SbyteField); X(++s.SbyteProp); X(++customClassField.SbyteField); X(++customClassField.SbyteProp); X(++otherCustomStructField.SbyteField); X(++otherCustomStructField.SbyteProp); X(++CustomClassProp.SbyteField); X(++CustomClassProp.SbyteProp); X(++GetClass().SbyteField); X(++GetClass().SbyteProp); #if CS70 X(++GetRefStruct().SbyteField); X(++GetRefStruct().SbyteProp); X(++GetRefSbyte()); #endif } public static void SbytePostDecTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; X(p--); X(b--); Use(ref b); X(sbyteField--); X(SbyteProp--); X(c.SbyteField--); X(c.SbyteProp--); X(s.SbyteField--); X(s.SbyteProp--); X(customClassField.SbyteField--); X(customClassField.SbyteProp--); X(otherCustomStructField.SbyteField--); X(otherCustomStructField.SbyteProp--); X(CustomClassProp.SbyteField--); X(CustomClassProp.SbyteProp--); X(GetClass().SbyteField--); X(GetClass().SbyteProp--); #if CS70 X(GetRefStruct().SbyteField--); X(GetRefStruct().SbyteProp--); X(GetRefSbyte()--); #endif } public static void SbytePreDecTest(sbyte p, CustomClass c, CustomStruct2 s) { sbyte b = 0; X(--p); X(--b); Use(ref b); X(--sbyteField); X(--SbyteProp); X(--c.SbyteField); X(--c.SbyteProp); X(--s.SbyteField); X(--s.SbyteProp); X(--customClassField.SbyteField); X(--customClassField.SbyteProp); X(--otherCustomStructField.SbyteField); X(--otherCustomStructField.SbyteProp); X(--CustomClassProp.SbyteField); X(--CustomClassProp.SbyteProp); X(--GetClass().SbyteField); X(--GetClass().SbyteProp); #if CS70 X(--GetRefStruct().SbyteField); X(--GetRefStruct().SbyteProp); X(--GetRefSbyte()); #endif } public static void ShortAddTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p += 5; num += 5; Use(ref num); shortField += 5; ShortProp += 5; c.ShortField += 5; c.ShortProp += 5; s.ShortField += 5; s.ShortProp += 5; customClassField.ShortField += 5; customClassField.ShortProp += 5; otherCustomStructField.ShortField += 5; otherCustomStructField.ShortProp += 5; CustomClassProp.ShortField += 5; CustomClassProp.ShortProp += 5; GetClass().ShortField += 5; GetClass().ShortProp += 5; #if CS70 GetRefStruct().ShortField += 5; GetRefStruct().ShortProp += 5; GetRefShort() += 5; #endif } public static void ShortSubtractTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p -= 5; num -= 5; Use(ref num); shortField -= 5; ShortProp -= 5; c.ShortField -= 5; c.ShortProp -= 5; s.ShortField -= 5; s.ShortProp -= 5; customClassField.ShortField -= 5; customClassField.ShortProp -= 5; otherCustomStructField.ShortField -= 5; otherCustomStructField.ShortProp -= 5; CustomClassProp.ShortField -= 5; CustomClassProp.ShortProp -= 5; GetClass().ShortField -= 5; GetClass().ShortProp -= 5; #if CS70 GetRefStruct().ShortField -= 5; GetRefStruct().ShortProp -= 5; GetRefShort() -= 5; #endif } public static void ShortMultiplyTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p *= 5; num *= 5; Use(ref num); shortField *= 5; ShortProp *= 5; c.ShortField *= 5; c.ShortProp *= 5; s.ShortField *= 5; s.ShortProp *= 5; customClassField.ShortField *= 5; customClassField.ShortProp *= 5; otherCustomStructField.ShortField *= 5; otherCustomStructField.ShortProp *= 5; CustomClassProp.ShortField *= 5; CustomClassProp.ShortProp *= 5; GetClass().ShortField *= 5; GetClass().ShortProp *= 5; #if CS70 GetRefStruct().ShortField *= 5; GetRefStruct().ShortProp *= 5; GetRefShort() *= 5; #endif } public static void ShortDivideTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p /= 5; num /= 5; Use(ref num); shortField /= 5; ShortProp /= 5; c.ShortField /= 5; c.ShortProp /= 5; s.ShortField /= 5; s.ShortProp /= 5; customClassField.ShortField /= 5; customClassField.ShortProp /= 5; otherCustomStructField.ShortField /= 5; otherCustomStructField.ShortProp /= 5; CustomClassProp.ShortField /= 5; CustomClassProp.ShortProp /= 5; GetClass().ShortField /= 5; GetClass().ShortProp /= 5; #if CS70 GetRefStruct().ShortField /= 5; GetRefStruct().ShortProp /= 5; GetRefShort() /= 5; #endif } public static void ShortModulusTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p %= 5; num %= 5; Use(ref num); shortField %= 5; ShortProp %= 5; c.ShortField %= 5; c.ShortProp %= 5; s.ShortField %= 5; s.ShortProp %= 5; customClassField.ShortField %= 5; customClassField.ShortProp %= 5; otherCustomStructField.ShortField %= 5; otherCustomStructField.ShortProp %= 5; CustomClassProp.ShortField %= 5; CustomClassProp.ShortProp %= 5; GetClass().ShortField %= 5; GetClass().ShortProp %= 5; #if CS70 GetRefStruct().ShortField %= 5; GetRefStruct().ShortProp %= 5; GetRefShort() %= 5; #endif } public static void ShortLeftShiftTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p <<= 5; num <<= 5; Use(ref num); shortField <<= 5; ShortProp <<= 5; c.ShortField <<= 5; c.ShortProp <<= 5; s.ShortField <<= 5; s.ShortProp <<= 5; customClassField.ShortField <<= 5; customClassField.ShortProp <<= 5; otherCustomStructField.ShortField <<= 5; otherCustomStructField.ShortProp <<= 5; CustomClassProp.ShortField <<= 5; CustomClassProp.ShortProp <<= 5; GetClass().ShortField <<= 5; GetClass().ShortProp <<= 5; #if CS70 GetRefStruct().ShortField <<= 5; GetRefStruct().ShortProp <<= 5; GetRefShort() <<= 5; #endif } public static void ShortRightShiftTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p >>= 5; num >>= 5; Use(ref num); shortField >>= 5; ShortProp >>= 5; c.ShortField >>= 5; c.ShortProp >>= 5; s.ShortField >>= 5; s.ShortProp >>= 5; customClassField.ShortField >>= 5; customClassField.ShortProp >>= 5; otherCustomStructField.ShortField >>= 5; otherCustomStructField.ShortProp >>= 5; CustomClassProp.ShortField >>= 5; CustomClassProp.ShortProp >>= 5; GetClass().ShortField >>= 5; GetClass().ShortProp >>= 5; #if CS70 GetRefStruct().ShortField >>= 5; GetRefStruct().ShortProp >>= 5; GetRefShort() >>= 5; #endif } #if CS110 public static void ShortUnsignedRightShiftTest(short p, CustomClass c, CustomStruct2 s) { //X(p >>>= 5); shortField >>>= 5; ShortProp >>>= 5; c.ShortField >>>= 5; c.ShortProp >>>= 5; s.ShortField >>>= 5; s.ShortProp >>>= 5; customClassField.ShortField >>>= 5; customClassField.ShortProp >>>= 5; otherCustomStructField.ShortField >>>= 5; otherCustomStructField.ShortProp >>>= 5; CustomClassProp.ShortField >>>= 5; CustomClassProp.ShortProp >>>= 5; GetClass().ShortField >>>= 5; GetClass().ShortProp >>>= 5; GetRefStruct().ShortField >>>= 5; GetRefStruct().ShortProp >>>= 5; GetRefShort() >>>= 5; } #endif public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p &= 5; num &= 5; Use(ref num); shortField &= 5; ShortProp &= 5; c.ShortField &= 5; c.ShortProp &= 5; s.ShortField &= 5; s.ShortProp &= 5; customClassField.ShortField &= 5; customClassField.ShortProp &= 5; otherCustomStructField.ShortField &= 5; otherCustomStructField.ShortProp &= 5; CustomClassProp.ShortField &= 5; CustomClassProp.ShortProp &= 5; GetClass().ShortField &= 5; GetClass().ShortProp &= 5; #if CS70 GetRefStruct().ShortField &= 5; GetRefStruct().ShortProp &= 5; GetRefShort() &= 5; #endif } public static void ShortBitOrTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p |= 5; num |= 5; Use(ref num); shortField |= 5; ShortProp |= 5; c.ShortField |= 5; c.ShortProp |= 5; s.ShortField |= 5; s.ShortProp |= 5; customClassField.ShortField |= 5; customClassField.ShortProp |= 5; otherCustomStructField.ShortField |= 5; otherCustomStructField.ShortProp |= 5; CustomClassProp.ShortField |= 5; CustomClassProp.ShortProp |= 5; GetClass().ShortField |= 5; GetClass().ShortProp |= 5; #if CS70 GetRefStruct().ShortField |= 5; GetRefStruct().ShortProp |= 5; GetRefShort() |= 5; #endif } public static void ShortBitXorTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; p ^= 5; num ^= 5; Use(ref num); shortField ^= 5; ShortProp ^= 5; c.ShortField ^= 5; c.ShortProp ^= 5; s.ShortField ^= 5; s.ShortProp ^= 5; customClassField.ShortField ^= 5; customClassField.ShortProp ^= 5; otherCustomStructField.ShortField ^= 5; otherCustomStructField.ShortProp ^= 5; CustomClassProp.ShortField ^= 5; CustomClassProp.ShortProp ^= 5; GetClass().ShortField ^= 5; GetClass().ShortProp ^= 5; #if CS70 GetRefStruct().ShortField ^= 5; GetRefStruct().ShortProp ^= 5; GetRefShort() ^= 5; #endif } public static void ShortPostIncTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; X(p++); X(num++); Use(ref num); X(shortField++); X(ShortProp++); X(c.ShortField++); X(c.ShortProp++); X(s.ShortField++); X(s.ShortProp++); X(customClassField.ShortField++); X(customClassField.ShortProp++); X(otherCustomStructField.ShortField++); X(otherCustomStructField.ShortProp++); X(CustomClassProp.ShortField++); X(CustomClassProp.ShortProp++); X(GetClass().ShortField++); X(GetClass().ShortProp++); #if CS70 X(GetRefStruct().ShortField++); X(GetRefStruct().ShortProp++); X(GetRefShort()++); #endif } public static void ShortPreIncTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; X(++p); X(++num); Use(ref num); X(++shortField); X(++ShortProp); X(++c.ShortField); X(++c.ShortProp); X(++s.ShortField); X(++s.ShortProp); X(++customClassField.ShortField); X(++customClassField.ShortProp); X(++otherCustomStructField.ShortField); X(++otherCustomStructField.ShortProp); X(++CustomClassProp.ShortField); X(++CustomClassProp.ShortProp); X(++GetClass().ShortField); X(++GetClass().ShortProp); #if CS70 X(++GetRefStruct().ShortField); X(++GetRefStruct().ShortProp); X(++GetRefShort()); #endif } public static void ShortPostDecTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; X(p--); X(num--); Use(ref num); X(shortField--); X(ShortProp--); X(c.ShortField--); X(c.ShortProp--); X(s.ShortField--); X(s.ShortProp--); X(customClassField.ShortField--); X(customClassField.ShortProp--); X(otherCustomStructField.ShortField--); X(otherCustomStructField.ShortProp--); X(CustomClassProp.ShortField--); X(CustomClassProp.ShortProp--); X(GetClass().ShortField--); X(GetClass().ShortProp--); #if CS70 X(GetRefStruct().ShortField--); X(GetRefStruct().ShortProp--); X(GetRefShort()--); #endif } public static void ShortPreDecTest(short p, CustomClass c, CustomStruct2 s) { short num = 0; X(--p); X(--num); Use(ref num); X(--shortField); X(--ShortProp); X(--c.ShortField); X(--c.ShortProp); X(--s.ShortField); X(--s.ShortProp); X(--customClassField.ShortField); X(--customClassField.ShortProp); X(--otherCustomStructField.ShortField); X(--otherCustomStructField.ShortProp); X(--CustomClassProp.ShortField); X(--CustomClassProp.ShortProp); X(--GetClass().ShortField); X(--GetClass().ShortProp); #if CS70 X(--GetRefStruct().ShortField); X(--GetRefStruct().ShortProp); X(--GetRefShort()); #endif } public static void UshortAddTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p += 5; num += 5; Use(ref num); ushortField += 5; UshortProp += 5; c.UshortField += 5; c.UshortProp += 5; s.UshortField += 5; s.UshortProp += 5; customClassField.UshortField += 5; customClassField.UshortProp += 5; otherCustomStructField.UshortField += 5; otherCustomStructField.UshortProp += 5; CustomClassProp.UshortField += 5; CustomClassProp.UshortProp += 5; GetClass().UshortField += 5; GetClass().UshortProp += 5; #if CS70 GetRefStruct().UshortField += 5; GetRefStruct().UshortProp += 5; GetRefUshort() += 5; #endif } public static void UshortSubtractTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p -= 5; num -= 5; Use(ref num); ushortField -= 5; UshortProp -= 5; c.UshortField -= 5; c.UshortProp -= 5; s.UshortField -= 5; s.UshortProp -= 5; customClassField.UshortField -= 5; customClassField.UshortProp -= 5; otherCustomStructField.UshortField -= 5; otherCustomStructField.UshortProp -= 5; CustomClassProp.UshortField -= 5; CustomClassProp.UshortProp -= 5; GetClass().UshortField -= 5; GetClass().UshortProp -= 5; #if CS70 GetRefStruct().UshortField -= 5; GetRefStruct().UshortProp -= 5; GetRefUshort() -= 5; #endif } public static void UshortMultiplyTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p *= 5; num *= 5; Use(ref num); ushortField *= 5; UshortProp *= 5; c.UshortField *= 5; c.UshortProp *= 5; s.UshortField *= 5; s.UshortProp *= 5; customClassField.UshortField *= 5; customClassField.UshortProp *= 5; otherCustomStructField.UshortField *= 5; otherCustomStructField.UshortProp *= 5; CustomClassProp.UshortField *= 5; CustomClassProp.UshortProp *= 5; GetClass().UshortField *= 5; GetClass().UshortProp *= 5; #if CS70 GetRefStruct().UshortField *= 5; GetRefStruct().UshortProp *= 5; GetRefUshort() *= 5; #endif } public static void UshortDivideTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p /= 5; num /= 5; Use(ref num); ushortField /= 5; UshortProp /= 5; c.UshortField /= 5; c.UshortProp /= 5; s.UshortField /= 5; s.UshortProp /= 5; customClassField.UshortField /= 5; customClassField.UshortProp /= 5; otherCustomStructField.UshortField /= 5; otherCustomStructField.UshortProp /= 5; CustomClassProp.UshortField /= 5; CustomClassProp.UshortProp /= 5; GetClass().UshortField /= 5; GetClass().UshortProp /= 5; #if CS70 GetRefStruct().UshortField /= 5; GetRefStruct().UshortProp /= 5; GetRefUshort() /= 5; #endif } public static void UshortModulusTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p %= 5; num %= 5; Use(ref num); ushortField %= 5; UshortProp %= 5; c.UshortField %= 5; c.UshortProp %= 5; s.UshortField %= 5; s.UshortProp %= 5; customClassField.UshortField %= 5; customClassField.UshortProp %= 5; otherCustomStructField.UshortField %= 5; otherCustomStructField.UshortProp %= 5; CustomClassProp.UshortField %= 5; CustomClassProp.UshortProp %= 5; GetClass().UshortField %= 5; GetClass().UshortProp %= 5; #if CS70 GetRefStruct().UshortField %= 5; GetRefStruct().UshortProp %= 5; GetRefUshort() %= 5; #endif } public static void UshortLeftShiftTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p <<= 5; num <<= 5; Use(ref num); ushortField <<= 5; UshortProp <<= 5; c.UshortField <<= 5; c.UshortProp <<= 5; s.UshortField <<= 5; s.UshortProp <<= 5; customClassField.UshortField <<= 5; customClassField.UshortProp <<= 5; otherCustomStructField.UshortField <<= 5; otherCustomStructField.UshortProp <<= 5; CustomClassProp.UshortField <<= 5; CustomClassProp.UshortProp <<= 5; GetClass().UshortField <<= 5; GetClass().UshortProp <<= 5; #if CS70 GetRefStruct().UshortField <<= 5; GetRefStruct().UshortProp <<= 5; GetRefUshort() <<= 5; #endif } public static void UshortRightShiftTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p >>= 5; num >>= 5; Use(ref num); ushortField >>= 5; UshortProp >>= 5; c.UshortField >>= 5; c.UshortProp >>= 5; s.UshortField >>= 5; s.UshortProp >>= 5; customClassField.UshortField >>= 5; customClassField.UshortProp >>= 5; otherCustomStructField.UshortField >>= 5; otherCustomStructField.UshortProp >>= 5; CustomClassProp.UshortField >>= 5; CustomClassProp.UshortProp >>= 5; GetClass().UshortField >>= 5; GetClass().UshortProp >>= 5; #if CS70 GetRefStruct().UshortField >>= 5; GetRefStruct().UshortProp >>= 5; GetRefUshort() >>= 5; #endif } #if CS110 public static void UshortUnsignedRightShiftTest(ushort p, CustomClass c, CustomStruct2 s) { //ushort l = 0; //p >>>= 5; //l >>>= 5; ushortField >>>= 5; UshortProp >>>= 5; c.UshortField >>>= 5; c.UshortProp >>>= 5; s.UshortField >>>= 5; s.UshortProp >>>= 5; customClassField.UshortField >>>= 5; customClassField.UshortProp >>>= 5; otherCustomStructField.UshortField >>>= 5; otherCustomStructField.UshortProp >>>= 5; CustomClassProp.UshortField >>>= 5; CustomClassProp.UshortProp >>>= 5; GetClass().UshortField >>>= 5; GetClass().UshortProp >>>= 5; GetRefStruct().UshortField >>>= 5; GetRefStruct().UshortProp >>>= 5; GetRefUshort() >>>= 5; } #endif public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p &= c.UshortField; num &= c.UshortField; Use(ref num); ushortField &= 5; UshortProp &= 5; c.UshortField &= 5; c.UshortProp &= 5; s.UshortField &= 5; s.UshortProp &= 5; customClassField.UshortField &= 5; customClassField.UshortProp &= 5; otherCustomStructField.UshortField &= 5; otherCustomStructField.UshortProp &= 5; CustomClassProp.UshortField &= 5; CustomClassProp.UshortProp &= 5; GetClass().UshortField &= 5; GetClass().UshortProp &= 5; #if CS70 GetRefStruct().UshortField &= 5; GetRefStruct().UshortProp &= 5; GetRefUshort() &= 5; #endif } public static void UshortBitOrTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p |= c.UshortField; num |= c.UshortField; Use(ref num); ushortField |= 5; UshortProp |= 5; c.UshortField |= 5; c.UshortProp |= 5; s.UshortField |= 5; s.UshortProp |= 5; customClassField.UshortField |= 5; customClassField.UshortProp |= 5; otherCustomStructField.UshortField |= 5; otherCustomStructField.UshortProp |= 5; CustomClassProp.UshortField |= 5; CustomClassProp.UshortProp |= 5; GetClass().UshortField |= 5; GetClass().UshortProp |= 5; #if CS70 GetRefStruct().UshortField |= 5; GetRefStruct().UshortProp |= 5; GetRefUshort() |= 5; #endif } public static void UshortBitXorTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; p ^= c.UshortField; num ^= c.UshortField; Use(ref num); ushortField ^= 5; UshortProp ^= 5; c.UshortField ^= 5; c.UshortProp ^= 5; s.UshortField ^= 5; s.UshortProp ^= 5; customClassField.UshortField ^= 5; customClassField.UshortProp ^= 5; otherCustomStructField.UshortField ^= 5; otherCustomStructField.UshortProp ^= 5; CustomClassProp.UshortField ^= 5; CustomClassProp.UshortProp ^= 5; GetClass().UshortField ^= 5; GetClass().UshortProp ^= 5; #if CS70 GetRefStruct().UshortField ^= 5; GetRefStruct().UshortProp ^= 5; GetRefUshort() ^= 5; #endif } public static void UshortPostIncTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; X(p++); X(num++); Use(ref num); X(ushortField++); X(UshortProp++); X(c.UshortField++); X(c.UshortProp++); X(s.UshortField++); X(s.UshortProp++); X(customClassField.UshortField++); X(customClassField.UshortProp++); X(otherCustomStructField.UshortField++); X(otherCustomStructField.UshortProp++); X(CustomClassProp.UshortField++); X(CustomClassProp.UshortProp++); X(GetClass().UshortField++); X(GetClass().UshortProp++); #if CS70 X(GetRefStruct().UshortField++); X(GetRefStruct().UshortProp++); X(GetRefUshort()++); #endif } public static void UshortPreIncTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; X(++p); X(++num); Use(ref num); X(++ushortField); X(++UshortProp); X(++c.UshortField); X(++c.UshortProp); X(++s.UshortField); X(++s.UshortProp); X(++customClassField.UshortField); X(++customClassField.UshortProp); X(++otherCustomStructField.UshortField); X(++otherCustomStructField.UshortProp); X(++CustomClassProp.UshortField); X(++CustomClassProp.UshortProp); X(++GetClass().UshortField); X(++GetClass().UshortProp); #if CS70 X(++GetRefStruct().UshortField); X(++GetRefStruct().UshortProp); X(++GetRefUshort()); #endif } public static void UshortPostDecTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; X(p--); X(num--); Use(ref num); X(ushortField--); X(UshortProp--); X(c.UshortField--); X(c.UshortProp--); X(s.UshortField--); X(s.UshortProp--); X(customClassField.UshortField--); X(customClassField.UshortProp--); X(otherCustomStructField.UshortField--); X(otherCustomStructField.UshortProp--); X(CustomClassProp.UshortField--); X(CustomClassProp.UshortProp--); X(GetClass().UshortField--); X(GetClass().UshortProp--); #if CS70 X(GetRefStruct().UshortField--); X(GetRefStruct().UshortProp--); X(GetRefUshort()--); #endif } public static void UshortPreDecTest(ushort p, CustomClass c, CustomStruct2 s) { ushort num = 0; X(--p); X(--num); Use(ref num); X(--ushortField); X(--UshortProp); X(--c.UshortField); X(--c.UshortProp); X(--s.UshortField); X(--s.UshortProp); X(--customClassField.UshortField); X(--customClassField.UshortProp); X(--otherCustomStructField.UshortField); X(--otherCustomStructField.UshortProp); X(--CustomClassProp.UshortField); X(--CustomClassProp.UshortProp); X(--GetClass().UshortField); X(--GetClass().UshortProp); #if CS70 X(--GetRefStruct().UshortField); X(--GetRefStruct().UshortProp); X(--GetRefUshort()); #endif } public static void IntAddTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p += 5; num += 5; Use(ref num); intField += 5; IntProp += 5; c.IntField += 5; c.IntProp += 5; s.IntField += 5; s.IntProp += 5; customClassField.IntField += 5; customClassField.IntProp += 5; otherCustomStructField.IntField += 5; otherCustomStructField.IntProp += 5; CustomClassProp.IntField += 5; CustomClassProp.IntProp += 5; GetClass().IntField += 5; GetClass().IntProp += 5; #if CS70 GetRefStruct().IntField += 5; GetRefStruct().IntProp += 5; GetRefInt() += 5; #endif } public static void IntSubtractTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p -= 5; num -= 5; Use(ref num); intField -= 5; IntProp -= 5; c.IntField -= 5; c.IntProp -= 5; s.IntField -= 5; s.IntProp -= 5; customClassField.IntField -= 5; customClassField.IntProp -= 5; otherCustomStructField.IntField -= 5; otherCustomStructField.IntProp -= 5; CustomClassProp.IntField -= 5; CustomClassProp.IntProp -= 5; GetClass().IntField -= 5; GetClass().IntProp -= 5; #if CS70 GetRefStruct().IntField -= 5; GetRefStruct().IntProp -= 5; GetRefInt() -= 5; #endif } public static void IntMultiplyTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p *= 5; num *= 5; Use(ref num); intField *= 5; IntProp *= 5; c.IntField *= 5; c.IntProp *= 5; s.IntField *= 5; s.IntProp *= 5; customClassField.IntField *= 5; customClassField.IntProp *= 5; otherCustomStructField.IntField *= 5; otherCustomStructField.IntProp *= 5; CustomClassProp.IntField *= 5; CustomClassProp.IntProp *= 5; GetClass().IntField *= 5; GetClass().IntProp *= 5; #if CS70 GetRefStruct().IntField *= 5; GetRefStruct().IntProp *= 5; GetRefInt() *= 5; #endif } public static void IntDivideTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p /= 5; num /= 5; Use(ref num); intField /= 5; IntProp /= 5; c.IntField /= 5; c.IntProp /= 5; s.IntField /= 5; s.IntProp /= 5; customClassField.IntField /= 5; customClassField.IntProp /= 5; otherCustomStructField.IntField /= 5; otherCustomStructField.IntProp /= 5; CustomClassProp.IntField /= 5; CustomClassProp.IntProp /= 5; GetClass().IntField /= 5; GetClass().IntProp /= 5; #if CS70 GetRefStruct().IntField /= 5; GetRefStruct().IntProp /= 5; GetRefInt() /= 5; #endif } public static void IntModulusTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p %= 5; num %= 5; Use(ref num); intField %= 5; IntProp %= 5; c.IntField %= 5; c.IntProp %= 5; s.IntField %= 5; s.IntProp %= 5; customClassField.IntField %= 5; customClassField.IntProp %= 5; otherCustomStructField.IntField %= 5; otherCustomStructField.IntProp %= 5; CustomClassProp.IntField %= 5; CustomClassProp.IntProp %= 5; GetClass().IntField %= 5; GetClass().IntProp %= 5; #if CS70 GetRefStruct().IntField %= 5; GetRefStruct().IntProp %= 5; GetRefInt() %= 5; #endif } public static void IntLeftShiftTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p <<= 5; num <<= 5; Use(ref num); intField <<= 5; IntProp <<= 5; c.IntField <<= 5; c.IntProp <<= 5; s.IntField <<= 5; s.IntProp <<= 5; customClassField.IntField <<= 5; customClassField.IntProp <<= 5; otherCustomStructField.IntField <<= 5; otherCustomStructField.IntProp <<= 5; CustomClassProp.IntField <<= 5; CustomClassProp.IntProp <<= 5; GetClass().IntField <<= 5; GetClass().IntProp <<= 5; #if CS70 GetRefStruct().IntField <<= 5; GetRefStruct().IntProp <<= 5; GetRefInt() <<= 5; #endif } public static void IntRightShiftTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p >>= 5; num >>= 5; Use(ref num); intField >>= 5; IntProp >>= 5; c.IntField >>= 5; c.IntProp >>= 5; s.IntField >>= 5; s.IntProp >>= 5; customClassField.IntField >>= 5; customClassField.IntProp >>= 5; otherCustomStructField.IntField >>= 5; otherCustomStructField.IntProp >>= 5; CustomClassProp.IntField >>= 5; CustomClassProp.IntProp >>= 5; GetClass().IntField >>= 5; GetClass().IntProp >>= 5; #if CS70 GetRefStruct().IntField >>= 5; GetRefStruct().IntProp >>= 5; GetRefInt() >>= 5; #endif } #if CS110 public static void IntUnsignedRightShiftTest(int p, CustomClass c, CustomStruct2 s) { X(p >>>= 5); intField >>>= 5; IntProp >>>= 5; c.IntField >>>= 5; c.IntProp >>>= 5; s.IntField >>>= 5; s.IntProp >>>= 5; customClassField.IntField >>>= 5; customClassField.IntProp >>>= 5; otherCustomStructField.IntField >>>= 5; otherCustomStructField.IntProp >>>= 5; CustomClassProp.IntField >>>= 5; CustomClassProp.IntProp >>>= 5; GetClass().IntField >>>= 5; GetClass().IntProp >>>= 5; GetRefStruct().IntField >>>= 5; GetRefStruct().IntProp >>>= 5; GetRefInt() >>>= 5; } #endif public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p &= 5; num &= 5; Use(ref num); intField &= 5; IntProp &= 5; c.IntField &= 5; c.IntProp &= 5; s.IntField &= 5; s.IntProp &= 5; customClassField.IntField &= 5; customClassField.IntProp &= 5; otherCustomStructField.IntField &= 5; otherCustomStructField.IntProp &= 5; CustomClassProp.IntField &= 5; CustomClassProp.IntProp &= 5; GetClass().IntField &= 5; GetClass().IntProp &= 5; #if CS70 GetRefStruct().IntField &= 5; GetRefStruct().IntProp &= 5; GetRefInt() &= 5; #endif } public static void IntBitOrTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p |= 5; num |= 5; Use(ref num); intField |= 5; IntProp |= 5; c.IntField |= 5; c.IntProp |= 5; s.IntField |= 5; s.IntProp |= 5; customClassField.IntField |= 5; customClassField.IntProp |= 5; otherCustomStructField.IntField |= 5; otherCustomStructField.IntProp |= 5; CustomClassProp.IntField |= 5; CustomClassProp.IntProp |= 5; GetClass().IntField |= 5; GetClass().IntProp |= 5; #if CS70 GetRefStruct().IntField |= 5; GetRefStruct().IntProp |= 5; GetRefInt() |= 5; #endif } public static void IntBitXorTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; p ^= 5; num ^= 5; Use(ref num); intField ^= 5; IntProp ^= 5; c.IntField ^= 5; c.IntProp ^= 5; s.IntField ^= 5; s.IntProp ^= 5; customClassField.IntField ^= 5; customClassField.IntProp ^= 5; otherCustomStructField.IntField ^= 5; otherCustomStructField.IntProp ^= 5; CustomClassProp.IntField ^= 5; CustomClassProp.IntProp ^= 5; GetClass().IntField ^= 5; GetClass().IntProp ^= 5; #if CS70 GetRefStruct().IntField ^= 5; GetRefStruct().IntProp ^= 5; GetRefInt() ^= 5; #endif } public static void IntPostIncTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; X(p++); X(num++); Use(ref num); X(intField++); X(IntProp++); X(c.IntField++); X(c.IntProp++); X(s.IntField++); X(s.IntProp++); X(customClassField.IntField++); X(customClassField.IntProp++); X(otherCustomStructField.IntField++); X(otherCustomStructField.IntProp++); X(CustomClassProp.IntField++); X(CustomClassProp.IntProp++); X(GetClass().IntField++); X(GetClass().IntProp++); #if CS70 X(GetRefStruct().IntField++); X(GetRefStruct().IntProp++); X(GetRefInt()++); #endif } public static void IntPreIncTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; X(++p); X(++num); Use(ref num); X(++intField); X(++IntProp); X(++c.IntField); X(++c.IntProp); X(++s.IntField); X(++s.IntProp); X(++customClassField.IntField); X(++customClassField.IntProp); X(++otherCustomStructField.IntField); X(++otherCustomStructField.IntProp); X(++CustomClassProp.IntField); X(++CustomClassProp.IntProp); X(++GetClass().IntField); X(++GetClass().IntProp); #if CS70 X(++GetRefStruct().IntField); X(++GetRefStruct().IntProp); X(++GetRefInt()); #endif } public static void IntPostDecTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; X(p--); X(num--); Use(ref num); X(intField--); X(IntProp--); X(c.IntField--); X(c.IntProp--); X(s.IntField--); X(s.IntProp--); X(customClassField.IntField--); X(customClassField.IntProp--); X(otherCustomStructField.IntField--); X(otherCustomStructField.IntProp--); X(CustomClassProp.IntField--); X(CustomClassProp.IntProp--); X(GetClass().IntField--); X(GetClass().IntProp--); #if CS70 X(GetRefStruct().IntField--); X(GetRefStruct().IntProp--); X(GetRefInt()--); #endif } public static void IntPreDecTest(int p, CustomClass c, CustomStruct2 s) { int num = 0; X(--p); X(--num); Use(ref num); X(--intField); X(--IntProp); X(--c.IntField); X(--c.IntProp); X(--s.IntField); X(--s.IntProp); X(--customClassField.IntField); X(--customClassField.IntProp); X(--otherCustomStructField.IntField); X(--otherCustomStructField.IntProp); X(--CustomClassProp.IntField); X(--CustomClassProp.IntProp); X(--GetClass().IntField); X(--GetClass().IntProp); #if CS70 X(--GetRefStruct().IntField); X(--GetRefStruct().IntProp); X(--GetRefInt()); #endif } public static void UintAddTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p += 5; num += 5; Use(ref num); uintField += 5u; UintProp += 5u; c.UintField += 5u; c.UintProp += 5u; s.UintField += 5u; s.UintProp += 5u; customClassField.UintField += 5u; customClassField.UintProp += 5u; otherCustomStructField.UintField += 5u; otherCustomStructField.UintProp += 5u; CustomClassProp.UintField += 5u; CustomClassProp.UintProp += 5u; GetClass().UintField += 5u; GetClass().UintProp += 5u; #if CS70 GetRefStruct().UintField += 5u; GetRefStruct().UintProp += 5u; GetRefUint() += 5u; #endif } public static void UintSubtractTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p -= 5; num -= 5; Use(ref num); uintField -= 5u; UintProp -= 5u; c.UintField -= 5u; c.UintProp -= 5u; s.UintField -= 5u; s.UintProp -= 5u; customClassField.UintField -= 5u; customClassField.UintProp -= 5u; otherCustomStructField.UintField -= 5u; otherCustomStructField.UintProp -= 5u; CustomClassProp.UintField -= 5u; CustomClassProp.UintProp -= 5u; GetClass().UintField -= 5u; GetClass().UintProp -= 5u; #if CS70 GetRefStruct().UintField -= 5u; GetRefStruct().UintProp -= 5u; GetRefUint() -= 5u; #endif } public static void UintMultiplyTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p *= 5; num *= 5; Use(ref num); uintField *= 5u; UintProp *= 5u; c.UintField *= 5u; c.UintProp *= 5u; s.UintField *= 5u; s.UintProp *= 5u; customClassField.UintField *= 5u; customClassField.UintProp *= 5u; otherCustomStructField.UintField *= 5u; otherCustomStructField.UintProp *= 5u; CustomClassProp.UintField *= 5u; CustomClassProp.UintProp *= 5u; GetClass().UintField *= 5u; GetClass().UintProp *= 5u; #if CS70 GetRefStruct().UintField *= 5u; GetRefStruct().UintProp *= 5u; GetRefUint() *= 5u; #endif } public static void UintDivideTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p /= 5; num /= 5; Use(ref num); uintField /= 5u; UintProp /= 5u; c.UintField /= 5u; c.UintProp /= 5u; s.UintField /= 5u; s.UintProp /= 5u; customClassField.UintField /= 5u; customClassField.UintProp /= 5u; otherCustomStructField.UintField /= 5u; otherCustomStructField.UintProp /= 5u; CustomClassProp.UintField /= 5u; CustomClassProp.UintProp /= 5u; GetClass().UintField /= 5u; GetClass().UintProp /= 5u; #if CS70 GetRefStruct().UintField /= 5u; GetRefStruct().UintProp /= 5u; GetRefUint() /= 5u; #endif } public static void UintModulusTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p %= 5; num %= 5; Use(ref num); uintField %= 5u; UintProp %= 5u; c.UintField %= 5u; c.UintProp %= 5u; s.UintField %= 5u; s.UintProp %= 5u; customClassField.UintField %= 5u; customClassField.UintProp %= 5u; otherCustomStructField.UintField %= 5u; otherCustomStructField.UintProp %= 5u; CustomClassProp.UintField %= 5u; CustomClassProp.UintProp %= 5u; GetClass().UintField %= 5u; GetClass().UintProp %= 5u; #if CS70 GetRefStruct().UintField %= 5u; GetRefStruct().UintProp %= 5u; GetRefUint() %= 5u; #endif } public static void UintLeftShiftTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p <<= 5; num <<= 5; Use(ref num); uintField <<= 5; UintProp <<= 5; c.UintField <<= 5; c.UintProp <<= 5; s.UintField <<= 5; s.UintProp <<= 5; customClassField.UintField <<= 5; customClassField.UintProp <<= 5; otherCustomStructField.UintField <<= 5; otherCustomStructField.UintProp <<= 5; CustomClassProp.UintField <<= 5; CustomClassProp.UintProp <<= 5; GetClass().UintField <<= 5; GetClass().UintProp <<= 5; #if CS70 GetRefStruct().UintField <<= 5; GetRefStruct().UintProp <<= 5; GetRefUint() <<= 5; #endif } public static void UintRightShiftTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p >>= 5; num >>= 5; Use(ref num); uintField >>= 5; UintProp >>= 5; c.UintField >>= 5; c.UintProp >>= 5; s.UintField >>= 5; s.UintProp >>= 5; customClassField.UintField >>= 5; customClassField.UintProp >>= 5; otherCustomStructField.UintField >>= 5; otherCustomStructField.UintProp >>= 5; CustomClassProp.UintField >>= 5; CustomClassProp.UintProp >>= 5; GetClass().UintField >>= 5; GetClass().UintProp >>= 5; #if CS70 GetRefStruct().UintField >>= 5; GetRefStruct().UintProp >>= 5; GetRefUint() >>= 5; #endif } public static void UintBitAndTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p &= 5; num &= 5; Use(ref num); uintField &= 5u; UintProp &= 5u; c.UintField &= 5u; c.UintProp &= 5u; s.UintField &= 5u; s.UintProp &= 5u; customClassField.UintField &= 5u; customClassField.UintProp &= 5u; otherCustomStructField.UintField &= 5u; otherCustomStructField.UintProp &= 5u; CustomClassProp.UintField &= 5u; CustomClassProp.UintProp &= 5u; GetClass().UintField &= 5u; GetClass().UintProp &= 5u; #if CS70 GetRefStruct().UintField &= 5u; GetRefStruct().UintProp &= 5u; GetRefUint() &= 5u; #endif } public static void UintBitOrTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p |= 5; num |= 5; Use(ref num); uintField |= 5u; UintProp |= 5u; c.UintField |= 5u; c.UintProp |= 5u; s.UintField |= 5u; s.UintProp |= 5u; customClassField.UintField |= 5u; customClassField.UintProp |= 5u; otherCustomStructField.UintField |= 5u; otherCustomStructField.UintProp |= 5u; CustomClassProp.UintField |= 5u; CustomClassProp.UintProp |= 5u; GetClass().UintField |= 5u; GetClass().UintProp |= 5u; #if CS70 GetRefStruct().UintField |= 5u; GetRefStruct().UintProp |= 5u; GetRefUint() |= 5u; #endif } public static void UintBitXorTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; p ^= 5; num ^= 5; Use(ref num); uintField ^= 5u; UintProp ^= 5u; c.UintField ^= 5u; c.UintProp ^= 5u; s.UintField ^= 5u; s.UintProp ^= 5u; customClassField.UintField ^= 5u; customClassField.UintProp ^= 5u; otherCustomStructField.UintField ^= 5u; otherCustomStructField.UintProp ^= 5u; CustomClassProp.UintField ^= 5u; CustomClassProp.UintProp ^= 5u; GetClass().UintField ^= 5u; GetClass().UintProp ^= 5u; #if CS70 GetRefStruct().UintField ^= 5u; GetRefStruct().UintProp ^= 5u; GetRefUint() ^= 5u; #endif } public static void UintPostIncTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; X(p++); X(num++); Use(ref num); X(uintField++); X(UintProp++); X(c.UintField++); X(c.UintProp++); X(s.UintField++); X(s.UintProp++); X(customClassField.UintField++); X(customClassField.UintProp++); X(otherCustomStructField.UintField++); X(otherCustomStructField.UintProp++); X(CustomClassProp.UintField++); X(CustomClassProp.UintProp++); X(GetClass().UintField++); X(GetClass().UintProp++); #if CS70 X(GetRefStruct().UintField++); X(GetRefStruct().UintProp++); X(GetRefUint()++); #endif } public static void UintPreIncTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; X(++p); X(++num); Use(ref num); X(++uintField); X(++UintProp); X(++c.UintField); X(++c.UintProp); X(++s.UintField); X(++s.UintProp); X(++customClassField.UintField); X(++customClassField.UintProp); X(++otherCustomStructField.UintField); X(++otherCustomStructField.UintProp); X(++CustomClassProp.UintField); X(++CustomClassProp.UintProp); X(++GetClass().UintField); X(++GetClass().UintProp); #if CS70 X(++GetRefStruct().UintField); X(++GetRefStruct().UintProp); X(++GetRefUint()); #endif } public static void UintPostDecTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; X(p--); X(num--); Use(ref num); X(uintField--); X(UintProp--); X(c.UintField--); X(c.UintProp--); X(s.UintField--); X(s.UintProp--); X(customClassField.UintField--); X(customClassField.UintProp--); X(otherCustomStructField.UintField--); X(otherCustomStructField.UintProp--); X(CustomClassProp.UintField--); X(CustomClassProp.UintProp--); X(GetClass().UintField--); X(GetClass().UintProp--); #if CS70 X(GetRefStruct().UintField--); X(GetRefStruct().UintProp--); X(GetRefUint()--); #endif } public static void UintPreDecTest(uint p, CustomClass c, CustomStruct2 s) { uint num = 0u; X(--p); X(--num); Use(ref num); X(--uintField); X(--UintProp); X(--c.UintField); X(--c.UintProp); X(--s.UintField); X(--s.UintProp); X(--customClassField.UintField); X(--customClassField.UintProp); X(--otherCustomStructField.UintField); X(--otherCustomStructField.UintProp); X(--CustomClassProp.UintField); X(--CustomClassProp.UintProp); X(--GetClass().UintField); X(--GetClass().UintProp); #if CS70 X(--GetRefStruct().UintField); X(--GetRefStruct().UintProp); X(--GetRefUint()); #endif } public static void LongAddTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p += 5; num += 5; Use(ref num); longField += 5L; LongProp += 5L; c.LongField += 5L; c.LongProp += 5L; s.LongField += 5L; s.LongProp += 5L; customClassField.LongField += 5L; customClassField.LongProp += 5L; otherCustomStructField.LongField += 5L; otherCustomStructField.LongProp += 5L; CustomClassProp.LongField += 5L; CustomClassProp.LongProp += 5L; GetClass().LongField += 5L; GetClass().LongProp += 5L; #if CS70 GetRefStruct().LongField += 5L; GetRefStruct().LongProp += 5L; GetRefLong() += 5L; #endif } public static void LongSubtractTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p -= 5; num -= 5; Use(ref num); longField -= 5L; LongProp -= 5L; c.LongField -= 5L; c.LongProp -= 5L; s.LongField -= 5L; s.LongProp -= 5L; customClassField.LongField -= 5L; customClassField.LongProp -= 5L; otherCustomStructField.LongField -= 5L; otherCustomStructField.LongProp -= 5L; CustomClassProp.LongField -= 5L; CustomClassProp.LongProp -= 5L; GetClass().LongField -= 5L; GetClass().LongProp -= 5L; #if CS70 GetRefStruct().LongField -= 5L; GetRefStruct().LongProp -= 5L; GetRefLong() -= 5L; #endif } public static void LongMultiplyTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p *= 5; num *= 5; Use(ref num); longField *= 5L; LongProp *= 5L; c.LongField *= 5L; c.LongProp *= 5L; s.LongField *= 5L; s.LongProp *= 5L; customClassField.LongField *= 5L; customClassField.LongProp *= 5L; otherCustomStructField.LongField *= 5L; otherCustomStructField.LongProp *= 5L; CustomClassProp.LongField *= 5L; CustomClassProp.LongProp *= 5L; GetClass().LongField *= 5L; GetClass().LongProp *= 5L; #if CS70 GetRefStruct().LongField *= 5L; GetRefStruct().LongProp *= 5L; GetRefLong() *= 5L; #endif } public static void LongDivideTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p /= 5; num /= 5; Use(ref num); longField /= 5L; LongProp /= 5L; c.LongField /= 5L; c.LongProp /= 5L; s.LongField /= 5L; s.LongProp /= 5L; customClassField.LongField /= 5L; customClassField.LongProp /= 5L; otherCustomStructField.LongField /= 5L; otherCustomStructField.LongProp /= 5L; CustomClassProp.LongField /= 5L; CustomClassProp.LongProp /= 5L; GetClass().LongField /= 5L; GetClass().LongProp /= 5L; #if CS70 GetRefStruct().LongField /= 5L; GetRefStruct().LongProp /= 5L; GetRefLong() /= 5L; #endif } public static void LongModulusTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p %= 5; num %= 5; Use(ref num); longField %= 5L; LongProp %= 5L; c.LongField %= 5L; c.LongProp %= 5L; s.LongField %= 5L; s.LongProp %= 5L; customClassField.LongField %= 5L; customClassField.LongProp %= 5L; otherCustomStructField.LongField %= 5L; otherCustomStructField.LongProp %= 5L; CustomClassProp.LongField %= 5L; CustomClassProp.LongProp %= 5L; GetClass().LongField %= 5L; GetClass().LongProp %= 5L; #if CS70 GetRefStruct().LongField %= 5L; GetRefStruct().LongProp %= 5L; GetRefLong() %= 5L; #endif } public static void LongLeftShiftTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p <<= 5; num <<= 5; Use(ref num); longField <<= 5; LongProp <<= 5; c.LongField <<= 5; c.LongProp <<= 5; s.LongField <<= 5; s.LongProp <<= 5; customClassField.LongField <<= 5; customClassField.LongProp <<= 5; otherCustomStructField.LongField <<= 5; otherCustomStructField.LongProp <<= 5; CustomClassProp.LongField <<= 5; CustomClassProp.LongProp <<= 5; GetClass().LongField <<= 5; GetClass().LongProp <<= 5; #if CS70 GetRefStruct().LongField <<= 5; GetRefStruct().LongProp <<= 5; GetRefLong() <<= 5; #endif } public static void LongRightShiftTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p >>= 5; num >>= 5; Use(ref num); longField >>= 5; LongProp >>= 5; c.LongField >>= 5; c.LongProp >>= 5; s.LongField >>= 5; s.LongProp >>= 5; customClassField.LongField >>= 5; customClassField.LongProp >>= 5; otherCustomStructField.LongField >>= 5; otherCustomStructField.LongProp >>= 5; CustomClassProp.LongField >>= 5; CustomClassProp.LongProp >>= 5; GetClass().LongField >>= 5; GetClass().LongProp >>= 5; #if CS70 GetRefStruct().LongField >>= 5; GetRefStruct().LongProp >>= 5; GetRefLong() >>= 5; #endif } public static void LongBitAndTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p &= 5; num &= 5; Use(ref num); longField &= 5L; LongProp &= 5L; c.LongField &= 5L; c.LongProp &= 5L; s.LongField &= 5L; s.LongProp &= 5L; customClassField.LongField &= 5L; customClassField.LongProp &= 5L; otherCustomStructField.LongField &= 5L; otherCustomStructField.LongProp &= 5L; CustomClassProp.LongField &= 5L; CustomClassProp.LongProp &= 5L; GetClass().LongField &= 5L; GetClass().LongProp &= 5L; #if CS70 GetRefStruct().LongField &= 5L; GetRefStruct().LongProp &= 5L; GetRefLong() &= 5L; #endif } public static void LongBitOrTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p |= 5; num |= 5; Use(ref num); longField |= 5L; LongProp |= 5L; c.LongField |= 5L; c.LongProp |= 5L; s.LongField |= 5L; s.LongProp |= 5L; customClassField.LongField |= 5L; customClassField.LongProp |= 5L; otherCustomStructField.LongField |= 5L; otherCustomStructField.LongProp |= 5L; CustomClassProp.LongField |= 5L; CustomClassProp.LongProp |= 5L; GetClass().LongField |= 5L; GetClass().LongProp |= 5L; #if CS70 GetRefStruct().LongField |= 5L; GetRefStruct().LongProp |= 5L; GetRefLong() |= 5L; #endif } public static void LongBitXorTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; p ^= 5; num ^= 5; Use(ref num); longField ^= 5L; LongProp ^= 5L; c.LongField ^= 5L; c.LongProp ^= 5L; s.LongField ^= 5L; s.LongProp ^= 5L; customClassField.LongField ^= 5L; customClassField.LongProp ^= 5L; otherCustomStructField.LongField ^= 5L; otherCustomStructField.LongProp ^= 5L; CustomClassProp.LongField ^= 5L; CustomClassProp.LongProp ^= 5L; GetClass().LongField ^= 5L; GetClass().LongProp ^= 5L; #if CS70 GetRefStruct().LongField ^= 5L; GetRefStruct().LongProp ^= 5L; GetRefLong() ^= 5L; #endif } public static void LongPostIncTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; X(p++); X(num++); Use(ref num); X(longField++); X(LongProp++); X(c.LongField++); X(c.LongProp++); X(s.LongField++); X(s.LongProp++); X(customClassField.LongField++); X(customClassField.LongProp++); X(otherCustomStructField.LongField++); X(otherCustomStructField.LongProp++); X(CustomClassProp.LongField++); X(CustomClassProp.LongProp++); X(GetClass().LongField++); X(GetClass().LongProp++); #if CS70 X(GetRefStruct().LongField++); X(GetRefStruct().LongProp++); X(GetRefLong()++); #endif } public static void LongPreIncTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; X(++p); X(++num); Use(ref num); X(++longField); X(++LongProp); X(++c.LongField); X(++c.LongProp); X(++s.LongField); X(++s.LongProp); X(++customClassField.LongField); X(++customClassField.LongProp); X(++otherCustomStructField.LongField); X(++otherCustomStructField.LongProp); X(++CustomClassProp.LongField); X(++CustomClassProp.LongProp); X(++GetClass().LongField); X(++GetClass().LongProp); #if CS70 X(++GetRefStruct().LongField); X(++GetRefStruct().LongProp); X(++GetRefLong()); #endif } public static void LongPostDecTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; X(p--); X(num--); Use(ref num); X(longField--); X(LongProp--); X(c.LongField--); X(c.LongProp--); X(s.LongField--); X(s.LongProp--); X(customClassField.LongField--); X(customClassField.LongProp--); X(otherCustomStructField.LongField--); X(otherCustomStructField.LongProp--); X(CustomClassProp.LongField--); X(CustomClassProp.LongProp--); X(GetClass().LongField--); X(GetClass().LongProp--); #if CS70 X(GetRefStruct().LongField--); X(GetRefStruct().LongProp--); X(GetRefLong()--); #endif } public static void LongPreDecTest(long p, CustomClass c, CustomStruct2 s) { long num = 0L; X(--p); X(--num); Use(ref num); X(--longField); X(--LongProp); X(--c.LongField); X(--c.LongProp); X(--s.LongField); X(--s.LongProp); X(--customClassField.LongField); X(--customClassField.LongProp); X(--otherCustomStructField.LongField); X(--otherCustomStructField.LongProp); X(--CustomClassProp.LongField); X(--CustomClassProp.LongProp); X(--GetClass().LongField); X(--GetClass().LongProp); #if CS70 X(--GetRefStruct().LongField); X(--GetRefStruct().LongProp); X(--GetRefLong()); #endif } public static void UlongAddTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p += 5; num += 5; Use(ref num); ulongField += 5uL; UlongProp += 5uL; c.UlongField += 5uL; c.UlongProp += 5uL; s.UlongField += 5uL; s.UlongProp += 5uL; customClassField.UlongField += 5uL; customClassField.UlongProp += 5uL; otherCustomStructField.UlongField += 5uL; otherCustomStructField.UlongProp += 5uL; CustomClassProp.UlongField += 5uL; CustomClassProp.UlongProp += 5uL; GetClass().UlongField += 5uL; GetClass().UlongProp += 5uL; #if CS70 GetRefStruct().UlongField += 5uL; GetRefStruct().UlongProp += 5uL; GetRefUlong() += 5uL; #endif } public static void UlongSubtractTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p -= 5; num -= 5; Use(ref num); ulongField -= 5uL; UlongProp -= 5uL; c.UlongField -= 5uL; c.UlongProp -= 5uL; s.UlongField -= 5uL; s.UlongProp -= 5uL; customClassField.UlongField -= 5uL; customClassField.UlongProp -= 5uL; otherCustomStructField.UlongField -= 5uL; otherCustomStructField.UlongProp -= 5uL; CustomClassProp.UlongField -= 5uL; CustomClassProp.UlongProp -= 5uL; GetClass().UlongField -= 5uL; GetClass().UlongProp -= 5uL; #if CS70 GetRefStruct().UlongField -= 5uL; GetRefStruct().UlongProp -= 5uL; GetRefUlong() -= 5uL; #endif } public static void UlongMultiplyTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p *= 5; num *= 5; Use(ref num); ulongField *= 5uL; UlongProp *= 5uL; c.UlongField *= 5uL; c.UlongProp *= 5uL; s.UlongField *= 5uL; s.UlongProp *= 5uL; customClassField.UlongField *= 5uL; customClassField.UlongProp *= 5uL; otherCustomStructField.UlongField *= 5uL; otherCustomStructField.UlongProp *= 5uL; CustomClassProp.UlongField *= 5uL; CustomClassProp.UlongProp *= 5uL; GetClass().UlongField *= 5uL; GetClass().UlongProp *= 5uL; #if CS70 GetRefStruct().UlongField *= 5uL; GetRefStruct().UlongProp *= 5uL; GetRefUlong() *= 5uL; #endif } public static void UlongDivideTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p /= 5; num /= 5; Use(ref num); ulongField /= 5uL; UlongProp /= 5uL; c.UlongField /= 5uL; c.UlongProp /= 5uL; s.UlongField /= 5uL; s.UlongProp /= 5uL; customClassField.UlongField /= 5uL; customClassField.UlongProp /= 5uL; otherCustomStructField.UlongField /= 5uL; otherCustomStructField.UlongProp /= 5uL; CustomClassProp.UlongField /= 5uL; CustomClassProp.UlongProp /= 5uL; GetClass().UlongField /= 5uL; GetClass().UlongProp /= 5uL; #if CS70 GetRefStruct().UlongField /= 5uL; GetRefStruct().UlongProp /= 5uL; GetRefUlong() /= 5uL; #endif } public static void UlongModulusTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p %= 5; num %= 5; Use(ref num); ulongField %= 5uL; UlongProp %= 5uL; c.UlongField %= 5uL; c.UlongProp %= 5uL; s.UlongField %= 5uL; s.UlongProp %= 5uL; customClassField.UlongField %= 5uL; customClassField.UlongProp %= 5uL; otherCustomStructField.UlongField %= 5uL; otherCustomStructField.UlongProp %= 5uL; CustomClassProp.UlongField %= 5uL; CustomClassProp.UlongProp %= 5uL; GetClass().UlongField %= 5uL; GetClass().UlongProp %= 5uL; #if CS70 GetRefStruct().UlongField %= 5uL; GetRefStruct().UlongProp %= 5uL; GetRefUlong() %= 5uL; #endif } public static void UlongLeftShiftTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p <<= 5; num <<= 5; Use(ref num); ulongField <<= 5; UlongProp <<= 5; c.UlongField <<= 5; c.UlongProp <<= 5; s.UlongField <<= 5; s.UlongProp <<= 5; customClassField.UlongField <<= 5; customClassField.UlongProp <<= 5; otherCustomStructField.UlongField <<= 5; otherCustomStructField.UlongProp <<= 5; CustomClassProp.UlongField <<= 5; CustomClassProp.UlongProp <<= 5; GetClass().UlongField <<= 5; GetClass().UlongProp <<= 5; #if CS70 GetRefStruct().UlongField <<= 5; GetRefStruct().UlongProp <<= 5; GetRefUlong() <<= 5; #endif } public static void UlongRightShiftTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p >>= 5; num >>= 5; Use(ref num); ulongField >>= 5; UlongProp >>= 5; c.UlongField >>= 5; c.UlongProp >>= 5; s.UlongField >>= 5; s.UlongProp >>= 5; customClassField.UlongField >>= 5; customClassField.UlongProp >>= 5; otherCustomStructField.UlongField >>= 5; otherCustomStructField.UlongProp >>= 5; CustomClassProp.UlongField >>= 5; CustomClassProp.UlongProp >>= 5; GetClass().UlongField >>= 5; GetClass().UlongProp >>= 5; #if CS70 GetRefStruct().UlongField >>= 5; GetRefStruct().UlongProp >>= 5; GetRefUlong() >>= 5; #endif } public static void UlongBitAndTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p &= 5; num &= 5; Use(ref num); ulongField &= 5uL; UlongProp &= 5uL; c.UlongField &= 5uL; c.UlongProp &= 5uL; s.UlongField &= 5uL; s.UlongProp &= 5uL; customClassField.UlongField &= 5uL; customClassField.UlongProp &= 5uL; otherCustomStructField.UlongField &= 5uL; otherCustomStructField.UlongProp &= 5uL; CustomClassProp.UlongField &= 5uL; CustomClassProp.UlongProp &= 5uL; GetClass().UlongField &= 5uL; GetClass().UlongProp &= 5uL; #if CS70 GetRefStruct().UlongField &= 5uL; GetRefStruct().UlongProp &= 5uL; GetRefUlong() &= 5uL; #endif } public static void UlongBitOrTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p |= 5; num |= 5; Use(ref num); ulongField |= 5uL; UlongProp |= 5uL; c.UlongField |= 5uL; c.UlongProp |= 5uL; s.UlongField |= 5uL; s.UlongProp |= 5uL; customClassField.UlongField |= 5uL; customClassField.UlongProp |= 5uL; otherCustomStructField.UlongField |= 5uL; otherCustomStructField.UlongProp |= 5uL; CustomClassProp.UlongField |= 5uL; CustomClassProp.UlongProp |= 5uL; GetClass().UlongField |= 5uL; GetClass().UlongProp |= 5uL; #if CS70 GetRefStruct().UlongField |= 5uL; GetRefStruct().UlongProp |= 5uL; GetRefUlong() |= 5uL; #endif } public static void UlongBitXorTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; p ^= 5; num ^= 5; Use(ref num); ulongField ^= 5uL; UlongProp ^= 5uL; c.UlongField ^= 5uL; c.UlongProp ^= 5uL; s.UlongField ^= 5uL; s.UlongProp ^= 5uL; customClassField.UlongField ^= 5uL; customClassField.UlongProp ^= 5uL; otherCustomStructField.UlongField ^= 5uL; otherCustomStructField.UlongProp ^= 5uL; CustomClassProp.UlongField ^= 5uL; CustomClassProp.UlongProp ^= 5uL; GetClass().UlongField ^= 5uL; GetClass().UlongProp ^= 5uL; #if CS70 GetRefStruct().UlongField ^= 5uL; GetRefStruct().UlongProp ^= 5uL; GetRefUlong() ^= 5uL; #endif } public static void UlongPostIncTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; X(p++); X(num++); Use(ref num); X(ulongField++); X(UlongProp++); X(c.UlongField++); X(c.UlongProp++); X(s.UlongField++); X(s.UlongProp++); X(customClassField.UlongField++); X(customClassField.UlongProp++); X(otherCustomStructField.UlongField++); X(otherCustomStructField.UlongProp++); X(CustomClassProp.UlongField++); X(CustomClassProp.UlongProp++); X(GetClass().UlongField++); X(GetClass().UlongProp++); #if CS70 X(GetRefStruct().UlongField++); X(GetRefStruct().UlongProp++); X(GetRefUlong()++); #endif } public static void UlongPreIncTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; X(++p); X(++num); Use(ref num); X(++ulongField); X(++UlongProp); X(++c.UlongField); X(++c.UlongProp); X(++s.UlongField); X(++s.UlongProp); X(++customClassField.UlongField); X(++customClassField.UlongProp); X(++otherCustomStructField.UlongField); X(++otherCustomStructField.UlongProp); X(++CustomClassProp.UlongField); X(++CustomClassProp.UlongProp); X(++GetClass().UlongField); X(++GetClass().UlongProp); #if CS70 X(++GetRefStruct().UlongField); X(++GetRefStruct().UlongProp); X(++GetRefUlong()); #endif } public static void UlongPostDecTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; X(p--); X(num--); Use(ref num); X(ulongField--); X(UlongProp--); X(c.UlongField--); X(c.UlongProp--); X(s.UlongField--); X(s.UlongProp--); X(customClassField.UlongField--); X(customClassField.UlongProp--); X(otherCustomStructField.UlongField--); X(otherCustomStructField.UlongProp--); X(CustomClassProp.UlongField--); X(CustomClassProp.UlongProp--); X(GetClass().UlongField--); X(GetClass().UlongProp--); #if CS70 X(GetRefStruct().UlongField--); X(GetRefStruct().UlongProp--); X(GetRefUlong()--); #endif } public static void UlongPreDecTest(ulong p, CustomClass c, CustomStruct2 s) { ulong num = 0uL; X(--p); X(--num); Use(ref num); X(--ulongField); X(--UlongProp); X(--c.UlongField); X(--c.UlongProp); X(--s.UlongField); X(--s.UlongProp); X(--customClassField.UlongField); X(--customClassField.UlongProp); X(--otherCustomStructField.UlongField); X(--otherCustomStructField.UlongProp); X(--CustomClassProp.UlongField); X(--CustomClassProp.UlongProp); X(--GetClass().UlongField); X(--GetClass().UlongProp); #if CS70 X(--GetRefStruct().UlongField); X(--GetRefStruct().UlongProp); X(--GetRefUlong()); #endif } public static void CustomClassAddTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p += (CustomClass)null; num += (CustomClass)null; Use(ref num); customClassField += (CustomClass)null; CustomClassProp += (CustomClass)null; c.CustomClassField += (CustomClass)null; c.CustomClassProp += (CustomClass)null; s.CustomClassField += (CustomClass)null; s.CustomClassProp += (CustomClass)null; customClassField.CustomClassField += (CustomClass)null; customClassField.CustomClassProp += (CustomClass)null; otherCustomStructField.CustomClassField += (CustomClass)null; otherCustomStructField.CustomClassProp += (CustomClass)null; CustomClassProp.CustomClassField += (CustomClass)null; CustomClassProp.CustomClassProp += (CustomClass)null; GetClass().CustomClassField += (CustomClass)null; GetClass().CustomClassProp += (CustomClass)null; #if CS70 GetRefStruct().CustomClassField += (CustomClass)null; GetRefStruct().CustomClassProp += (CustomClass)null; GetRefCustomClass() += (CustomClass)null; #endif } public static void CustomClassSubtractTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p -= (CustomClass)null; num -= (CustomClass)null; Use(ref num); customClassField -= (CustomClass)null; CustomClassProp -= (CustomClass)null; c.CustomClassField -= (CustomClass)null; c.CustomClassProp -= (CustomClass)null; s.CustomClassField -= (CustomClass)null; s.CustomClassProp -= (CustomClass)null; customClassField.CustomClassField -= (CustomClass)null; customClassField.CustomClassProp -= (CustomClass)null; otherCustomStructField.CustomClassField -= (CustomClass)null; otherCustomStructField.CustomClassProp -= (CustomClass)null; CustomClassProp.CustomClassField -= (CustomClass)null; CustomClassProp.CustomClassProp -= (CustomClass)null; GetClass().CustomClassField -= (CustomClass)null; GetClass().CustomClassProp -= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField -= (CustomClass)null; GetRefStruct().CustomClassProp -= (CustomClass)null; GetRefCustomClass() -= (CustomClass)null; #endif } public static void CustomClassMultiplyTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p *= (CustomClass)null; num *= (CustomClass)null; Use(ref num); customClassField *= (CustomClass)null; CustomClassProp *= (CustomClass)null; c.CustomClassField *= (CustomClass)null; c.CustomClassProp *= (CustomClass)null; s.CustomClassField *= (CustomClass)null; s.CustomClassProp *= (CustomClass)null; customClassField.CustomClassField *= (CustomClass)null; customClassField.CustomClassProp *= (CustomClass)null; otherCustomStructField.CustomClassField *= (CustomClass)null; otherCustomStructField.CustomClassProp *= (CustomClass)null; CustomClassProp.CustomClassField *= (CustomClass)null; CustomClassProp.CustomClassProp *= (CustomClass)null; GetClass().CustomClassField *= (CustomClass)null; GetClass().CustomClassProp *= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField *= (CustomClass)null; GetRefStruct().CustomClassProp *= (CustomClass)null; GetRefCustomClass() *= (CustomClass)null; #endif } public static void CustomClassDivideTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p /= (CustomClass)null; num /= (CustomClass)null; Use(ref num); customClassField /= (CustomClass)null; CustomClassProp /= (CustomClass)null; c.CustomClassField /= (CustomClass)null; c.CustomClassProp /= (CustomClass)null; s.CustomClassField /= (CustomClass)null; s.CustomClassProp /= (CustomClass)null; customClassField.CustomClassField /= (CustomClass)null; customClassField.CustomClassProp /= (CustomClass)null; otherCustomStructField.CustomClassField /= (CustomClass)null; otherCustomStructField.CustomClassProp /= (CustomClass)null; CustomClassProp.CustomClassField /= (CustomClass)null; CustomClassProp.CustomClassProp /= (CustomClass)null; GetClass().CustomClassField /= (CustomClass)null; GetClass().CustomClassProp /= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField /= (CustomClass)null; GetRefStruct().CustomClassProp /= (CustomClass)null; GetRefCustomClass() /= (CustomClass)null; #endif } public static void CustomClassModulusTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p %= (CustomClass)null; num %= (CustomClass)null; Use(ref num); customClassField %= (CustomClass)null; CustomClassProp %= (CustomClass)null; c.CustomClassField %= (CustomClass)null; c.CustomClassProp %= (CustomClass)null; s.CustomClassField %= (CustomClass)null; s.CustomClassProp %= (CustomClass)null; customClassField.CustomClassField %= (CustomClass)null; customClassField.CustomClassProp %= (CustomClass)null; otherCustomStructField.CustomClassField %= (CustomClass)null; otherCustomStructField.CustomClassProp %= (CustomClass)null; CustomClassProp.CustomClassField %= (CustomClass)null; CustomClassProp.CustomClassProp %= (CustomClass)null; GetClass().CustomClassField %= (CustomClass)null; GetClass().CustomClassProp %= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField %= (CustomClass)null; GetRefStruct().CustomClassProp %= (CustomClass)null; GetRefCustomClass() %= (CustomClass)null; #endif } public static void CustomClassLeftShiftTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p <<= 5; num <<= 5; Use(ref num); customClassField <<= 5; CustomClassProp <<= 5; c.CustomClassField <<= 5; c.CustomClassProp <<= 5; s.CustomClassField <<= 5; s.CustomClassProp <<= 5; customClassField.CustomClassField <<= 5; customClassField.CustomClassProp <<= 5; otherCustomStructField.CustomClassField <<= 5; otherCustomStructField.CustomClassProp <<= 5; CustomClassProp.CustomClassField <<= 5; CustomClassProp.CustomClassProp <<= 5; GetClass().CustomClassField <<= 5; GetClass().CustomClassProp <<= 5; #if CS70 GetRefStruct().CustomClassField <<= 5; GetRefStruct().CustomClassProp <<= 5; GetRefCustomClass() <<= 5; #endif } public static void CustomClassRightShiftTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p >>= 5; num >>= 5; Use(ref num); customClassField >>= 5; CustomClassProp >>= 5; c.CustomClassField >>= 5; c.CustomClassProp >>= 5; s.CustomClassField >>= 5; s.CustomClassProp >>= 5; customClassField.CustomClassField >>= 5; customClassField.CustomClassProp >>= 5; otherCustomStructField.CustomClassField >>= 5; otherCustomStructField.CustomClassProp >>= 5; CustomClassProp.CustomClassField >>= 5; CustomClassProp.CustomClassProp >>= 5; GetClass().CustomClassField >>= 5; GetClass().CustomClassProp >>= 5; #if CS70 GetRefStruct().CustomClassField >>= 5; GetRefStruct().CustomClassProp >>= 5; GetRefCustomClass() >>= 5; #endif } public static void CustomClassBitAndTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p &= (CustomClass)null; num &= (CustomClass)null; Use(ref num); customClassField &= (CustomClass)null; CustomClassProp &= (CustomClass)null; c.CustomClassField &= (CustomClass)null; c.CustomClassProp &= (CustomClass)null; s.CustomClassField &= (CustomClass)null; s.CustomClassProp &= (CustomClass)null; customClassField.CustomClassField &= (CustomClass)null; customClassField.CustomClassProp &= (CustomClass)null; otherCustomStructField.CustomClassField &= (CustomClass)null; otherCustomStructField.CustomClassProp &= (CustomClass)null; CustomClassProp.CustomClassField &= (CustomClass)null; CustomClassProp.CustomClassProp &= (CustomClass)null; GetClass().CustomClassField &= (CustomClass)null; GetClass().CustomClassProp &= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField &= (CustomClass)null; GetRefStruct().CustomClassProp &= (CustomClass)null; GetRefCustomClass() &= (CustomClass)null; #endif } public static void CustomClassBitOrTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p |= (CustomClass)null; num |= (CustomClass)null; Use(ref num); customClassField |= (CustomClass)null; CustomClassProp |= (CustomClass)null; c.CustomClassField |= (CustomClass)null; c.CustomClassProp |= (CustomClass)null; s.CustomClassField |= (CustomClass)null; s.CustomClassProp |= (CustomClass)null; customClassField.CustomClassField |= (CustomClass)null; customClassField.CustomClassProp |= (CustomClass)null; otherCustomStructField.CustomClassField |= (CustomClass)null; otherCustomStructField.CustomClassProp |= (CustomClass)null; CustomClassProp.CustomClassField |= (CustomClass)null; CustomClassProp.CustomClassProp |= (CustomClass)null; GetClass().CustomClassField |= (CustomClass)null; GetClass().CustomClassProp |= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField |= (CustomClass)null; GetRefStruct().CustomClassProp |= (CustomClass)null; GetRefCustomClass() |= (CustomClass)null; #endif } public static void CustomClassBitXorTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; p ^= (CustomClass)null; num ^= (CustomClass)null; Use(ref num); customClassField ^= (CustomClass)null; CustomClassProp ^= (CustomClass)null; c.CustomClassField ^= (CustomClass)null; c.CustomClassProp ^= (CustomClass)null; s.CustomClassField ^= (CustomClass)null; s.CustomClassProp ^= (CustomClass)null; customClassField.CustomClassField ^= (CustomClass)null; customClassField.CustomClassProp ^= (CustomClass)null; otherCustomStructField.CustomClassField ^= (CustomClass)null; otherCustomStructField.CustomClassProp ^= (CustomClass)null; CustomClassProp.CustomClassField ^= (CustomClass)null; CustomClassProp.CustomClassProp ^= (CustomClass)null; GetClass().CustomClassField ^= (CustomClass)null; GetClass().CustomClassProp ^= (CustomClass)null; #if CS70 GetRefStruct().CustomClassField ^= (CustomClass)null; GetRefStruct().CustomClassProp ^= (CustomClass)null; GetRefCustomClass() ^= (CustomClass)null; #endif } public static void CustomClassPostIncTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; X(p++); X(num++); Use(ref num); X(customClassField++); X(CustomClassProp++); X(c.CustomClassField++); X(c.CustomClassProp++); X(s.CustomClassField++); X(s.CustomClassProp++); X(customClassField.CustomClassField++); X(customClassField.CustomClassProp++); X(otherCustomStructField.CustomClassField++); X(otherCustomStructField.CustomClassProp++); X(CustomClassProp.CustomClassField++); X(CustomClassProp.CustomClassProp++); X(GetClass().CustomClassField++); X(GetClass().CustomClassProp++); #if CS70 X(GetRefStruct().CustomClassField++); X(GetRefStruct().CustomClassProp++); X(GetRefCustomClass()++); #endif } public static void CustomClassPreIncTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; X(++p); X(++num); Use(ref num); X(++customClassField); X(++CustomClassProp); X(++c.CustomClassField); X(++c.CustomClassProp); X(++s.CustomClassField); X(++s.CustomClassProp); X(++customClassField.CustomClassField); X(++customClassField.CustomClassProp); X(++otherCustomStructField.CustomClassField); X(++otherCustomStructField.CustomClassProp); X(++CustomClassProp.CustomClassField); X(++CustomClassProp.CustomClassProp); X(++GetClass().CustomClassField); X(++GetClass().CustomClassProp); #if CS70 X(++GetRefStruct().CustomClassField); X(++GetRefStruct().CustomClassProp); X(++GetRefCustomClass()); #endif } public static void CustomClassPostDecTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; X(p--); X(num--); Use(ref num); X(customClassField--); X(CustomClassProp--); X(c.CustomClassField--); X(c.CustomClassProp--); X(s.CustomClassField--); X(s.CustomClassProp--); X(customClassField.CustomClassField--); X(customClassField.CustomClassProp--); X(otherCustomStructField.CustomClassField--); X(otherCustomStructField.CustomClassProp--); X(CustomClassProp.CustomClassField--); X(CustomClassProp.CustomClassProp--); X(GetClass().CustomClassField--); X(GetClass().CustomClassProp--); #if CS70 X(GetRefStruct().CustomClassField--); X(GetRefStruct().CustomClassProp--); X(GetRefCustomClass()--); #endif } public static void CustomClassPreDecTest(CustomClass p, CustomClass c, CustomStruct2 s) { CustomClass num = null; X(--p); X(--num); Use(ref num); X(--customClassField); X(--CustomClassProp); X(--c.CustomClassField); X(--c.CustomClassProp); X(--s.CustomClassField); X(--s.CustomClassProp); X(--customClassField.CustomClassField); X(--customClassField.CustomClassProp); X(--otherCustomStructField.CustomClassField); X(--otherCustomStructField.CustomClassProp); X(--CustomClassProp.CustomClassField); X(--CustomClassProp.CustomClassProp); X(--GetClass().CustomClassField); X(--GetClass().CustomClassProp); #if CS70 X(--GetRefStruct().CustomClassField); X(--GetRefStruct().CustomClassProp); X(--GetRefCustomClass()); #endif } public static void CustomStructAddTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p += default(CustomStruct); num += default(CustomStruct); Use(ref num); customStructField += default(CustomStruct); CustomStructProp += default(CustomStruct); c.CustomStructField += default(CustomStruct); c.CustomStructProp += default(CustomStruct); s.CustomStructField += default(CustomStruct); s.CustomStructProp += default(CustomStruct); customClassField.CustomStructField += default(CustomStruct); customClassField.CustomStructProp += default(CustomStruct); otherCustomStructField.CustomStructField += default(CustomStruct); otherCustomStructField.CustomStructProp += default(CustomStruct); CustomClassProp.CustomStructField += default(CustomStruct); CustomClassProp.CustomStructProp += default(CustomStruct); GetClass().CustomStructField += default(CustomStruct); GetClass().CustomStructProp += default(CustomStruct); #if CS70 GetRefStruct().CustomStructField += default(CustomStruct); GetRefStruct().CustomStructProp += default(CustomStruct); GetRefCustomStruct() += default(CustomStruct); #endif } public static void CustomStructSubtractTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p -= default(CustomStruct); num -= default(CustomStruct); Use(ref num); customStructField -= default(CustomStruct); CustomStructProp -= default(CustomStruct); c.CustomStructField -= default(CustomStruct); c.CustomStructProp -= default(CustomStruct); s.CustomStructField -= default(CustomStruct); s.CustomStructProp -= default(CustomStruct); customClassField.CustomStructField -= default(CustomStruct); customClassField.CustomStructProp -= default(CustomStruct); otherCustomStructField.CustomStructField -= default(CustomStruct); otherCustomStructField.CustomStructProp -= default(CustomStruct); CustomClassProp.CustomStructField -= default(CustomStruct); CustomClassProp.CustomStructProp -= default(CustomStruct); GetClass().CustomStructField -= default(CustomStruct); GetClass().CustomStructProp -= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField -= default(CustomStruct); GetRefStruct().CustomStructProp -= default(CustomStruct); GetRefCustomStruct() -= default(CustomStruct); #endif } public static void CustomStructMultiplyTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p *= default(CustomStruct); num *= default(CustomStruct); Use(ref num); customStructField *= default(CustomStruct); CustomStructProp *= default(CustomStruct); c.CustomStructField *= default(CustomStruct); c.CustomStructProp *= default(CustomStruct); s.CustomStructField *= default(CustomStruct); s.CustomStructProp *= default(CustomStruct); customClassField.CustomStructField *= default(CustomStruct); customClassField.CustomStructProp *= default(CustomStruct); otherCustomStructField.CustomStructField *= default(CustomStruct); otherCustomStructField.CustomStructProp *= default(CustomStruct); CustomClassProp.CustomStructField *= default(CustomStruct); CustomClassProp.CustomStructProp *= default(CustomStruct); GetClass().CustomStructField *= default(CustomStruct); GetClass().CustomStructProp *= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField *= default(CustomStruct); GetRefStruct().CustomStructProp *= default(CustomStruct); GetRefCustomStruct() *= default(CustomStruct); #endif } public static void CustomStructDivideTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p /= default(CustomStruct); num /= default(CustomStruct); Use(ref num); customStructField /= default(CustomStruct); CustomStructProp /= default(CustomStruct); c.CustomStructField /= default(CustomStruct); c.CustomStructProp /= default(CustomStruct); s.CustomStructField /= default(CustomStruct); s.CustomStructProp /= default(CustomStruct); customClassField.CustomStructField /= default(CustomStruct); customClassField.CustomStructProp /= default(CustomStruct); otherCustomStructField.CustomStructField /= default(CustomStruct); otherCustomStructField.CustomStructProp /= default(CustomStruct); CustomClassProp.CustomStructField /= default(CustomStruct); CustomClassProp.CustomStructProp /= default(CustomStruct); GetClass().CustomStructField /= default(CustomStruct); GetClass().CustomStructProp /= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField /= default(CustomStruct); GetRefStruct().CustomStructProp /= default(CustomStruct); GetRefCustomStruct() /= default(CustomStruct); #endif } public static void CustomStructModulusTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p %= default(CustomStruct); num %= default(CustomStruct); Use(ref num); customStructField %= default(CustomStruct); CustomStructProp %= default(CustomStruct); c.CustomStructField %= default(CustomStruct); c.CustomStructProp %= default(CustomStruct); s.CustomStructField %= default(CustomStruct); s.CustomStructProp %= default(CustomStruct); customClassField.CustomStructField %= default(CustomStruct); customClassField.CustomStructProp %= default(CustomStruct); otherCustomStructField.CustomStructField %= default(CustomStruct); otherCustomStructField.CustomStructProp %= default(CustomStruct); CustomClassProp.CustomStructField %= default(CustomStruct); CustomClassProp.CustomStructProp %= default(CustomStruct); GetClass().CustomStructField %= default(CustomStruct); GetClass().CustomStructProp %= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField %= default(CustomStruct); GetRefStruct().CustomStructProp %= default(CustomStruct); GetRefCustomStruct() %= default(CustomStruct); #endif } public static void CustomStructLeftShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p <<= 5; num <<= 5; Use(ref num); customStructField <<= 5; CustomStructProp <<= 5; c.CustomStructField <<= 5; c.CustomStructProp <<= 5; s.CustomStructField <<= 5; s.CustomStructProp <<= 5; customClassField.CustomStructField <<= 5; customClassField.CustomStructProp <<= 5; otherCustomStructField.CustomStructField <<= 5; otherCustomStructField.CustomStructProp <<= 5; CustomClassProp.CustomStructField <<= 5; CustomClassProp.CustomStructProp <<= 5; GetClass().CustomStructField <<= 5; GetClass().CustomStructProp <<= 5; #if CS70 GetRefStruct().CustomStructField <<= 5; GetRefStruct().CustomStructProp <<= 5; GetRefCustomStruct() <<= 5; #endif } public static void CustomStructRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p >>= 5; num >>= 5; Use(ref num); customStructField >>= 5; CustomStructProp >>= 5; c.CustomStructField >>= 5; c.CustomStructProp >>= 5; s.CustomStructField >>= 5; s.CustomStructProp >>= 5; customClassField.CustomStructField >>= 5; customClassField.CustomStructProp >>= 5; otherCustomStructField.CustomStructField >>= 5; otherCustomStructField.CustomStructProp >>= 5; CustomClassProp.CustomStructField >>= 5; CustomClassProp.CustomStructProp >>= 5; GetClass().CustomStructField >>= 5; GetClass().CustomStructProp >>= 5; #if CS70 GetRefStruct().CustomStructField >>= 5; GetRefStruct().CustomStructProp >>= 5; GetRefCustomStruct() >>= 5; #endif } #if CS110 public static void CustomStructUnsignedRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) { //CustomStruct l = default(CustomStruct); //p >>>= 5; //l >>>= 5; customStructField >>>= 5; CustomStructProp >>>= 5; c.CustomStructField >>>= 5; c.CustomStructProp >>>= 5; s.CustomStructField >>>= 5; s.CustomStructProp >>>= 5; customClassField.CustomStructField >>>= 5; customClassField.CustomStructProp >>>= 5; otherCustomStructField.CustomStructField >>>= 5; otherCustomStructField.CustomStructProp >>>= 5; CustomClassProp.CustomStructField >>>= 5; CustomClassProp.CustomStructProp >>>= 5; GetClass().CustomStructField >>>= 5; GetClass().CustomStructProp >>>= 5; GetRefStruct().CustomStructField >>>= 5; GetRefStruct().CustomStructProp >>>= 5; GetRefCustomStruct() >>>= 5; } #endif public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p &= default(CustomStruct); num &= default(CustomStruct); Use(ref num); customStructField &= default(CustomStruct); CustomStructProp &= default(CustomStruct); c.CustomStructField &= default(CustomStruct); c.CustomStructProp &= default(CustomStruct); s.CustomStructField &= default(CustomStruct); s.CustomStructProp &= default(CustomStruct); customClassField.CustomStructField &= default(CustomStruct); customClassField.CustomStructProp &= default(CustomStruct); otherCustomStructField.CustomStructField &= default(CustomStruct); otherCustomStructField.CustomStructProp &= default(CustomStruct); CustomClassProp.CustomStructField &= default(CustomStruct); CustomClassProp.CustomStructProp &= default(CustomStruct); GetClass().CustomStructField &= default(CustomStruct); GetClass().CustomStructProp &= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField &= default(CustomStruct); GetRefStruct().CustomStructProp &= default(CustomStruct); GetRefCustomStruct() &= default(CustomStruct); #endif } public static void CustomStructBitOrTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p |= default(CustomStruct); num |= default(CustomStruct); Use(ref num); customStructField |= default(CustomStruct); CustomStructProp |= default(CustomStruct); c.CustomStructField |= default(CustomStruct); c.CustomStructProp |= default(CustomStruct); s.CustomStructField |= default(CustomStruct); s.CustomStructProp |= default(CustomStruct); customClassField.CustomStructField |= default(CustomStruct); customClassField.CustomStructProp |= default(CustomStruct); otherCustomStructField.CustomStructField |= default(CustomStruct); otherCustomStructField.CustomStructProp |= default(CustomStruct); CustomClassProp.CustomStructField |= default(CustomStruct); CustomClassProp.CustomStructProp |= default(CustomStruct); GetClass().CustomStructField |= default(CustomStruct); GetClass().CustomStructProp |= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField |= default(CustomStruct); GetRefStruct().CustomStructProp |= default(CustomStruct); GetRefCustomStruct() |= default(CustomStruct); #endif } public static void CustomStructBitXorTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); p ^= default(CustomStruct); num ^= default(CustomStruct); Use(ref num); customStructField ^= default(CustomStruct); CustomStructProp ^= default(CustomStruct); c.CustomStructField ^= default(CustomStruct); c.CustomStructProp ^= default(CustomStruct); s.CustomStructField ^= default(CustomStruct); s.CustomStructProp ^= default(CustomStruct); customClassField.CustomStructField ^= default(CustomStruct); customClassField.CustomStructProp ^= default(CustomStruct); otherCustomStructField.CustomStructField ^= default(CustomStruct); otherCustomStructField.CustomStructProp ^= default(CustomStruct); CustomClassProp.CustomStructField ^= default(CustomStruct); CustomClassProp.CustomStructProp ^= default(CustomStruct); GetClass().CustomStructField ^= default(CustomStruct); GetClass().CustomStructProp ^= default(CustomStruct); #if CS70 GetRefStruct().CustomStructField ^= default(CustomStruct); GetRefStruct().CustomStructProp ^= default(CustomStruct); GetRefCustomStruct() ^= default(CustomStruct); #endif } public static void CustomStructPostIncTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); X(p++); X(num++); Use(ref num); X(customStructField++); X(CustomStructProp++); X(c.CustomStructField++); X(c.CustomStructProp++); X(s.CustomStructField++); X(s.CustomStructProp++); X(customClassField.CustomStructField++); X(customClassField.CustomStructProp++); X(otherCustomStructField.CustomStructField++); X(otherCustomStructField.CustomStructProp++); X(CustomClassProp.CustomStructField++); X(CustomClassProp.CustomStructProp++); X(GetClass().CustomStructField++); X(GetClass().CustomStructProp++); #if CS70 X(GetRefStruct().CustomStructField++); X(GetRefStruct().CustomStructProp++); X(GetRefCustomStruct()++); #endif } public static void CustomStructPreIncTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); X(++p); X(++num); Use(ref num); X(++customStructField); X(++CustomStructProp); X(++c.CustomStructField); X(++c.CustomStructProp); X(++s.CustomStructField); X(++s.CustomStructProp); X(++customClassField.CustomStructField); X(++customClassField.CustomStructProp); X(++otherCustomStructField.CustomStructField); X(++otherCustomStructField.CustomStructProp); X(++CustomClassProp.CustomStructField); X(++CustomClassProp.CustomStructProp); X(++GetClass().CustomStructField); X(++GetClass().CustomStructProp); #if CS70 X(++GetRefStruct().CustomStructField); X(++GetRefStruct().CustomStructProp); X(++GetRefCustomStruct()); #endif } public static void CustomStructPostDecTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); X(p--); X(num--); Use(ref num); X(customStructField--); X(CustomStructProp--); X(c.CustomStructField--); X(c.CustomStructProp--); X(s.CustomStructField--); X(s.CustomStructProp--); X(customClassField.CustomStructField--); X(customClassField.CustomStructProp--); X(otherCustomStructField.CustomStructField--); X(otherCustomStructField.CustomStructProp--); X(CustomClassProp.CustomStructField--); X(CustomClassProp.CustomStructProp--); X(GetClass().CustomStructField--); X(GetClass().CustomStructProp--); #if CS70 X(GetRefStruct().CustomStructField--); X(GetRefStruct().CustomStructProp--); X(GetRefCustomStruct()--); #endif } public static void CustomStructPreDecTest(CustomStruct p, CustomClass c, CustomStruct2 s) { CustomStruct num = default(CustomStruct); X(--p); X(--num); Use(ref num); X(--customStructField); X(--CustomStructProp); X(--c.CustomStructField); X(--c.CustomStructProp); X(--s.CustomStructField); X(--s.CustomStructProp); X(--customClassField.CustomStructField); X(--customClassField.CustomStructProp); X(--otherCustomStructField.CustomStructField); X(--otherCustomStructField.CustomStructProp); X(--CustomClassProp.CustomStructField); X(--CustomClassProp.CustomStructProp); X(--GetClass().CustomStructField); X(--GetClass().CustomStructProp); #if CS70 X(--GetRefStruct().CustomStructField); X(--GetRefStruct().CustomStructProp); X(--GetRefCustomStruct()); #endif } #endregion public static void AddOneToCustomClass(ref CustomClass c) { // This should not be turned into post-increment: c += 1; c.CustomClassProp += 1; } private static Item GetItem(object obj) { return null; } private static void Issue882() { Item item = GetItem(null); item.Self = item; } private void Issue954(ref MyEnum a, MyEnum b) { // cannot decompile to: "a %= b;", because the % operator does not apply to enums a = (MyEnum)((int)a % (int)b); // same with enum field: enumField = (MyEnum)((int)enumField % (int)b); } private void Issue588(ushort val) { ushortDict.Add(ushortField++, val); } private void Issue1007(TimeSpan[] items, int startIndex, TimeSpan item) { int num = startIndex; items[num++] = item; items[num++] = item; } #if !LEGACY_CSC // Legacy csc generates a slightly different pattern for string compound assignment // as for all other compound assignments. We'll ignore that edge case. // Note: it's possible that the pre-CopyPropagation run of TransformAssignments is causing trouble there, // and that the compound assignment transform would be fine if it didn't get disrupted. private static void Issue1082(string[] strings, List chars, bool flag, int i) { // The 'chars[i]' result is stored in a temporary, and both branches use the // same temporary. In order to inline the generated value-type temporary, we // need to split it, even though it has the address taken for the ToString() call. if (flag) { strings[1] += chars[i]; } else { strings[0] += chars[i]; } } #endif private static void StringPropertyCompoundAssign(char c) { StaticStringProperty += "a"; StaticStringProperty += 1; StaticStringProperty += c; new CustomClass().StringProp += "a"; new CustomClass().StringProp += 1; new CustomClass().StringProp += c; } public uint PreIncrementIndexer(string name) { return ++M()[name]; } public int PreIncrementByRef(ref int i) { return ++i; } public unsafe int PreIncrementByPointer() { return ++(*GetPointer()); } public unsafe int PreIncrementOfPointer(int* ptr) { return *(++ptr); } public int PreIncrement2DArray() { return ++Array()[1, 2]; } public int CompoundAssignInstanceField() { return M().Field *= 10; } public int CompoundAssignInstanceProperty() { return M().Property *= 10; } public int CompoundAssignStaticField() { return StaticField ^= 100; } public int CompoundAssignStaticProperty() { return StaticProperty &= 10; } public int CompoundAssignArrayElement1(int[] array, int pos) { return array[pos] *= 10; } public int CompoundAssignArrayElement2(int[] array) { return array[Environment.TickCount] *= 10; } public uint CompoundAssignIndexer(string name) { return M()[name] -= 2u; } public uint CompoundAssignIndexerComplexIndex() { return M()[ToString()] -= 2u; } public int CompoundAssignIncrement2DArray() { return Array()[1, 2] %= 10; } public int CompoundAssignByRef(ref int i) { return i <<= 2; } public unsafe int* CompoundAssignOfPointer(int* ptr) { return ptr += 10; } public unsafe double CompoundAssignByPointer(double* ptr) { return *ptr /= 1.5; } public void CompoundAssignEnum() { enumField |= MyEnum.Two; enumField &= ~MyEnum.Four; } public int PostIncrementInAddition(int i, int j) { return i++ + j; } public void PostIncrementInlineLocalVariable(Func f) { int num = 0; f(num++); } public int PostDecrementArrayElement(int[] array, int pos) { return array[pos]--; } public uint PostIncrementIndexer(string name) { return M()[name]++; } public unsafe int PostIncrementOfPointer(int* ptr) { return *(ptr++); } public unsafe int PostIncrementOfSmallIntegerPointerDereference(byte* ptr) { return (*ptr)++ * (*ptr)++; } public unsafe int PreIncrementOfSmallIntegerPointerDereference(byte* ptr) { return ++(*ptr) * ++(*ptr); } public unsafe int CompoundAssignSmallIntegerPointerDereference(byte* ptr) { return (*ptr += 5) * (*ptr += 5); } public int PostDecrementInstanceField() { return M().Field--; } public int PostDecrementInstanceProperty() { return M().Property--; } public int PostIncrement2DArray() { return Array()[StaticField, StaticProperty]++; } public int PostIncrementByRef(ref int i) { return i++; } public unsafe int PostIncrementByPointer() { return (*GetPointer())++; } public float PostIncrementFloat(float f) { return f++; } public double PostIncrementDouble(double d) { return d++; } public void Issue1552Pre(CustomStruct a, CustomStruct b) { CustomStruct customStruct = a + b; Console.WriteLine(++customStruct); } public void Issue1552Stmt(CustomStruct a, CustomStruct b) { CustomStruct customStruct = a + b; ++customStruct; } public void Issue1552StmtUseLater(CustomStruct a, CustomStruct b) { CustomStruct customStruct = a + b; ++customStruct; Console.WriteLine(); Console.WriteLine(customStruct * b); } public void Issue1552Decimal(decimal a) { // Legacy csc compiles this using op_Increment, // ensure we don't misdetect this as an invalid pre-increment "++(a * 10m)" Console.WriteLine(a * 10m + 1m); } #if !(ROSLYN && OPT) // Roslyn opt no longer has a detectable post-increment pattern // due to optimizing out some of the stores. // Our emitted code is valid but has some additional temporaries. public void Issue1552Post(CustomStruct a, CustomStruct b) { CustomStruct customStruct = a + b; Console.WriteLine(customStruct++); } public void Issue1552StmtTwice(CustomStruct a, CustomStruct b) { CustomStruct customStruct = a + b; ++customStruct; ++customStruct; } #endif public void Issue1779(int value) { CustomStruct2 customStruct = GetStruct(); customStruct.IntProp += value; } #if ROSLYN2 public static string PreIncrementWithMethodCall(int value) { return (++value).ToString(); } #endif #if CS72 public static string PreIncrementWithInParameter(int value) { PreIncrementWithInParameter_Helper(++value); return value.ToString(); } public static void PreIncrementWithInParameter_Helper(in int value) { } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs ================================================ #if !(CS110 && NET70) using System; #endif using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class ConstantsTests { #if CS90 public nint? NullableNInt() { return null; } public nuint? NullableNUInt() { return null; } #endif #if !(CS110 && NET70) public IntPtr? NullableIntPtr() { return null; } public UIntPtr? NullableUIntPtr() { return null; } #endif public ulong Issue1308(ulong u = 8uL) { Test((u & 0xFFFFFFFFu) != 0); return 18446744069414584320uL; } public void Byte_BitmaskingInCondition(byte v) { Test((v & 0xF) == 0); Test((v & 0x123) == 0); Test((v | 0xF) == 0); Test((v | 0x123) == 0); } public void SByte_BitmaskingInCondition(sbyte v) { Test((v & 0xF) == 0); Test((v & 0x123) == 0); Test((v | 0xF) == 0); Test((v | 0x123) == 0); } public void Enum_Flag_Check(TaskCreationOptions v) { Test((v & TaskCreationOptions.AttachedToParent) != 0); Test((v & TaskCreationOptions.AttachedToParent) == 0); } private void Test(bool expr) { } private void Test(decimal expr) { } public void Decimal() { // Roslyn and legacy csc both normalize the decimal constant references, // but to a different representation (ctor call vs. field use) Test(0m); Test(1m); Test(-1m); Test(decimal.MinValue); Test(decimal.MaxValue); } public void BitwiseAndWithConstantUInt64(ulong a) { ExpectUInt64(a & 7); ExpectUInt64(a & 0x7FFFFFFF); ExpectUInt64(a & 0xFFFFFFFFu); ExpectUInt64(a & 0x7FFFFFFFFFFFFFFFL); ExpectUInt64(a & 0xFFFFFFFFFFFFFFFFuL); } public void BitwiseAndWithConstantInt64(long a) { ExpectInt64(a & 7); ExpectInt64(a & 0x7FFFFFFF); ExpectInt64(a & 0xFFFFFFFFu); ExpectInt64(a & 0x7FFFFFFFFFFFFFFFL); } public void BitwiseAndWithConstantUInt32(uint a) { ExpectUInt32(a & 7); ExpectUInt32(a & 0x7FFFFFFF); ExpectUInt32(a & 0xFFFFFFFFu); } public void BitwiseAndWithConstantInt32(int a) { ExpectInt32(a & 7); ExpectInt32(a & 0x7FFFFFFF); } public void BitwiseAndWithConstantUInt16(ushort a) { ExpectUInt16((ushort)(a & 7)); ExpectUInt16((ushort)(a & 0x7FFF)); ExpectUInt16((ushort)(a & 0xFFFF)); } public void BitwiseAndWithConstantInt16(short a) { ExpectInt16((short)(a & 7)); ExpectInt16((short)(a & 0x7FFF)); } public void BitwiseAndWithConstantUInt8(byte a) { ExpectUInt8((byte)(a & 7)); ExpectUInt8((byte)(a & 0x7F)); ExpectUInt8((byte)(a & 0xFF)); } public void BitwiseAndWithConstantInt8(sbyte a) { ExpectInt8((sbyte)(a & 7)); ExpectInt8((sbyte)(a & 0x7F)); } public void BitwiseOrWithConstantUInt64(ulong a) { ExpectUInt64(a | 7); ExpectUInt64(a | 0x7FFFFFFF); ExpectUInt64(a | 0xFFFFFFFFu); ExpectUInt64(a | 0x7FFFFFFFFFFFFFFFL); ExpectUInt64(a | 0xFFFFFFFFFFFFFFFFuL); } public void BitwiseOrWithConstantInt64(long a) { ExpectInt64(a | 7); ExpectInt64(a | 0x7FFFFFFF); ExpectInt64(a | 0xFFFFFFFFu); ExpectInt64(a | 0x7FFFFFFFFFFFFFFFL); } public void BitwiseOrWithConstantUInt32(uint a) { ExpectUInt32(a | 7); ExpectUInt32(a | 0x7FFFFFFF); ExpectUInt32(a | 0xFFFFFFFFu); } public void BitwiseOrWithConstantInt32(int a) { ExpectInt32(a | 7); ExpectInt32(a | 0x7FFFFFFF); } public void BitwiseOrWithConstantUInt16(ushort a) { ExpectUInt16((ushort)(a | 7)); ExpectUInt16((ushort)(a | 0x7FFF)); ExpectUInt16((ushort)(a | 0xFFFF)); } public void BitwiseOrWithConstantInt16(short a) { ExpectInt16((short)(a | 7)); ExpectInt16((short)(a | 0x7FFF)); } public void BitwiseOrWithConstantUInt8(byte a) { ExpectUInt8((byte)(a | 7)); ExpectUInt8((byte)(a | 0x7F)); ExpectUInt8((byte)(a | 0xFF)); } public void BitwiseOrWithConstantInt8(sbyte a) { ExpectInt8((sbyte)(a | 7)); ExpectInt8((sbyte)(a | 0x7F)); } public void BitwiseXorWithConstantUInt64(ulong a) { ExpectUInt64(a ^ 7); ExpectUInt64(a ^ 0x7FFFFFFF); ExpectUInt64(a ^ 0xFFFFFFFFu); ExpectUInt64(a ^ 0x7FFFFFFFFFFFFFFFL); ExpectUInt64(a ^ 0xFFFFFFFFFFFFFFFFuL); } public void BitwiseXorWithConstantInt64(long a) { ExpectInt64(a ^ 7); ExpectInt64(a ^ 0x7FFFFFFF); ExpectInt64(a ^ 0xFFFFFFFFu); ExpectInt64(a ^ 0x7FFFFFFFFFFFFFFFL); } public void BitwiseXorWithConstantUInt32(uint a) { ExpectUInt32(a ^ 7); ExpectUInt32(a ^ 0x7FFFFFFF); ExpectUInt32(a ^ 0xFFFFFFFFu); } public void BitwiseXorWithConstantInt32(int a) { ExpectInt32(a ^ 7); ExpectInt32(a ^ 0x7FFFFFFF); } public void BitwiseXorWithConstantUInt16(ushort a) { ExpectUInt16((ushort)(a ^ 7)); ExpectUInt16((ushort)(a ^ 0x7FFF)); ExpectUInt16((ushort)(a ^ 0xFFFF)); } public void BitwiseXorWithConstantInt16(short a) { ExpectInt16((short)(a ^ 7)); ExpectInt16((short)(a ^ 0x7FFF)); } public void BitwiseXorWithConstantUInt8(byte a) { ExpectUInt8((byte)(a ^ 7)); ExpectUInt8((byte)(a ^ 0x7F)); ExpectUInt8((byte)(a ^ 0xFF)); } public void BitwiseXorWithConstantInt8(sbyte a) { ExpectInt8((sbyte)(a ^ 7)); ExpectInt8((sbyte)(a ^ 0x7F)); } public int Issue2166a(int x) { if ((x & 0x10) != 0) { return 1; } return 0; } public byte Issue2166b(int x) { return (byte)(x & 0x10); } public decimal Issue3367() { #if CS70 return new decimal(0, 0, 0, isNegative: false, 29); #else return new decimal(0, 0, 0, false, 29); #endif } private void ExpectUInt64(ulong _) { } private void ExpectInt64(long _) { } private void ExpectUInt32(uint _) { } private void ExpectInt32(int _) { } private void ExpectUInt16(ushort _) { } private void ExpectInt16(short _) { } private void ExpectUInt8(byte _) { } private void ExpectInt8(sbyte _) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs ================================================ // Copyright (c) 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 // 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. #pragma warning disable CS9113 using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class ConstructorInitializers { public class JArray { private readonly List objects = new List(); private readonly List strings = new List(); public JArray() { } public JArray(params object[] items) { foreach (object item in items) { objects.Add(item); } } public JArray(object content) { objects.Add(content); } public JArray(string content) { strings.Add(content); } } public struct Issue1743 { public int Leet; public Issue1743(int dummy) : this(dummy, dummy) { Leet += dummy; } public Issue1743(int dummy1, int dummy2) { Leet = dummy1 + dummy2; } } #if CS120 public struct Issue1743WithPrimaryCtor(int dummy1, int dummy2) { public int Leet = dummy1 + dummy2; public Issue1743WithPrimaryCtor(int dummy) : this(dummy, dummy) { Leet += dummy; } } /// /// This is info about the class /// private struct StructWithXmlDocCtor { public int A; public int B; /// /// This is info about the constructor /// public StructWithXmlDocCtor(int a, int b) { A = a; B = b; } } /// /// This is info about the class /// private struct StructWithoutXmlDocCtor(int a, int b) { public int A = a; public int B = b; } #endif public class ClassWithConstant { // using decimal constants has the effect that there is a cctor // generated containing the explicit initialization of this field. // The type is marked beforefieldinit private const decimal a = 1.0m; } public class ClassWithConstantAndStaticCtor { // The type is not marked beforefieldinit private const decimal a = 1.0m; static ClassWithConstantAndStaticCtor() { } } public class MethodCallInCtorInit { public MethodCallInCtorInit(ConsoleKey key) #if MCS5 : this(((int)key/*cast due to .constrained prefix*/).ToString()) #else : this(((int)key).ToString()) #endif { } public MethodCallInCtorInit(string s) { } } public struct SimpleStruct { public int Field1; public int Field2; } public class UnsafeFields { public unsafe static int StaticSizeOf = sizeof(SimpleStruct); public unsafe int SizeOf = sizeof(SimpleStruct); } #if CS120 public class ClassWithPrimaryCtorUsingGlobalParameter(int a) { public void Print() { Console.WriteLine(a); } } public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToField(int a) { #pragma warning disable CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. private readonly int _a = a; #pragma warning restore CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. public void Print() { Console.WriteLine(_a); } } public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToFieldAndUsedInMethod(int a) { #pragma warning disable CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. private readonly int _a = a; #pragma warning restore CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. public void Print() { Console.WriteLine(a); } } public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToProperty(int a) { public int A { get; set; } = a; public void Print() { Console.WriteLine(A); } } public class ClassWithPrimaryCtorUsingGlobalParameterInExpressionAssignedToProperty(int a) { public int A { get; set; } = (int)Math.Abs(Math.PI * (double)a); public void Print() { Console.WriteLine(A); } } public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToEvent(EventHandler a) { public event EventHandler A = a; public void Print() { Console.WriteLine(this.A); } } #endif public class NoRecordButCopyConstructorLike { private NoRecordButCopyConstructorLike parent; public NoRecordButCopyConstructorLike(NoRecordButCopyConstructorLike parent) { this.parent = parent; } } #if CS100 public class PrimaryCtorClassThisChain(Guid id) { public Guid guid { get; } = id; public PrimaryCtorClassThisChain(Guid id, int value) : this(Guid.NewGuid()) { } public PrimaryCtorClassThisChain() : this(Guid.NewGuid(), 222) { } } #if EXPECTED_OUTPUT public class UnusedPrimaryCtorParameter { public UnusedPrimaryCtorParameter(int unused) { } } #else public class UnusedPrimaryCtorParameter(int unused) { } #endif #if OPT && EXPECTED_OUTPUT public class C8(object obj) { public int Test() { object obj2 = obj; if (obj2 is int) { return (int)obj2; } return 0; } } #else public class C8(object obj) { public int Test() { if (obj is int result) { return result; } return 0; } } #endif #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.CovariantReturns { public abstract class AbstractDerived : Base { public abstract override AbstractDerived Instance { get; } public abstract override AbstractDerived this[int index] { get; } public abstract override AbstractDerived Build(); protected abstract override AbstractDerived SetParent(object parent); } public abstract class Base { public abstract Base Instance { get; } public abstract Base this[int index] { get; } public virtual Base Build() { throw null; } protected abstract Base SetParent(object parent); } public class Derived : Base { public override Derived Instance { get; } public override Derived this[int index] { get { throw null; } } public override Derived Build() { throw null; } protected override Derived SetParent(object parent) { throw null; } } public class UseSites { public Base Test(Base x) { return x.Build(); } public Derived Test(Derived x) { return x.Build(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributeConflicts.cs ================================================ using System; using CustomAttributeConflicts.NS1; using CustomAttributeConflicts.NS2; using CustomAttributeConflicts.NSWithConflictingTypes; using CustomAttributeConflicts.NSWithConflictingTypes2; namespace CustomAttributeConflicts { internal class AttributeWithSameNameAsNormalType { } internal class TestClass { [Other] public void Test1() { } [CustomAttributeConflicts.NS1.Simple] public void Test2() { } [CustomAttributeConflicts.NS2.Simple] public void Test3() { } [CustomAttributeConflicts.NS1.AttributeWithSameNameAsNormalType] public void Test4() { } [@My] public void Test5() { } [@MyAttribute] public void Test6() { } [CustomAttributeConflicts.NSWithConflictingTypes2.@MyOther] public void Test7() { } [CustomAttributeConflicts.NSWithConflictingTypes2.@MyOtherAttribute] public void Test8() { } } } namespace CustomAttributeConflicts.NS1 { internal class AttributeWithSameNameAsNormalType : Attribute { } internal class OtherAttribute : Attribute { } internal class SimpleAttribute : Attribute { } } namespace CustomAttributeConflicts.NS2 { internal class SimpleAttribute : Attribute { } } namespace CustomAttributeConflicts.NSWithConflictingTypes { internal class My : Attribute { } internal class MyAttribute : Attribute { } internal class MyAttributeAttribute : Attribute { } internal class MyOther : Attribute { } internal class MyOtherAttribute : Attribute { } internal class MyOtherAttributeAttribute : Attribute { } } namespace CustomAttributeConflicts.NSWithConflictingTypes2 { internal class MyOther : Attribute { } internal class MyOtherAttribute : Attribute { } internal class MyOtherAttributeAttribute : Attribute { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributeSamples.cs ================================================ // Copyright (c) 2014 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.CustomAttributeSamples { [Obsolete("reason")] public delegate int AppliedToDelegate(); [Obsolete("reason")] public interface AppliedToInterface { } [Obsolete("reason")] public struct AppliedToStruct { public int Field; } [Flags] public enum EnumWithFlagsAttribute { None = 0 } [AttributeUsage(AttributeTargets.All)] public class MyAttributeAttribute : Attribute { } [AttributeUsage(AttributeTargets.All)] public class MyAttributeNamedInitializerFieldEnumAttribute : Attribute { public AttributeTargets Field; } [AttributeUsage(AttributeTargets.All)] public class MyAttributeNamedInitializerPropertyEnumAttribute : Attribute { public AttributeTargets Prop { get { return AttributeTargets.All; } set { } } } [AttributeUsage(AttributeTargets.All)] public class MyAttributeOnReturnTypeOfDelegateAttribute : Attribute { } [AttributeUsage(AttributeTargets.All)] public class MyAttributeTargetPropertyIndexSetMultiParamAttribute : Attribute { public int Field; } [AttributeUsage(AttributeTargets.All)] public class MyAttributeWithCustomPropertyAttribute : Attribute { public string Prop { get { return ""; } set { } } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class MyAttributeWithNamedArgumentAppliedAttribute : Attribute { } [AttributeUsage(AttributeTargets.All)] public class MyAttributeWithNamedInitializerPropertyTypeAttribute : Attribute { public Type Prop { get { return null; } set { } } } [MyAttributeWithCustomProperty(Prop = "value")] public class MyClass { } public class MyClass<[MyClassAttributeOnTypeParameter] T> { } [MyAttributeWithNamedInitializerPropertyType(Prop = typeof(Enum))] public class MyClass02 { } [MyAttributeNamedInitializerPropertyEnum(Prop = (AttributeTargets.Class | AttributeTargets.Method))] public class MyClass03 { } [MyAttributeNamedInitializerFieldEnum(Field = (AttributeTargets.Class | AttributeTargets.Method))] public class MyClass04 { } public class MyClass05 { [return: MyAttribute] public int MyMethod() { return 5; } } public class MyClass06 { public int Prop { [return: MyAttribute] get { return 3; } } } public class MyClass07 { public int Prop { [param: MyAttribute] set { } } } public class MyClass08 { public int Prop { get { return 3; } [return: MyAttribute] set { } } } public class MyClass09 { public int this[string s] { [return: MyAttribute] get { return 3; } } } public class MyClass10 { public int this[[MyAttribute] string s] { set { } } } public class MyClass11 { #if ROSLYN public int this[[MyAttribute] string s] => 3; #else public int this[[MyAttribute] string s] { get { return 3; } } #endif } public class MyClass12 { public string this[int index] { get { return ""; } [return: MyAttribute] set { } } } public class MyClass13 { public string this[[MyAttributeTargetPropertyIndexSetMultiParam(Field = 2)] int index1, [MyAttributeTargetPropertyIndexSetMultiParam(Field = 3)] int index2] { get { return ""; } [param: MyAttribute] set { } } } [AttributeUsage(AttributeTargets.All)] public class MyClassAttributeOnTypeParameterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)] public class MyMethodOrInterfaceAttributeAttribute : Attribute { } [AttributeUsage(AttributeTargets.All)] public class MyTypeAttribute : Attribute { public MyTypeAttribute(Type t) { } } [Obsolete("message")] public class ObsoleteClass { } [MyType(typeof(Attribute))] public class SomeClass { } [return: MyAttributeOnReturnTypeOfDelegate] public delegate void Test(); public class TestClass { [MyAttribute] public int Field; [Obsolete("reason")] #if ROSLYN public int Property => 0; #else public int Property { get { return 0; } } #endif public int PropertyAttributeOnGetter { [MyAttribute] get { return 0; } } public int PropertyAttributeOnSetter { get { return 3; } [MyAttribute] set { } } [Obsolete("reason")] #if ROSLYN public int this[int i] => 0; #else public int this[int i] { get { return 0; } } #endif [MyAttribute] public event EventHandler MyEvent; [method: MyAttribute] public event EventHandler MyEvent2; [MyAttribute] public void Method() { } public void Method([MyAttribute] int val) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs ================================================ // Copyright (c) 2014 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 // 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; namespace CustomAttributes { public static class CustomAttributes { [Flags] public enum EnumWithFlag { All = 0xF, None = 0, Item1 = 1, Item2 = 2, Item3 = 4, Item4 = 8 } [AttributeUsage(AttributeTargets.All)] public class MyAttribute : Attribute { public MyAttribute(object val) { } } #if CS110 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class GenericAttribute : Attribute { public GenericAttribute() { } public GenericAttribute(T val) { } } #endif [My(ULongEnum.MaxUInt64)] public enum ULongEnum : ulong { [My(null)] MaxUInt64 = ulong.MaxValue } [AttributeUsage(AttributeTargets.Field)] public class TypesAttribute : Attribute { public TypesAttribute(Type type) { } } private class SomeType { } private class SomeType { } private struct DataType { private int i; } [Types(typeof(int))] private static int typeattr_int; [Types(null)] private static int typeattr_null; [Types(typeof(List))] private static int typeattr_list_of_int; [Types(typeof(List<>))] private static int typeattr_list_unbound; [Types(typeof(SomeType))] private static int typeattr_sometype_of_datatype; [Types(typeof(SomeType))] private static int typeattr_sometype_of_datatype2; [Types(typeof(SomeType))] private static int typeattr_sometype_of_datatype_and_int; [Types(typeof(SomeType))] private static int typeattr_sometype_of_datatype_array_and_int; [Types(typeof(SomeType, int>))] private static int typeattr_sometype_of_nested_sometype; [Types(typeof(SomeType))] private static int typeattr_sometype_of_int_and_datatype; [Types(typeof(int[]))] private static int typeattr_array_of_int; [Types(typeof(int[,,,][,]))] private static int typeattr_multidim_array_of_int; [My(EnumWithFlag.Item1 | EnumWithFlag.Item2)] private static int field; [My(EnumWithFlag.All)] #if ROSLYN public static string Property => "aa"; #else public static string Property { get { return "aa"; } } #endif [Obsolete("some message")] public static void ObsoletedMethod() { } // No Boxing [My(new StringComparison[] { StringComparison.Ordinal, StringComparison.CurrentCulture })] public static void EnumArray() { } // Boxing of each array element [My(new object[] { StringComparison.Ordinal, StringComparison.CurrentCulture })] public static void BoxedEnumArray() { } [My(new object[] { 1, 2u, 3L, 4uL, // Ensure the decompiler doesn't leave these casts out: (short)5, (ushort)6, (byte)7, (sbyte)8, 'a', '\0', '\ufeff', '\uffff', 1f, 2.0, "text", null, typeof(int), new object[] { 1 }, new int[] { 1 } })] public static void BoxedLiteralsArray() { } #if CS110 [Generic] [Generic] public static void UseGenericAttribute() { } [Generic(42)] [Generic("Hi")] [Generic("Hi")] [Generic((short)42)] public static void UseGenericAttributeWithArg() { } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes2.cs ================================================ using System; namespace CustomAttributes2 { public static class CustomAttributes { [Flags] public enum EnumWithFlag { All = 0xF, None = 0, Item1 = 1, Item2 = 2, Item3 = 4, Item4 = 8 } [AttributeUsage(AttributeTargets.All)] public class MyAttribute : Attribute { public MyAttribute(EnumWithFlag en) { } } [My(EnumWithFlag.Item1 | EnumWithFlag.Item2)] private static int field; [My(EnumWithFlag.All)] #if ROSLYN public static string Property => "aa"; #else public static string Property { get { return "aa"; } } #endif public static string GetterOnlyPropertyWithAttributeOnGetter { [My(EnumWithFlag.Item1)] get { return "aa"; } } [My(EnumWithFlag.All)] public static string GetterOnlyPropertyWithAttributeOnGetter2 { [My(EnumWithFlag.Item1)] get { return "aa"; } } [Obsolete("some message")] public static void ObsoletedMethod() { Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field); AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field; Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomShortCircuitOperators.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.CustomShortCircuitOperators { internal class BaseClass { public static bool operator true(BaseClass x) { return true; } public static bool operator false(BaseClass x) { return false; } } internal class C : BaseClass { public static C operator &(C x, C y) { return null; } public static C operator |(C x, C y) { return null; } public static C operator !(C x) { return x; } public static C GetC(int a) { return new C(); } public static C LogicAnd() { return GetC(1) && GetC(2); } public static C LogicOr() { return GetC(1) || GetC(2); } public static C Complex() { return (GetC(1) || GetC(2)) && GetC(3) && !(GetC(4) || GetC(5)); } private static void Main() { C c = new C(); C c2 = new C(); C c3 = c && c2; C c4 = c || c2; Console.WriteLine(c3.ToString()); Console.WriteLine(c4.ToString()); } private static void Test2() { if (GetC(1) && GetC(2)) { Console.WriteLine(GetC(3)); } if (GetC(1) || GetC(2)) { Console.WriteLine(GetC(3)); } if (!(GetC(1) && GetC(2))) { Console.WriteLine(GetC(3)); } } private static void Test3() { C c = new C(); if (c) { Console.WriteLine(c.ToString()); } if (!c) { Console.WriteLine(c.ToString()); } } public void WithDynamic(dynamic d) { Console.WriteLine(GetC(1) && d.P); Console.WriteLine(GetC(2) || d.P); if (GetC(3) && d.P) { Console.WriteLine(GetC(4)); } if (GetC(5) || d.P) { Console.WriteLine(GetC(6)); } } } internal struct S { private readonly bool val; public bool Val { get { return val; } set { } } public S(bool val) { this.val = val; } public static bool operator true(S x) { return x.val; } public static bool operator false(S x) { return x.val; } public static S operator &(S x, S y) { return new S(x.val & y.val); } public static S operator |(S x, S y) { return new S(x.val | y.val); } public static S operator !(S x) { return new S(!x.val); } public static S Get(int i) { return new S(i > 0); } public static S LogicAnd() { return Get(1) && Get(2); } public static S LogicOr() { return Get(1) || Get(2); } public void InConditionDetection() { Console.WriteLine("a"); if (Get(1) && Get(2)) { Console.WriteLine("b"); } else { Console.WriteLine("c"); } if (Get(1) || Get(2)) { Console.WriteLine("d"); } else { Console.WriteLine("e"); } } public void WithDynamic(dynamic d) { Console.WriteLine(Get(1) && d.P); Console.WriteLine(Get(2) || d.P); if (Get(3) && d.P) { Console.WriteLine(Get(4)); } if (Get(5) || d.P) { Console.WriteLine(Get(6)); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs ================================================ #pragma warning disable 1998 using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class ValueTaskType { private int memberField; public async ValueTask SimpleVoidTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); } public async ValueTask TaskMethodWithoutAwait() { Console.WriteLine("No Await"); } public async ValueTask CapturingThis() { await Task.Delay(memberField); } public async ValueTask CapturingThisWithoutAwait() { Console.WriteLine(memberField); } public async ValueTask SimpleBoolTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); return true; } public async void TwoAwaitsWithDifferentAwaiterTypes() { Console.WriteLine("Before"); if (await SimpleBoolTaskMethod()) { await Task.Delay(TimeSpan.FromSeconds(1.0)); } Console.WriteLine("After"); } public async void AwaitInLoopCondition() { while (await SimpleBoolTaskMethod()) { Console.WriteLine("Body"); } } public async ValueTask AwaitInCatch(bool b, ValueTask task1, ValueTask task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception) { if (!b) { await task2; } else { Console.WriteLine("No await"); } } } public async ValueTask AwaitInFinally(bool b, ValueTask task1, ValueTask task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } finally { if (!b) { await task2; } else { Console.WriteLine("No await"); } } } public static async ValueTask GetIntegerSumAsync(IEnumerable items) { await Task.Delay(100); int num = 0; foreach (int item in items) { num += item; } return num; } public static Func> AsyncLambda() { return async () => await GetIntegerSumAsync(new int[3] { 1, 2, 3 }); } public static Func> AsyncDelegate() { return async delegate { await Task.Delay(10); return 2; }; } public static async ValueTask AsyncLocalFunctions() { return await Nested(1) + await Nested(2); #if CS80 static async ValueTask Nested(int i) #else async ValueTask Nested(int i) #endif { await Task.Delay(i); return i; } } } } namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1788 { #pragma warning disable CS8981 [AsyncMethodBuilder(typeof(builder))] internal class async { public awaiter GetAwaiter() { throw null; } } internal class await { public awaiter GetAwaiter() { throw null; } } internal class awaiter : INotifyCompletion { public bool IsCompleted => true; public void GetResult() { } public void OnCompleted(Action continuation) { } } internal class builder { public async Task { get { throw null; } } public static builder Create() { throw null; } public void SetResult() { } public void SetException(Exception e) { } public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { throw null; } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { throw null; } public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { throw null; } public void SetStateMachine(IAsyncStateMachine stateMachine) { throw null; } } public class C { internal async async @await(@await async) { await async; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs ================================================ // Copyright (c) 2020 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.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class DeconstructionExt { public static void Deconstruct(this KeyValuePair pair, out K key, out V value) { key = pair.Key; value = pair.Value; } } internal class DeconstructionTests { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct MyInt { public static implicit operator int(MyInt x) { return 0; } public static implicit operator MyInt(int x) { return default(MyInt); } } private class DeconstructionSource { public int Dummy { get; set; } public void Deconstruct(out T a, out T2 b) { a = default(T); b = default(T2); } } private class DeconstructionSource { public int Dummy { get; set; } public void Deconstruct(out T a, out T2 b, out T3 c) { a = default(T); b = default(T2); c = default(T3); } } private class AssignmentTargets { public int IntField; public long LongField; public float FloatField; public double DoubleField; public decimal DecimalField; public MyInt MyField; public MyInt? NMyField; public string StringField; public object ObjectField; public dynamic DynamicField; public int? NullableIntField; public MyInt MyIntField; public MyInt? NullableMyIntField; public int Int { get; set; } public long Long { get; set; } public float Float { get; set; } public double Double { get; set; } public decimal Decimal { get; set; } public string String { get; set; } public object Object { get; set; } public dynamic Dynamic { get; set; } public int? NInt { get; set; } public MyInt My { get; set; } public MyInt? NMy { get; set; } public static MyInt StaticMy { get; set; } public static MyInt? StaticNMy { get; set; } } private DeconstructionSource GetSource() { return null; } private DeconstructionSource GetSource() { return null; } private ref T GetRef() { throw new NotImplementedException(); } private (T, T2) GetTuple() { return default((T, T2)); } private (T, T2, T3) GetTuple() { return default((T, T2, T3)); } private AssignmentTargets Get(int i) { return null; } public void LocalVariable_NoConversion_Custom() { var (myInt3, myInt4) = GetSource(); Console.WriteLine(myInt3); Console.WriteLine(myInt4); } public void LocalVariable_NoConversion_Tuple() { var (myInt, myInt2) = GetTuple(); Console.WriteLine(myInt); Console.WriteLine(myInt2); } public void LocalVariable_NoConversion_Custom_DiscardFirst() { var (_, myInt3, value) = GetSource(); Console.WriteLine(myInt3); Console.WriteLine(value); } // currently we detect deconstruction, iff the first element is not discarded //public void LocalVariable_NoConversion_Tuple_DiscardFirst() //{ // var (_, x, value) = GetTuple(); // Console.WriteLine(x); // Console.WriteLine(value); //} public void LocalVariable_NoConversion_Custom_DiscardLast() { var (myInt3, myInt4, _) = GetSource(); Console.WriteLine(myInt3); Console.WriteLine(myInt4); } public void LocalVariable_NoConversion_Tuple_DiscardLast() { var (myInt, myInt2, _) = GetTuple(); Console.WriteLine(myInt); Console.WriteLine(myInt2); } public void LocalVariable_NoConversion_Custom_DiscardSecond() { var (myInt3, _, value) = GetSource(); Console.WriteLine(myInt3); Console.WriteLine(value); } public void LocalVariable_NoConversion_Tuple_DiscardSecond() { var (myInt, _, value) = GetTuple(); Console.WriteLine(myInt); Console.WriteLine(value); } public void LocalVariable_NoConversion_Custom_ReferenceTypes() { var (value, value2) = GetSource(); Console.WriteLine(value); Console.WriteLine(value2); } public void LocalVariable_NoConversion_Tuple_ReferenceTypes() { var (value, value2) = GetTuple(); Console.WriteLine(value); Console.WriteLine(value2); } public void Issue2378(Tuple tuple) { var (value, value2) = tuple; Console.WriteLine(value2); Console.WriteLine(value); } public void Issue2378_IntToLongConversion(Tuple tuple) { int value; long value2; (value, value2) = tuple; Console.WriteLine(value2); Console.WriteLine(value); } public void LocalVariable_IntToLongConversion_Custom() { int value; long value2; (value, value2) = GetSource(); Console.WriteLine(value); Console.WriteLine(value2); } public void LocalVariable_IntToLongConversion_Tuple() { int value; long value2; (value, value2) = GetTuple(); Console.WriteLine(value); Console.WriteLine(value2); } public void LocalVariable_FloatToDoubleConversion_Custom() { int value; double value2; (value, value2) = GetSource(); Console.WriteLine(value); Console.WriteLine(value2); } public void LocalVariable_FloatToDoubleConversion_Tuple() { int value; double value2; (value, value2) = GetTuple(); Console.WriteLine(value); Console.WriteLine(value2); } // dynamic conversion is currently not supported //public void LocalVariable_ImplicitReferenceConversion_Custom() //{ // object value; // dynamic value2; // (value, value2) = GetSource(); // Console.WriteLine(value); // value2.UseMe(); //} //public void LocalVariable_ImplicitReferenceConversion_Tuple() //{ // object value; // dynamic value2; // (value, value2) = GetTuple(); // Console.WriteLine(value); // value2.UseMe(); //} public void LocalVariable_NoConversion_ComplexValue_Custom() { var (myInt3, myInt4) = new DeconstructionSource { Dummy = 3 }; Console.WriteLine(myInt3); Console.WriteLine(myInt4); } public void Property_NoConversion_Custom() { (Get(0).NMy, Get(1).My) = GetSource(); } public void Property_IntToLongConversion_Custom() { (Get(0).Int, Get(1).Long) = GetSource(); } public void Property_FloatToDoubleConversion_Custom() { (Get(0).Int, Get(1).Double) = GetSource(); } // dynamic conversion is not supported //public void Property_ImplicitReferenceConversion_Custom() //{ // (Get(0).Object, Get(1).Dynamic) = GetSource(); //} public void Property_NoConversion_Custom_DiscardFirst() { (_, Get(1).My) = GetSource(); } public void Property_NoConversion_Custom_DiscardLast() { (Get(0).NMy, _) = GetSource(); } public void Property_NoConversion_Tuple() { (Get(0).NMy, Get(1).My) = GetTuple(); } public void Property_NoConversion_Tuple_DiscardLast() { (Get(0).NMy, Get(1).My, _) = GetTuple(); } // currently we detect deconstruction, iff the first element is not discarded //public void Property_NoConversion_Tuple_DiscardFirst() //{ // (_, Get(1).My, Get(2).Int) = GetTuple(); //} public void Property_NoConversion_Custom_DiscardSecond() { (Get(0).NMy, _, Get(2).Int) = GetSource(); } public void Property_NoConversion_Tuple_DiscardSecond() { (Get(0).NMy, _, Get(2).Int) = GetTuple(); } public void Property_NoConversion_Custom_ReferenceTypes() { (Get(0).String, Get(1).String) = GetSource(); } public void Property_NoConversion_Tuple_ReferenceTypes() { (Get(0).String, Get(1).String) = GetTuple(); } public void Property_IntToLongConversion_Tuple() { (Get(0).Int, Get(1).Long) = GetTuple(); } public void Property_FloatToDoubleConversion_Tuple() { (Get(0).Int, Get(1).Double) = GetTuple(); } public void RefLocal_NoConversion_Custom(out double a) { (a, GetRef()) = GetSource(); } public void RefLocal_NoConversion_Tuple(out double a) { (a, GetRef()) = GetTuple(); } public void RefLocal_FloatToDoubleConversion_Custom(out double a) { (a, GetRef()) = GetSource(); } public void RefLocal_FloatToDoubleConversion_Custom2(out double a) { (a, GetRef()) = GetSource(); } public void RefLocal_FloatToDoubleConversion_Tuple(out double a) { (a, GetRef()) = GetTuple(); } public void RefLocal_NoConversion_Custom(out MyInt? a) { (a, GetRef()) = GetSource(); } public void RefLocal_IntToLongConversion_Custom(out long a) { (a, GetRef()) = GetSource(); } // dynamic conversion is not supported //public void RefLocal_ImplicitReferenceConversion_Custom(out object a) //{ // (a, GetRef()) = GetSource(); //} public void RefLocal_NoConversion_Custom_DiscardFirst() { (_, GetRef()) = GetSource(); } public void RefLocal_NoConversion_Custom_DiscardLast(out MyInt? a) { (a, _) = GetSource(); } public void RefLocal_NoConversion_Tuple(out MyInt? a) { (a, GetRef()) = GetTuple(); } public void RefLocal_NoConversion_Tuple_DiscardLast(out MyInt? a) { (a, GetRef(), _) = GetTuple(); } // currently we detect deconstruction, iff the first element is not discarded //public void RefLocal_NoConversion_Tuple_DiscardFirst(out var a) //{ // (_, GetRef(), GetRef()) = GetTuple(); //} public void RefLocal_NoConversion_Custom_DiscardSecond(out MyInt? a) { (a, _, GetRef()) = GetSource(); } public void RefLocal_NoConversion_Tuple_DiscardSecond(out MyInt? a) { (a, _, GetRef()) = GetTuple(); } public void RefLocal_NoConversion_Custom_ReferenceTypes(out string a) { (a, GetRef()) = GetSource(); } public void RefLocal_NoConversion_Tuple_ReferenceTypes(out string a) { (a, GetRef()) = GetTuple(); } public void RefLocal_IntToLongConversion_Tuple(out long a) { (a, GetRef()) = GetTuple(); } //public void ArrayAssign_FloatToDoubleConversion_Custom(double[] arr) //{ // (arr[0], arr[1], arr[2]) = GetSource(); //} public void Field_NoConversion_Custom() { (Get(0).IntField, Get(1).IntField) = GetSource(); } public void Field_NoConversion_Tuple() { (Get(0).IntField, Get(1).IntField) = GetTuple(); } public void Field_IntToLongConversion_Custom() { (Get(0).IntField, Get(1).LongField) = GetSource(); } public void Field_IntToLongConversion_Tuple() { (Get(0).IntField, Get(1).LongField) = GetTuple(); } public void Field_FloatToDoubleConversion_Custom() { (Get(0).DoubleField, Get(1).DoubleField) = GetSource(); } public void Field_FloatToDoubleConversion_Tuple() { (Get(0).DoubleField, Get(1).DoubleField) = GetTuple(); } // dynamic conversion is not supported //public void Field_ImplicitReferenceConversion_Custom() //{ // (Get(0).ObjectField, Get(1).DynamicField) = GetSource(); //} public void Field_NoConversion_Custom_DiscardFirst() { (_, Get(1).MyField) = GetSource(); } public void Field_NoConversion_Custom_DiscardLast() { (Get(0).NMyField, _) = GetSource(); } public void Field_NoConversion_Tuple_DiscardLast() { (Get(0).NMyField, Get(1).MyField, _) = GetTuple(); } // currently we detect deconstruction, iff the first element is not discarded //public void Field_NoConversion_Tuple_DiscardFirst() //{ // (_, Get(1).MyField, Get(2).IntField) = GetTuple(); //} public void Field_NoConversion_Custom_DiscardSecond() { (Get(0).NMyField, _, Get(2).IntField) = GetSource(); } public void Field_NoConversion_Tuple_DiscardSecond() { (Get(0).NMyField, _, Get(2).IntField) = GetTuple(); } public void Field_NoConversion_Custom_ReferenceTypes() { (Get(0).StringField, Get(1).StringField) = GetSource(); } public void Field_NoConversion_Tuple_ReferenceTypes() { (Get(0).StringField, Get(1).StringField) = GetTuple(); } public void DeconstructDictionaryForEach(Dictionary dictionary) { foreach (var (text2, num2) in dictionary) { Console.WriteLine(text2 + ": " + num2); } } public void DeconstructTupleListForEach(List<(string, int)> tuples) { foreach (var (text, num) in tuples) { Console.WriteLine(text + ": " + num); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs ================================================ // Copyright (c) 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 // 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; using System.Runtime.CompilerServices; using System.Threading; #if CS100 using System.Threading.Tasks; #endif namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction { public static class DelegateConstruction { internal class Dummy { public int baz; public List more; } [CompilerGenerated] internal class Helper { [CompilerGenerated] internal bool HelpMe(Dummy dum) { return true; } } private class InstanceTests { public struct SomeData { public string Value; } private int x; public Action CaptureOfThis() { return delegate { CaptureOfThis(); }; } public Action CaptureOfThisAndParameter(int a) { return delegate { CaptureOfThisAndParameter(a); }; } public Action CaptureOfThisAndParameterInForEach(int a) { foreach (int item in Enumerable.Empty()) { if (item > 0) { return delegate { CaptureOfThisAndParameter(item + a); }; } } return null; } public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a) { foreach (int item in Enumerable.Empty()) { int copyOfItem = item; if (item > 0) { return delegate { CaptureOfThisAndParameter(item + a + copyOfItem); }; } } return null; } public void LambdaInForLoop() { for (int i = 0; i < 100000; i++) { Bar(() => Foo()); } } public int Foo() { return 0; } public void Bar(Func f) { } private void Bug955() { new Thread((ThreadStart)delegate { }); } public void Bug951(int amount) { DoAction(delegate { if (amount < 0) { amount = 0; } DoAction(delegate { NoOp(amount); }); }); } public void Bug951b() { int amount = Foo(); DoAction(delegate { if (amount < 0) { amount = 0; } DoAction(delegate { NoOp(amount); }); }); } public void Bug951c(SomeData data) { DoAction(delegate { DoAction(delegate { DoSomething(data.Value); }); }); } public Func Issue2143() { return (int x) => this.x; } public Action Bug971_DelegateWithoutParameterList() { return delegate { }; } private void DoAction(Action action) { } private void NoOp(int a) { } private void DoSomething(string text) { } } public interface IM3 { void M(); } public class BaseClass : IM3 { protected virtual void M1() { } protected virtual void M2() { } public virtual void M() { } public static void StaticMethod() { } } public class SubClass : BaseClass { protected override void M2() { } public new void M() { } public void Test() { Noop("M1.base", base.M1); Noop("M1", M1); Noop("M2.base", base.M2); Noop("M2", M2); Noop("M.base", base.M); Noop("M.base_virt", ((BaseClass)this).M); Noop("M.base_interface", ((IM3)this).M); #if CS70 Noop("M", this.M); Noop("M", M); #if CS80 static void M() #else void M() #endif { } #else Noop("M", M); #endif } public void Test2() { Noop("M.new", new BaseClass().M); Noop("M.new", new SubClass().M); } private void Noop(string name, Action _) { } } public class GenericTest { public Func GetFunc(Func f) { TCaptured captured = f(default(TNonCaptured)); return delegate { Console.WriteLine(captured.GetType().FullName); return captured; }; } public Func GetFunc(Func f) { TCaptured captured = f(); return delegate (TNonCaptured a, TNonCapturedMP d) { Console.WriteLine(a.GetHashCode()); Console.WriteLine(captured.GetType().FullName); return captured; }; } } private delegate void GenericDelegate(); public delegate void RefRecursiveDelegate(ref RefRecursiveDelegate d); public static Func test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); public static Func test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b); public static Func test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); public static Func test3 = (string a, string b) => !string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b); public static Func test4 = (string a, string b) => string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b); public static Func test5 = (string a, string b) => string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b); public static Func test6 = (string a, string b) => !string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b); public static Func test7 = (string a, string b) => !string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b); public static void Test(this string a) { } public static Predicate And(this Predicate filter1, Predicate filter2) { if (filter1 == null) { return filter2; } if (filter2 == null) { return filter1; } return (T m) => filter1(m) && filter2(m); } public static Action ExtensionMethodUnbound() { return Test; } public static Action ExtensionMethodBound() { return "abc".Test; } public static Action ExtensionMethodBoundOnNull() { return ((string)null).Test; } public static Predicate NoExtensionMethodOnLambda() { return And((int x) => x >= 0, (int x) => x <= 100); } public static object StaticMethod() { return new Func(ExtensionMethodBound); } public static object InstanceMethod() { return new Func("hello".ToUpper); } public static object InstanceMethodOnNull() { return new Func(((string)null).ToUpper); } public static List> AnonymousMethodStoreWithinLoop() { List> list = new List>(); for (int i = 0; i < 10; i++) { int counter; list.Add(delegate (int x) { counter = x; }); } return list; } public static List> AnonymousMethodStoreOutsideLoop() { List> list = new List>(); int counter; for (int i = 0; i < 10; i++) { list.Add(delegate (int x) { counter = x; }); } return list; } public static Action StaticAnonymousMethodNoClosure() { return delegate { Console.WriteLine(); }; } public static void NameConflict() { // i is local in main method, // j is captured variable, // k is parameter in anonymous method // l is local in anonymous method, // Ensure that the decompiler doesn't introduce name conflicts List> list = new List>(); for (int i = 0; i < 10; i++) { int j; for (j = 0; j < 10; j++) { list.Add(delegate (int k) { for (int l = 0; l < j; l += k) { Console.WriteLine(); } }); } } } public static void NameConflict2(int j) { List> list = new List>(); for (int i = 0; i < 10; i++) { list.Add(delegate (int k) { Console.WriteLine(k); }); } } public static Action NameConflict3(int i) { return delegate (int j) { for (int k = 0; k < j; k++) { Console.WriteLine(k); } }; } public static Func> CurriedAddition(int a) { return (int b) => (int c) => a + b + c; } public static Func>> CurriedAddition2(int a) { return (int b) => (int c) => (int d) => a + b + c + d; } public static Func CapturedTypeParameter1(TNonCaptured a, Func f) { TCaptured captured = f(a); return delegate { Console.WriteLine(captured.GetType().FullName); return captured; }; } public static Func CapturedTypeParameter2(TNonCaptured a, Func> f) { List captured = f(a); return delegate { Console.WriteLine(captured.GetType().FullName); return captured.FirstOrDefault(); }; } public static Func Issue1773(short data) { int integerData = data; return () => integerData; } #if !MCS // does not compile with mcs... public static Func Issue1773b(object data) { #if ROSLYN dynamic dynamicData = data; return () => dynamicData.DynamicCall(); #else // This is a bug in the old csc: captured dynamic local variables did not have the [DynamicAttribute] // on the display-class field. return () => ((dynamic)data).DynamicCall(); #endif } public static Func Issue1773c(object data) { #if ROSLYN dynamic dynamicData = data; return () => dynamicData; #else return () => (dynamic)data; #endif } #endif #if CS70 public static Func Issue1773d((int Integer, string String) data) { (int Integer, string RenamedString) valueTuple = data; return () => valueTuple.RenamedString; } #endif public static Func Identity() { return (T _) => _; } private static void Use(Action a) { } private static void Use2(Func, IEnumerable> a) { } private static void Use2(GenericDelegate a) { } private static void Use3(Func> a) { } public static void SimpleDelegateReference() { Use(SimpleDelegateReference); #if !MCS2 Use3(Identity); #endif } public static void DelegateReferenceWithStaticTarget() { Use(NameConflict); Use(BaseClass.StaticMethod); } public static void ExtensionDelegateReference(IEnumerable ints) { Use2(ints.Select); } #if CS70 public static void LocalFunctionDelegateReference() { Use(LocalFunction); Use2(LocalFunction2); #if CS80 static void LocalFunction() #else void LocalFunction() #endif { } #if CS80 static void LocalFunction2() #else void LocalFunction2() #endif { } } #endif #if CS90 public static Func LambdaParameterDiscard() { return (int _, int _, int _) => 0; } #endif #if CS100 public static Func LambdaWithAttribute0() { return [My] () => 0; } public static Func LambdaWithAttribute1() { return [My] (int x) => 0; } public static Func LambdaWithAttributeOnParam() { return ([My] int x) => 0; } public static Func> AsyncLambdaWithAttribute0() { return [My] async () => 0; } public static Action StatementLambdaWithAttribute0() { return [My] () => { }; } public static Action StatementLambdaWithAttribute1() { return [return: My] (int x) => { Console.WriteLine(x); }; } public static Action StatementLambdaWithAttribute2() { return ([My] int x) => { Console.WriteLine(x); }; } #endif public static void CallRecursiveDelegate(ref RefRecursiveDelegate d) { d(ref d); } } public class Issue1867 { private int value; public Func TestLambda(Issue1867 x) { Issue1867 m1; Issue1867 m2; if (x.value > value) { m1 = this; m2 = x; } else { m1 = x; m2 = this; } return () => m1.value + 1 == 4 && m2.value > 5; } } internal class Issue2791 { public void M() { Run(delegate (object o) { try { List list = o as List; Action action = delegate { list.Select((int x) => x * 2); }; Action action2 = delegate { list.Select((int x) => x * 2); }; Console.WriteLine(); action(); Console.WriteLine(); action2(); } catch (Exception) { Console.WriteLine("catch"); } finally { Console.WriteLine("finally"); } }, null); } private void Run(ParameterizedThreadStart del, object x) { del(x); } public void Issue1572(DelegateConstruction.Dummy dum) { #if EXPECTED_OUTPUT DelegateConstruction.Helper CS_0024_003C_003E8__locals0 = new DelegateConstruction.Helper(); DelegateConstruction.Dummy dummy = dum.more.Where((DelegateConstruction.Dummy dummy2) => true).Where((DelegateConstruction.Dummy dummy2) => true).FirstOrDefault(); Console.WriteLine(); dummy.baz++; #else DelegateConstruction.Helper h = new DelegateConstruction.Helper(); DelegateConstruction.Dummy localDummy = dum.more.Where(h.HelpMe).Where(h.HelpMe).FirstOrDefault(); Console.WriteLine(); localDummy.baz++; #endif } } [AttributeUsage(AttributeTargets.All)] internal class MyAttribute : Attribute { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Discards.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Discards { public class @_ { } public void GetOut(out int value) { value = 0; } public void GetOutOverloaded(out int value) { value = 0; } public void GetOutOverloaded(out string value) { value = "Hello World"; } public void MakeValue(Func func) { } public void MakeValue(Func<@_, int> func) { } public void SimpleParameter(@_ _) { } public void ParameterHiddenByLocal(@_ _) { GetOut(out var _); } public void ExplicitlyTypedDiscard() { GetOutOverloaded(out string _); GetOutOverloaded(out int _); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs ================================================ using System; #if CS120 using System.Collections.Generic; #endif namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { 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) { } } public interface I { } private static dynamic field; private static volatile dynamic volatileField; private static object objectField; public dynamic Property { get; set; } public DynamicTests() { } public DynamicTests(dynamic test) { } public DynamicTests(DynamicTests test) { } private static void CallWithOut(out dynamic d) { d = null; } #if CS70 private static void CallWithIn(in dynamic d) { } #endif #if CS120 private static void CallWithRefReadonly(ref readonly Dictionary d) { } #endif private static void CallWithRef(ref dynamic d) { } private static void RefCallSiteTests() { #if CS70 CallWithOut(out var d); CallWithIn(in d); #else dynamic d; CallWithOut(out d); #endif CallWithRef(ref d); d.SomeCall(); } 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(); 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 void StaticCallWithDynamicArgument(dynamic d) { M3(d + 5); } private static void StaticCallWithDynamicArgumentInStaticContext(dynamic d) { DynamicTests.M3(d + 5); } 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) { if (a == null || b == null) { Console.WriteLine("One is null"); } } private static void If3(dynamic a, dynamic b) { if (a == null && b == null) { Console.WriteLine("Both are null"); } } private static void If4(dynamic a, dynamic b) { if ((a == null || b == null) && GetDynamic(1) && !(GetDynamic(2) && GetDynamic(3))) { Console.WriteLine("then"); } else { Console.WriteLine("else"); } } private static bool ConstantTarget(dynamic a) { return true.Equals(a); } #if CS110 && NET70 private static nint NewIntPtr(dynamic a) { return new nint(a); } #else private static IntPtr NewIntPtr(dynamic a) { return new IntPtr(a); } #endif private static dynamic GetDynamic(int i) { return null; } private static bool GetBool(int i) { return false; } private static dynamic LogicAnd() { return GetDynamic(1) && GetDynamic(2); } private static dynamic LogicAnd(dynamic a, dynamic b) { return a && b; } private static void LogicAndExtended(int i, dynamic d) { Console.WriteLine(GetDynamic(1) && GetDynamic(2)); Console.WriteLine(GetDynamic(1) && GetBool(2)); Console.WriteLine(GetBool(1) && GetDynamic(2)); Console.WriteLine(i == 1 && d == null); } private static dynamic LogicOr() { return GetDynamic(1) || GetDynamic(2); } private static dynamic LogicOr(dynamic a, dynamic b) { return a || b; } private static void LogicOrExtended(int i, dynamic d) { Console.WriteLine(GetDynamic(1) || GetDynamic(2)); Console.WriteLine(GetDynamic(1) || GetBool(2)); Console.WriteLine(GetBool(1) || GetDynamic(2)); Console.WriteLine(i == 1 || d == null); } private static int ImplicitCast(object o) { return (dynamic)o; } private static int ExplicitCast(object o) { return (int)(dynamic)o; } private static dynamic GetI() { return null; } public I Test() { return GetI(); } public I Test1() { return (I)GetI(); } public I Test2() { return (I)(object)GetI(); } #if CS72 public void RefParams(ref object a, ref dynamic b, ref dynamic c) { } public void RefParams2(in object a, ref dynamic b, out dynamic c) { c = null; } public ref dynamic RefReturn(ref object o) { return ref o; } public ref readonly dynamic RefReadonlyReturn(in object o) { return ref o; } #endif } internal static class Extension { public static dynamic ToDynamic(this int i, dynamic info) { throw null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/EnumTests.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class EnumTests { public enum SimpleEnum { Item1, Item2 } public enum NoZero { Item1 = 1, Item2 } public enum OutOfOrderMembers { Item1 = 1, Item0 = 0 } public enum EnumSkippedItemTest { Item0 = 0, Item2 = 2 } public enum EnumDuplicateItemTest { Item0 = 0, Item1 = 1, Item2A = 2, Item2B = 2 } public enum LongBasedEnum : long { Item1, Item2 } public enum LongWithInitializers : long { Item1 = 0L, Item2 = 20L, Item3 = 21L } public enum ShortWithInitializers : short { Item1 = 0, Item2 = 20, Item3 = 21 } public enum ByteWithInitializers : byte { Item1 = 0, Item2 = 20, Item3 = 21 } [Flags] public enum SimpleFlagsEnum { None = 0, Item1 = 1, Item2 = 2, Item3 = 4, All = 7 } [Flags] public enum NegativeValueWithFlags { Value = -2147483647 } public enum NegativeValueWithoutFlags { Value = -2147483647 } public AttributeTargets SingleEnumValue() { return AttributeTargets.Class; } public AttributeTargets TwoEnumValuesOr() { return AttributeTargets.Class | AttributeTargets.Method; } public AttributeTargets ThreeEnumValuesOr() { return AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter; } public AttributeTargets UnknownEnumValue() { return (AttributeTargets)1000000; } public AttributeTargets EnumAllValue() { return AttributeTargets.All; } public AttributeTargets EnumZeroValue() { return (AttributeTargets)0; } public object PreservingTypeWhenBoxed() { return AttributeTargets.Delegate; } public object PreservingTypeWhenBoxedTwoEnum() { return AttributeTargets.Class | AttributeTargets.Delegate; } public void EnumInNotZeroCheck(SimpleEnum value, NoZero value2) { if (value != SimpleEnum.Item1) { Console.WriteLine(); } if (value2 != 0) { Console.WriteLine(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs ================================================ // Copyright (c) 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 // 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; #if CS60 using System.IO; #endif using System.Threading; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public abstract class ExceptionHandling { public abstract bool B(int i); public abstract Task T(); public abstract void M(int i); public bool ConditionalReturnInThrow() { try { if (B(0)) { return B(1); } } catch { } return false; } public bool SimpleTryCatchException() { try { Console.WriteLine("Try"); return B(new Random().Next()); } catch (Exception) { Console.WriteLine("CatchException"); } return false; } public bool SimpleTryCatchExceptionWithName() { try { Console.WriteLine("Try"); return B(new Random().Next()); } catch (Exception ex) { Console.WriteLine("CatchException ex: " + ex.ToString()); } return false; } #if CS60 public bool SimpleTryCatchExceptionWithNameAndCondition() { try { Console.WriteLine("Try"); return B(new Random().Next()); } catch (Exception ex) when (ex.Message.Contains("test")) { Console.WriteLine("CatchException ex: " + ex.ToString()); } return false; } public bool SimpleTryCatchExceptionWithNameAndConditionWithOr() { try { Console.WriteLine("Try"); return B(new Random().Next()); } catch (Exception ex) when (ex is ArgumentException || ex is IOException) { Console.WriteLine("CatchException ex: " + ex.ToString()); } return false; } public async Task SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr() { try { Console.WriteLine("Try"); return await T(); } catch (Exception ex) when (ex is ArgumentException || ex is IOException) { Console.WriteLine("CatchException ex: " + ex.ToString()); } return false; } public void CatchWhenWithConditionWithoutExceptionVar() { int num = 0; try { throw new Exception(); } catch (Exception) when (num == 0) { Console.WriteLine("jo"); } } #endif public bool SimpleTryFinally() { try { Console.WriteLine("Try"); } finally { Console.WriteLine("Finally"); } return false; } public void MethodEndingWithEndFinally() { try { throw null; } finally { Console.WriteLine(); } } public void MethodEndingWithRethrow() { try { throw null; } catch { throw; } } public void TryCatchFinally() { try { Console.WriteLine("Try"); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Console.WriteLine("Finally"); } } public void TryCatchMultipleHandlers() { try { Console.WriteLine("Try"); } catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); } catch (SystemException ex2) { Console.WriteLine(ex2.Message); } catch { Console.WriteLine("other"); } } //public void TwoCatchBlocksWithSameVariable() //{ // try { // Console.WriteLine("Try1"); // } catch (Exception ex) { // Console.WriteLine(ex.Message); // } // try { // Console.WriteLine("Try2"); // } catch (Exception ex) { // Console.WriteLine(ex.Message); // } //} public void NoUsingStatementBecauseTheVariableIsAssignedTo() { CancellationTokenSource cancellationTokenSource = null; try { cancellationTokenSource = new CancellationTokenSource(); } finally { if (cancellationTokenSource != null) { cancellationTokenSource.Dispose(); } } } public void ThrowInFinally() { try { } finally { throw new Exception(); } } internal void EarlyReturnInTryBlock(bool a, bool b) { try { if (a) { Console.WriteLine("a"); } else if (b) { // #2379: The only goto-free way of representing this code is to use a return statement return; } Console.WriteLine("a || !b"); } finally { Console.WriteLine("finally"); } } #if ROSLYN || !OPT // TODO Non-Roslyn compilers create a second while loop inside the try, by inverting the if // This is fixed in the non-optimised version by the enabling the RemoveDeadCode flag //public bool EarlyExitInLoopTry() //{ // while (true) { // try { // while (B(0)) { // Console.WriteLine(); // } // // return false; // } catch { // } // } //} public bool EarlyExitInLoopTry() { while (true) { try { if (!B(0)) { return false; } Console.WriteLine(); } catch { } } } #endif public bool ComplexConditionalReturnInThrow() { try { if (B(0)) { if (B(1)) { Console.WriteLine("0 && 1"); return B(2); } if (B(3)) { Console.WriteLine("0 && 3"); return !B(2); } Console.WriteLine("0"); } Console.WriteLine("End Try"); } catch { try { try { if (((B(0) || B(1)) && B(2)) || B(3)) { return B(4) && !B(5); } if (B(6) || B(7)) { return B(8) || B(9); } } catch { Console.WriteLine("Catch2"); } return B(10) && B(11); } catch { Console.WriteLine("Catch"); } finally { Console.WriteLine("Finally"); } } return false; } public void AppropriateLockExit() { int num = 0; lock (this) { if (num <= 256) { Console.WriteLine(0); } else if (num <= 1024) { Console.WriteLine(1); } else if (num <= 16384) { Console.WriteLine(2); } } } public void ReassignExceptionVar() { try { Console.WriteLine("ReassignExceptionVar"); } catch (Exception innerException) { if (innerException.InnerException != null) { innerException = innerException.InnerException; } Console.WriteLine(innerException); } } public int UseExceptionVarOutsideCatch() { Exception ex2; try { return 1; } catch (Exception ex) { ex2 = ex; } Console.WriteLine(ex2 != null); return 2; } public void GenericException(int input) where TException : Exception { try { Console.WriteLine(input); } catch (TException ex) { Console.WriteLine(ex.Message); throw; } } public void GenericException2() where T : Exception { try { Console.WriteLine("CatchT"); #if ROSLYN } catch (T val) { Console.WriteLine("{0} {1}", val, val.ToString()); } #else } catch (T arg) { Console.WriteLine("{0} {1}", arg, arg.ToString()); } #endif } #if CS60 public void GenericExceptionWithCondition(int input) where TException : Exception { try { Console.WriteLine(input); } catch (TException ex) when (ex.Message.Contains("Test")) { Console.WriteLine(ex.Message); throw; } } public void GenericException2WithCondition(int input) where TException : Exception { try { Console.WriteLine(input); } catch (TException ex) when (ex.Message.Contains("Test")) { Console.WriteLine("{0} {1}", ex, ex.ToString()); } } public void XXX1() { try { Console.WriteLine(); } catch (Exception ex) when (ex.Data.IsFixedSize) { Console.WriteLine(ex.ToString()); #pragma warning disable CA2200 // Rethrow to preserve stack details throw ex; #pragma warning restore CA2200 // Rethrow to preserve stack details } } public void XXX2() { try { Console.WriteLine(); } catch (Exception ex) when (ex is InternalBufferOverflowException) { Console.WriteLine(ex.ToString()); #pragma warning disable CA2200 // Rethrow to preserve stack details throw ex; #pragma warning restore CA2200 // Rethrow to preserve stack details } } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpandParamsArgumentsDisabled.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class ExpandParamsArgumentsDisabled { public void Test() { MethodWithParams(Array.Empty()); MethodWithParams(new int[1] { 5 }); } public void MethodWithParams(params int[] b) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs ================================================ #pragma warning disable format // Copyright (c) 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 // 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; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Xml; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class ExpressionTrees { private class GenericClass { public static X StaticField; public X InstanceField; public static X StaticProperty { get; set; } public X InstanceProperty { get; set; } public static bool GenericMethod() { return false; } } internal class GenericClassWithCtor { } internal class GenericClassWithMultipleCtors { public GenericClassWithMultipleCtors() { } public GenericClassWithMultipleCtors(int x) { } } private class AssertTest { private struct DataStruct { private int dummy; } private struct WrapperStruct { internal DataStruct Data; } private class SomeClass { internal WrapperStruct DataWrapper; } private SomeClass someClass; public void Test() { GetMember(() => someClass.DataWrapper.Data); } public static MemberInfo GetMember(Expression> p) { return null; } } public class Administrator { public int ID { get; set; } public string TrueName { get; set; } public string Phone { get; set; } } public class Contract { public int ID { get; set; } public string ContractNo { get; set; } public string HouseAddress { get; set; } public DateTime SigningTime { get; set; } public string BuyerName { get; set; } public string BuyerTelephone { get; set; } public string Customer { get; set; } public string CustTelephone { get; set; } public int AdminID { get; set; } public int StoreID { get; set; } } public class Database { public IQueryable Contracts { get; set; } public IQueryable Loan { get; set; } public IQueryable Administrator { get; set; } public IQueryable Store { get; set; } } public class Loan { public string ContractNo { get; set; } public DateTime? ShenDate { get; set; } public DateTime? LoanDate { get; set; } public string Credit { get; set; } public string LoanBank { get; set; } public string Remarks { get; set; } } public class Store { public int ID { get; set; } public string Name { get; set; } } internal class MyClass { public static MyClass operator +(MyClass a, MyClass b) { return new MyClass(); } } internal class SimpleType { public const int ConstField = 1; public static readonly int StaticReadonlyField = 2; public static int StaticField = 3; public readonly int ReadonlyField = 2; public int Field = 3; #if CS60 public static int StaticReadonlyProperty => 0; #else public static int StaticReadonlyProperty { get { return 0; } } #endif public static int StaticProperty { get; set; } #if CS60 public int ReadonlyProperty => 0; #else public int ReadonlyProperty { get { return 0; } } #endif public int Property { get; set; } } internal class SimpleTypeWithCtor { public SimpleTypeWithCtor(int i) { } } internal class SimpleTypeWithMultipleCtors { public SimpleTypeWithMultipleCtors() { } public SimpleTypeWithMultipleCtors(int i) { } } private int field; private Database db; private dynamic ViewBag; public static readonly object[] SupportedMethods = new object[2] { ToCode(null, () => ((IQueryable)null).Aggregate((object o1, object o2) => (object)null)), ToCode(null, () => ((IEnumerable)null).Aggregate((object o1, object o2) => (object)null)) }; public static readonly object[] SupportedMethods2 = new object[4] { ToCode(null, () => ((IQueryable)null).Aggregate(null, (object o1, object o2) => (object)null)), ToCode(null, () => ((IQueryable)null).Aggregate(null, (object o1, object o2) => (object)null, (object o) => (object)null)), ToCode(null, () => ((IEnumerable)null).Aggregate(null, (object o1, object o2) => (object)null)), ToCode(null, () => ((IEnumerable)null).Aggregate(null, (object o1, object o2) => (object)null, (object o) => (object)null)) }; public static void TestCall(object a) { } public static void TestCall(ref object a) { } private void Issue1249(int ID) { if (ID == 0) { ViewBag.data = "''"; return; } var model = (from a in db.Contracts where a.ID == ID select new { ID = a.ID, ContractNo = a.ContractNo, HouseAddress = a.HouseAddress, AdminID = (from b in db.Administrator where b.ID == a.AdminID select b.TrueName).FirstOrDefault(), StoreID = (from b in db.Store where b.ID == a.StoreID select b.Name).FirstOrDefault(), SigningTime = a.SigningTime, YeWuPhone = (from b in db.Administrator where b.ID == a.AdminID select b.Phone).FirstOrDefault(), BuyerName = a.BuyerName, BuyerTelephone = a.BuyerTelephone, Customer = a.Customer, CustTelephone = a.CustTelephone, Credit = (from b in db.Loan where b.ContractNo == a.ContractNo select b.Credit).FirstOrDefault(), LoanBank = (from b in db.Loan where b.ContractNo == a.ContractNo select b.LoanBank).FirstOrDefault(), Remarks = (from b in db.Loan where b.ContractNo == a.ContractNo select b.Remarks).FirstOrDefault() }).FirstOrDefault(); ViewBag.data = model.ToJson(); DateTime? dateTime = (from b in db.Loan where b.ContractNo == model.ContractNo select b.ShenDate).FirstOrDefault(); DateTime? dateTime2 = (from b in db.Loan where b.ContractNo == model.ContractNo select b.LoanDate).FirstOrDefault(); ViewBag.ShenDate = ((!dateTime.HasValue) ? "" : dateTime.ParseDateTime().ToString("yyyy-MM-dd")); ViewBag.LoanDate = ((!dateTime2.HasValue) ? "" : dateTime2.ParseDateTime().ToString("yyyy-MM-dd")); } private static object ToCode(object x, Expression> expr) { return expr; } private static object ToCode(object x, Expression> expr) { return expr; } private static object ToCode(object x, Expression> expr) { return expr; } private static object ToCode(object x, Expression> expr) { return expr; } private static object X() { return null; } public void Parameter(bool a) { ToCode(X(), () => a); } public void LocalVariable() { bool a = true; ToCode(X(), () => a); } public void LambdaParameter() { ToCode(X(), (bool a) => a); } public void AddOperator(int x) { ToCode(X(), () => 1 + x + 2); } public void AnonymousClasses() { ToCode(X(), () => new { X = 3, A = "a" }); } public void ArrayIndex() { ToCode(X(), () => (new int[3] { 3, 4, 5 })[0 + (int)(DateTime.Now.Ticks % 3)]); } public void ArrayLengthAndDoubles() { ToCode(X(), () => new double[3] { 1.0, 2.01, 3.5 }.Concat(new double[2] { 1.0, 2.0 }).ToArray().Length); } public void AsOperator() { ToCode(X(), () => new object() as string); } public void ComplexGenericName() { ToCode(X(), () => ((Func)((int x) => x > 0))(0)); } public void DefaultValue() { ToCode(X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan)); } public void EnumConstant() { ToCode(X(), () => new object().Equals(MidpointRounding.ToEven)); } public void IndexerAccess() { Dictionary dict = Enumerable.Range(1, 20).ToDictionary((int n) => n.ToString()); ToCode(X(), () => dict["3"] == 3); } public void IsOperator() { ToCode(X(), () => new object() is string); } public void ListInitializer() { ToCode(X(), () => new Dictionary { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3); } public void ListInitializer2() { ToCode(X(), () => new List(50) { 1, 2, 3 }.Count == 3); } public void ListInitializer3() { ToCode(X(), () => new List { 1, 2, 3 }.Count == 3); } public void LiteralCharAndProperty() { ToCode(X(), () => new string(' ', 3).Length == 1); } public void CharNoCast() { ToCode(X(), () => "abc"[1] == 'b'); } public void StringsImplicitCast() { int i = 1; string x = "X"; ToCode(X(), () => ((("a\n\\b" ?? x) + x).Length == 2) ? false : (true && (1m + (decimal)(-i) > 0m || false))); } public void NotImplicitCast() { byte z = 42; ToCode(X(), () => ~z == 0); } public void MembersBuiltin() { ToCode(X(), () => 1.23m.ToString()); ToCode(X(), () => AttributeTargets.All.HasFlag(AttributeTargets.Assembly)); ToCode(X(), () => "abc".Length == 3); ToCode(X(), () => 'a'.CompareTo('b') < 0); } public void MembersDefault() { ToCode(X(), () => default(DateTime).Ticks == 0); ToCode(X(), () => ((Array)null).Length == 0); ToCode(X(), () => ((Type)null).IsLayoutSequential); ToCode(X(), () => ((List)null).Count); ToCode(X(), () => ((Array)null).Clone() == null); ToCode(X(), () => ((Type)null).IsInstanceOfType(new object())); ToCode(X(), () => ((List)null).AsReadOnly()); } public void DoAssert() { ToCode(X(), () => field != C()); ToCode(X(), () => !object.ReferenceEquals(this, new ExpressionTrees())); ToCode(X(), () => MyEquals(this) && !MyEquals(null)); } private int C() { throw new NotImplementedException(); } private bool MyEquals(ExpressionTrees other) { throw new NotImplementedException(); } public void MethodGroupAsExtensionMethod() { ToCode(X(), (Expression>>)(() => ((IEnumerable)new int[4] { 2000, 2004, 2008, 2012 }).Any)); } public void MethodGroupConstant() { ToCode(X(), () => Array.TrueForAll(new int[4] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear)); HashSet set = new HashSet(); ToCode(X(), () => new int[4] { 2000, 2004, 2008, 2012 }.All(set.Add)); Func, bool> sink = (Func f) => f(null, null); ToCode(X(), () => sink(object.Equals)); } public void MultipleCasts() { ToCode(X(), () => 1 == (int)(object)1); } public void MultipleDots() { ToCode(X(), () => 3.ToString().ToString().Length > 0); } public void NestedLambda() { Func, int> call = (Func f) => f(); //no params ToCode(X(), () => call(() => 42)); //one param ToCode(X(), () => new int[2] { 37, 42 }.Select((int x) => x * 2)); //two params ToCode(X(), () => new int[2] { 37, 42 }.Select((int x, int i) => x * 2)); } public void CurriedLambda() { ToCode(X(), (Expression>>>)((int a) => (int b) => (int c) => a + b + c)); } private bool Fizz(Func a) { return a(42); } private bool Buzz(Func a) { return a(42); } private bool Fizz(Func a) { return a("42"); } private bool Fizz(Func a) { return a(null); } public void NestedLambda2() { ToCode(X(), () => Fizz((string x) => x == "a")); ToCode(X(), () => Fizz((string x) => x != "a")); ToCode(X(), () => Fizz((Action x) => x == new Action(NestedLambda2))); ToCode(X(), () => Fizz((Action x) => x != new Action(NestedLambda2))); ToCode(X(), () => Fizz((int x) => x == 37)); ToCode(X(), () => Fizz((int x) => true)); ToCode(X(), () => Buzz((int x) => true)); } public void NewArrayAndExtensionMethod() { ToCode(X(), () => new double[3] { 1.0, 2.01, 3.5 }.SequenceEqual(new double[3] { 1.0, 2.01, 3.5 })); } public void NewMultiDimArray() { ToCode(X(), () => new int[3, 4].Length == 1); } public void NewObject() { ToCode(X(), () => new object() != new object()); } public void NotOperator() { bool x = true; int y = 3; byte z = 42; ToCode(X(), () => ~z == 0); ToCode(X(), () => ~y == 0); ToCode(X(), () => !x); } public void ObjectInitializers() { XmlReaderSettings s = new XmlReaderSettings { CloseInput = false, CheckCharacters = false }; ToCode(X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s)); } public void Quoted() { ToCode(X(), () => (Expression>)((int n, string s) => s + n.ToString()) != null); } public void Quoted2() { ToCode(X(), () => ToCode(X(), () => true).Equals(null)); } public void QuotedWithAnonymous() { ToCode(X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single()); } public void StaticCall() { ToCode(X(), () => object.Equals(3, 0)); } public void ThisCall() { ToCode(X(), () => !Equals(3)); } public void ThisExplicit() { ToCode(X(), () => object.Equals(this, 3)); } public void TypedConstant() { ToCode(X(), () => new Type[2] { typeof(int), typeof(string) }); } public void StaticCallImplicitCast() { ToCode(X(), () => object.Equals(3, 0)); } public void StaticMembers() { ToCode(X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False"); } public void Strings() { int i = 1; string x = "X"; ToCode(X(), () => ((("a\n\\b" ?? x) + x).Length == 2) ? false : (true && (1m + (decimal)(-i) > 0m || false))); } public void GenericClassInstance() { ToCode(X(), () => (double)new GenericClass().InstanceField + new GenericClass().InstanceProperty); } public void GenericClassStatic() { ToCode(X(), () => (double)GenericClass.StaticField + GenericClass.StaticProperty); } public void InvokeGenericMethod() { ToCode(X(), () => GenericClass.GenericMethod()); } private static void Test(T delegateExpression, Expression expressionTree) { } public static void ArrayIndexer() { Test>((int[] array) => array[0], (int[] array) => array[0]); Test>((int[] array, int index) => array[index], (int[] array, int index) => array[index]); Test>((int[,] array) => array[0, 5], (int[,] array) => array[0, 5]); Test>((int[,] array, int index) => array[index, 7], (int[,] array, int index) => array[index, 7]); Test>((int[][] array, int index) => array[index][7], (int[][] array, int index) => array[index][7]); } public static void ArrayLength() { Test>((int[] array) => array.Length, (int[] array) => array.Length); Test>(() => ((Array)null).Length, () => ((Array)null).Length); } public static void NewObj() { Test>(() => new SimpleType(), () => new SimpleType()); Test>(() => new SimpleTypeWithCtor(5), () => new SimpleTypeWithCtor(5)); Test>(() => new SimpleTypeWithMultipleCtors(), () => new SimpleTypeWithMultipleCtors()); Test>(() => new SimpleTypeWithMultipleCtors(5), () => new SimpleTypeWithMultipleCtors(5)); Test>(() => new GenericClass(), () => new GenericClass()); Test>(() => new GenericClassWithCtor(), () => new GenericClassWithCtor()); Test>(() => new GenericClassWithMultipleCtors(5), () => new GenericClassWithMultipleCtors(5)); } public unsafe static void TypeOfExpr() { Test>(() => typeof(int), () => typeof(int)); Test>(() => typeof(object), () => typeof(object)); Test>(() => typeof(List<>), () => typeof(List<>)); Test>(() => typeof(List), () => typeof(List)); Test>(() => typeof(int*), () => typeof(int*)); } public static void AsTypeExpr() { Test>((object obj) => obj as MyClass, (object obj) => obj as MyClass); Test>((object obj) => obj as int?, (object obj) => obj as int?); Test>>((object obj) => obj as GenericClass, (object obj) => obj as GenericClass); } public static void IsTypeExpr() { Test>((object obj) => obj is MyClass, (object obj) => obj is MyClass); Test>((object obj) => obj is int?, (object obj) => obj is int?); } public static void UnaryLogicalOperators() { Test>((bool a) => !a, (bool a) => !a); } public static void ConditionalOperator() { ToCode(null, (bool a) => a ? 5 : 10); ToCode(null, (object a) => a ?? new MyClass()); } public static void ComparisonOperators() { ToCode(null, (int a, int b) => a == b); ToCode(null, (int a, int b) => a != b); ToCode(null, (int a, int b) => a < b); ToCode(null, (int a, int b) => a <= b); ToCode(null, (int a, int b) => a > b); ToCode(null, (int a, int b) => a >= b); ToCode(null, (int a, int b) => a == 1 && b == 2); ToCode(null, (int a, int b) => a == 1 || b == 2); ToCode(null, (int a, short b) => a == b); ToCode(null, (ushort a, int b) => a != b); ToCode(null, (int a, long b) => (long)a < b); ToCode(null, (ulong a, uint b) => a <= (ulong)b); ToCode(null, (int a, uint b) => (long)a <= (long)b); ToCode(null, (int a, long b) => (long)a > b); ToCode(null, (short a, long b) => (long)a >= b); ToCode(null, (int a, int b) => a == 1 && b == 2); ToCode(null, (int a, int b) => a == 1 || b == 2); } public static void LiftedComparisonOperators() { ToCode(X(), (int? a, int? b) => a == b); ToCode(X(), (int? a, int? b) => a != b); ToCode(X(), (int? a, int? b) => a < b); ToCode(X(), (int? a, int? b) => a <= b); ToCode(X(), (int? a, int? b) => a > b); ToCode(X(), (int? a, int? b) => a >= b); } public static void UnaryArithmeticOperators() { Test>((int a) => a, (int a) => a); Test>((int a) => -a, (int a) => -a); } public static void BinaryArithmeticOperators() { Test>((int a, int b) => a + b, (int a, int b) => a + b); Test>((int a, int b) => a - b, (int a, int b) => a - b); Test>((int a, int b) => a * b, (int a, int b) => a * b); Test>((int a, int b) => a / b, (int a, int b) => a / b); Test>((int a, int b) => a % b, (int a, int b) => a % b); Test>((long a, int b) => a + b, (long a, int b) => a + (long)b); Test>((long a, int b) => a - b, (long a, int b) => a - (long)b); Test>((long a, int b) => a * b, (long a, int b) => a * (long)b); Test>((long a, int b) => a / b, (long a, int b) => a / (long)b); Test>((long a, int b) => a % b, (long a, int b) => a % (long)b); Test>((short a, int b) => a + b, (short a, int b) => a + b); Test>((int a, short b) => a - b, (int a, short b) => a - b); Test>((short a, int b) => a * b, (short a, int b) => a * b); Test>((int a, short b) => a / b, (int a, short b) => a / b); Test>((short a, int b) => a % b, (short a, int b) => a % b); } public static void BitOperators() { Test>((int a) => ~a, (int a) => ~a); Test>((int a, int b) => a & b, (int a, int b) => a & b); Test>((int a, int b) => a | b, (int a, int b) => a | b); Test>((int a, int b) => a ^ b, (int a, int b) => a ^ b); } public static void ShiftOperators() { Test>((int a) => a >> 2, (int a) => a >> 2); Test>((int a) => a << 2, (int a) => a << 2); Test>((long a) => a >> 2, (long a) => a >> 2); Test>((long a) => a << 2, (long a) => a << 2); } public static void SimpleExpressions() { Test>(() => 0, () => 0); Test>((int a) => a, (int a) => a); } public static void Capturing() { int captured = 5; Test>(() => captured, () => captured); } public static void FieldAndPropertyAccess() { ToCode(null, () => 1); ToCode(null, () => SimpleType.StaticField); ToCode(null, () => SimpleType.StaticReadonlyField); ToCode(null, () => SimpleType.StaticProperty); ToCode(null, () => SimpleType.StaticReadonlyProperty); ToCode(null, (SimpleType a) => a.Field); ToCode(null, (SimpleType a) => a.Property); ToCode(null, (SimpleType a) => a.ReadonlyField); ToCode(null, (SimpleType a) => a.ReadonlyProperty); } public static void Call() { ToCode(null, (string a) => Console.WriteLine(a)); Test>((string a) => a.ToString(), (string a) => a.ToString()); Test>((int a) => a.ToString(), (int a) => a.ToString()); Test>((string a) => a.ToArray(), (string a) => a.ToArray()); Test>(() => 'a'.CompareTo('b') < 0, () => 'a'.CompareTo('b') < 0); Test>(delegate (object lockObj, bool lockTaken) { Monitor.Enter(lockObj, ref lockTaken); }, (object lockObj, bool lockTaken) => Monitor.Enter(lockObj, ref lockTaken)); Test>((string str, int num) => int.TryParse(str, out num), (string str, int num) => int.TryParse(str, out num)); Test>((string str, SimpleType t) => int.TryParse(str, out t.Field), (string str, SimpleType t) => int.TryParse(str, out t.Field)); Test>(delegate (object o) { TestCall(o); }, (object o) => TestCall(o)); Test>(delegate (object o) { TestCall(ref o); }, (object o) => TestCall(ref o)); } public static void Quote() { Test>(() => (Expression>)((int n, string s) => s + n.ToString()) != null, () => (Expression>)((int n, string s) => s + n.ToString()) != null); } public static void ArrayInitializer() { Test>(() => new int[3] { 1, 2, 3 }, () => new int[3] { 1, 2, 3 }); Test>(() => new int[3], () => new int[3]); Test>(() => new int[3, 5], () => new int[3, 5]); Test>(() => new int[3][], () => new int[3][]); Test>(() => new int[1][] { new int[3] { 1, 2, 3 } }, () => new int[1][] { new int[3] { 1, 2, 3 } }); } public static void AnonymousTypes() { Test>(() => new { A = 5, B = "Test" }, () => new { A = 5, B = "Test" }); } public static void ObjectInit() { ToCode(null, () => new SimpleType { Property = 4, Field = 3 }); } public static void StringConcat() { Test>(null, (string a, object b) => a + b); Test>(null, (string a, object b) => a + b.ToString()); Test>(null, (string a, int b) => a + b); Test>(null, (string a, int b) => a + b.ToString()); } public async Task Issue1524(string str) { await Task.Delay(100); #if CS70 if (string.IsNullOrEmpty(str) && int.TryParse(str, out var id)) { #else int id; if (string.IsNullOrEmpty(str) && int.TryParse(str, out id)) { #endif (from a in new List().AsQueryable() where a == id select a).FirstOrDefault(); } } public void NullCoalescing() { Test>((string a, string b) => a ?? b, (string a, string b) => a ?? b); Test>((int? a) => a ?? 1, (int? a) => a ?? 1); } public void NullableLifting(Guid? a, Guid? b) { ToCode(null, () => a == b); ToCode(null, () => (Guid)a == (Guid)b); } public void NullableLifting_UnaryOperators() { ToCode(null, (int? a) => -a); ToCode(null, (int? a) => ~a); ToCode(null, (byte? a) => -(int?)a); ToCode(null, (byte? a) => ~(int?)a); ToCode(null, (short? a) => -(int?)a); ToCode(null, (short? a) => ~(int?)a); ToCode(null, (long? a) => -a); ToCode(null, (long? a) => ~a); ToCode(null, (uint? a) => -(long?)a); ToCode(null, (uint? a) => ~a); ToCode(null, (sbyte? a) => -(int?)a); ToCode(null, (sbyte? a) => ~(int?)a); ToCode(null, (ushort? a) => -(int?)a); ToCode(null, (ushort? a) => ~(int?)a); ToCode(null, (ulong? a) => ~a); ToCode(null, (bool? a) => !a); } public void NullableLifting_Arithmetic_BinaryOperators() { ToCode(null, (int? a) => a + (int?)1); ToCode(null, (int? a) => a + a); ToCode(null, (int? a) => a - (int?)1); ToCode(null, (int? a) => a - a); ToCode(null, (int? a) => a * (int?)2); ToCode(null, (int? a) => a * a); ToCode(null, (int? a) => a / (int?)2); ToCode(null, (int? a) => a / a); ToCode(null, (int? a) => a % (int?)2); ToCode(null, (int? a) => a % a); ToCode(null, (int? a) => a << (int?)2); ToCode(null, (int? a) => a << a); ToCode(null, (int? a) => a >> (int?)2); ToCode(null, (int? a) => a >> a); ToCode(null, (int? a) => a ^ (int?)2); ToCode(null, (int? a) => a ^ a); ToCode(null, (int? a) => a | (int?)2); ToCode(null, (int? a) => a | a); ToCode(null, (int? a) => a & (int?)2); ToCode(null, (int? a) => a & a); } } internal static class Extensions { public static dynamic ToJson(this object o) { return null; } public static DateTime ParseDateTime(this object str) { return default(DateTime); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExtensionProperties.cs ================================================ using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class ExtensionProperties { extension(ICollection collection) where T : notnull { public bool IsEmpty => collection.Count == 0; public int Test { get { return 42; } set { } } public void AddIfNotNull(T item) { if (item != null) { collection.Add(item); } } public T2 Cast(int index) where T2 : T { return (T2)(object)collection.ElementAt(index); } public static void StaticExtension() { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty; internal delegate void DelegateInFileScopedNamespace(); internal class FileScopedNamespaces { } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { internal class A { protected internal virtual Task Test(string test) { return Task.Run(() => test.ToUpper()); } } internal class B : A { protected internal override async Task Test(string test) { return await base.Test(test); } } internal class B2 : A { protected internal override async Task Test(string test) { return await base.Test(test); } } internal class C { protected internal virtual string Test(string test) { return string.Join(test, "fsdf"); } } internal class D : C { protected internal IEnumerable Test2(string test) { yield return base.Test(test); } } internal class E { protected internal virtual string Test(string test) { return string.Join(test, "fsdf"); } } internal class F : E { protected internal override string Test(string test) { Func func = (string a) => base.Test(a); test = string.Join(test, "aa"); return func(test); } } [CompilerGenerated] internal class FalsePositive_Issue1443 { private static void WrongMethod() { Console.WriteLine("Wrong!"); } private void CorrectMethod() { WrongMethod(); } private void Use() { CorrectMethod(); } } internal class G { protected internal virtual void Test(string test) { string.Join(test, "fsdf"); } } internal class H : G { private Action action; protected internal override void Test(string test) { action = delegate (string a) { base.Test(a); }; if (test.Equals(1)) { throw new Exception("roslyn optimizes is inlining the assignment which lets the test fail"); } action(test); } } internal class I { protected internal virtual void Test(int a) { } } public class Issue1660 : Issue1660Base { public Action M(object state) { return delegate (object x) { base.BaseCall(x, state, () => (object)null); }; } } public class Issue1660Base { protected virtual void BaseCall(object x, object state, Func action) { } } internal class J : I { protected internal override void Test(int a) { Action action = delegate { base.Test(a); }; if (a.Equals(1)) { throw new Exception("roslyn optimize is inlining the assignment which lets the test fail"); } action(); } } internal class K { protected internal virtual IEnumerable Test(int p) { yield return p + 1; yield return p + 2; } } internal class L : K { protected internal override IEnumerable Test(int p) { yield return base.Test(base.Test(0).GetEnumerator().Current).GetEnumerator().Current; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class FunctionPointerAddressOf { public static void Overloaded() { } public static void Overloaded(int a) { } public unsafe delegate* GetAddress() { return &Overloaded; } #if !(CS110 && NET70) public unsafe IntPtr GetAddressAsIntPtr() { return (IntPtr)(delegate*)(&Overloaded); } #endif public unsafe nint GetAddressAsNInt() { return (nint)(delegate*)(&Overloaded); } public unsafe void* GetAddressAsVoidPtr() { return (delegate*)(&Overloaded); } public static string VarianceTest(object o) { return null; } public unsafe delegate* Variance() { return (delegate*)(&VarianceTest); } public unsafe delegate* AddressOfLocalFunction_Managed() { return &LocalFunction; static void LocalFunction() { } } public unsafe delegate* unmanaged AddressOfLocalFunction_Unmanaged() { return &LocalFunction; [UnmanagedCallersOnly] static void LocalFunction() { } } public unsafe delegate* unmanaged[Cdecl] AddressOfLocalFunction_CDecl() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Fastcall] AddressOfLocalFunction_Fastcall() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvFastcall) })] static void LocalFunction() { } } #if NET60 public unsafe delegate* unmanaged[MemberFunction] AddressOfLocalFunction_MemberFunction() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvMemberFunction) })] static void LocalFunction() { } } #endif public unsafe delegate* unmanaged[Stdcall] AddressOfLocalFunction_Stdcall() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvStdcall) })] static void LocalFunction() { } } #if NET60 public unsafe delegate* unmanaged[SuppressGCTransition] AddressOfLocalFunction_SuppressGCTransition() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSuppressGCTransition) })] static void LocalFunction() { } } #endif public unsafe delegate* unmanaged[Thiscall] AddressOfLocalFunction_Thiscall() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvThiscall) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Cdecl, Fastcall] AddressOfLocalFunction_CDeclAndFastcall() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl), typeof(CallConvFastcall) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Fastcall, Cdecl] AddressOfLocalFunction_FastcallAndCDecl() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvFastcall), typeof(CallConvCdecl) })] static void LocalFunction() { } } #if NET60 public unsafe delegate* unmanaged[Cdecl, SuppressGCTransition] AddressOfLocalFunction_CDeclAndSuppressGCTransition() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl), typeof(CallConvSuppressGCTransition) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Fastcall, SuppressGCTransition] AddressOfLocalFunction_FastcallAndSuppressGCTransition() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvFastcall), typeof(CallConvSuppressGCTransition) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Stdcall, SuppressGCTransition] AddressOfLocalFunction_StdcallAndSuppressGCTransition() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvStdcall), typeof(CallConvSuppressGCTransition) })] static void LocalFunction() { } } public unsafe delegate* unmanaged[Thiscall, SuppressGCTransition] AddressOfLocalFunction_ThiscallAndSuppressGCTransition() { return &LocalFunction; [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvThiscall), typeof(CallConvSuppressGCTransition) })] static void LocalFunction() { } } #endif } internal class FunctionPointersWithCallingConvention { public unsafe delegate* fn_default; // Unmanaged without explicit callconv is only supported with .NET 5, // and emits metadata that cannot be parsed by older SRM versions. //public delegate* unmanaged fn_unmanaged; public unsafe delegate* unmanaged[Cdecl] fn_cdecl; public unsafe delegate* unmanaged[Fastcall] fn_fastcall; public unsafe delegate* unmanaged[Stdcall] fn_stdcall; public unsafe delegate* unmanaged[Thiscall] fn_thiscall; } internal class FunctionPointersWithDynamicTypes { public class D { } public class A { public class B { } } public unsafe delegate* F1; public unsafe delegate* F2; public unsafe delegate* F3; public unsafe delegate* F4; public unsafe delegate* F5; public unsafe delegate* F6; public unsafe delegate* F7; public unsafe delegate* F8; public unsafe delegate* F9; public unsafe delegate* F10; public unsafe delegate* F11; public unsafe delegate* F12; public unsafe delegate* F13; public unsafe delegate* F14; #if CS120 public unsafe delegate* F15; #endif public unsafe D[], dynamic> F16; public unsafe delegate*.B> F17; } internal class FunctionPointersWithNativeIntegerTypes { public unsafe delegate* F1; #if !(CS110 && NET70) public unsafe delegate* F2; public unsafe delegate* F3; public unsafe delegate* F4; public unsafe delegate*, nint> F5; public unsafe delegate*> F6; public unsafe delegate*, IntPtr> F7; public unsafe delegate*> F8; public unsafe delegate*> F9; #endif } internal class FunctionPointersWithRefParams { public unsafe delegate* F1; public unsafe delegate* F2; public unsafe int CallF1(byte b, char c, out float f) { return F1(in b, ref c, out f); } public unsafe void CallF2(byte b, char c, out float f) { F2(ref c, out f) = b; } } internal class FunctionPointerTypeInference { private static char Test(int i) { return (char)i; } public unsafe R GenericMethod(delegate* f, T arg) { return f(arg); } public unsafe void Call() { delegate* f = &Test; GenericMethod(f, 0); GenericMethod((delegate*)(&Test), 1); GenericMethod(null, 2); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Generics { private class GenericClass { private readonly T issue1760; public void M(out GenericClass self) { self = this; } public void Issue1760() { Console.WriteLine(", " + issue1760); } } public class BaseClass { } public class DerivedClass : BaseClass { } public class MyArray { public class NestedClass { public T Item1; public Y Item2; } public enum NestedEnum { A, B } private T[] arr; public MyArray(int capacity) { arr = new T[capacity]; } public void Size(int capacity) { Array.Resize(ref arr, capacity); } public void Grow(int capacity) { if (capacity >= arr.Length) { Size(capacity); } } } public interface IInterface { void Method1() where T : class; void Method2() where T : class; void Method3(int a, string b, Type c); #if CS72 void Method4(in int a); #endif } public abstract class Base : IInterface { // constraints must be repeated on implicit interface implementation public abstract void Method1() where T : class; // constraints must not be specified on explicit interface implementation void IInterface.Method2() { } void IInterface.Method3(int a, string b, Type c) { } #if CS72 void IInterface.Method4(in int a) { } #endif } public class Derived : Base { // constraints are inherited automatically and must not be specified public override void Method1() { } } private const MyArray.NestedEnum enumVal = MyArray.NestedEnum.A; private static Type type1 = typeof(List<>); private static Type type2 = typeof(MyArray<>); private static Type type3 = typeof(List<>.Enumerator); private static Type type4 = typeof(MyArray<>.NestedClass<>); private static Type type5 = typeof(List[]); private static Type type6 = typeof(MyArray<>.NestedEnum); public T CastToTypeParameter(DerivedClass d) where T : BaseClass { return (T)(BaseClass)d; } public TTarget GenericAsGeneric(TSource source) where TTarget : class { return source as TTarget; } public TTarget? GenericAsNullable(TSource source) where TTarget : struct { return source as TTarget?; } public TTarget ObjectAsGeneric(object source) where TTarget : class { return source as TTarget; } public TTarget? ObjectAsNullable(object source) where TTarget : struct { return source as TTarget?; } public TTarget IntAsGeneric(int source) where TTarget : class { return source as TTarget; } public TTarget? IntAsNullable(int source) where TTarget : struct { return source as TTarget?; } public T New() where T : new() { return new T(); } public T NotNew() { return Activator.CreateInstance(); } public bool IsNull(T t) { return t == null; } public T[] NewArray(int size) { return new T[size]; } public T[,] NewArray(int size1, int size2) { return new T[size1, size2]; } public Type[] TestTypeOf() { return new Type[8] { typeof(int), typeof(int[]), typeof(GenericClass<>), typeof(GenericClass), typeof(GenericClass), typeof(Dictionary<, >), typeof(List.Enumerator), typeof(List<>.Enumerator) }; } public static void MethodWithConstraint() where T : class, S where S : ICloneable, new() { } public static void MethodWithStructConstraint() where T : struct { } private static void MultidimensionalArray(T[,] array) { array[0, 0] = array[0, 1]; } public static Dictionary.KeyCollection.Enumerator GetEnumerator(Dictionary d, MyArray.NestedClass nc) { // Tests references to inner classes in generic classes return d.Keys.GetEnumerator(); } public static bool IsString(T input) { return input is string; } public static string AsString(T input) { return input as string; } public static string CastToString(T input) { return (string)(object)input; } public static T CastFromString(string input) { return (T)(object)input; } public static bool IsInt(T input) { return input is int; } public static int CastToInt(T input) { return (int)(object)input; } public static T CastFromInt(int input) { return (T)(object)input; } public static bool IsNullableInt(T input) { return input is int?; } public static int? AsNullableInt(T input) { return input as int?; } public static int? CastToNullableInt(T input) { return (int?)(object)input; } public static T CastFromNullableInt(int? input) { return (T)(object)input; } #if CS73 public static object CallDelegate(T input) where T : Delegate { return input.DynamicInvoke(); } public static int CountEnumerators() where T : Enum { return typeof(T).GetEnumValues().Length; } public unsafe static int UnmanagedConstraint() where T : unmanaged { return sizeof(T); } #endif #if NET90 public static void AllowsRefStruct() where T : allows ref struct { } #endif public static void Issue1959(int a, int b, int? c) { // This line requires parentheses around `a < b` to avoid a grammar ambiguity. Console.WriteLine("{}, {}", (a < b), a > (c ?? b)); // But here there's no ambiguity: Console.WriteLine("{}, {}", a < b, a > b); Console.WriteLine("{}, {}", a < Environment.GetLogicalDrives().Length, a > (c ?? b)); } public static Type Issue2231() { return default(T).GetType(); } public static string Issue2231b() { return default(T).ToString(); } public static void ConstrainedCall(T x, ref T y) where T : IDisposable { x.Dispose(); y.Dispose(); } // prior to C# 7.0 UseRefLocalsForAccurateOrderOfEvaluation is disabled, so we will inline. // Roslyn 4 generates the explicit ldobj.if.ref pattern, so we can also inline. // The versions in between, we don't inline, so the code doesn't look pretty. #if ROSLYN4 || !CS70 public static int[] Issue3438(T[] array) { List list = new List(); for (int i = 0; i < array.Length; i++) { if (!array[i].Equals(default(T))) { list.Add(i); } } return list.ToArray(); } public void Issue3438b(T[] item1, T item2, int item3) where T : IInterface { item1[CastToInt(item2)].Method3(CastToInt(item2), CastToString(item2), TestTypeOf()[1]); } public void Issue3438c(T item, T item2, int item3) where T : IInterface { CastFromInt(item3).Method3(CastToInt(item2), CastToString(item2), TestTypeOf()[1]); } //#if CS72 // Disabled because ILInlining does not support inlining ldloca currently. // public void Issue3438d(T[] item1, T item2, int item3) where T : IInterface // { // item1[CastToInt(item2)].Method4(CastToInt(item2)); // } //#endif #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/GloballyQualifiedTypeInStringInterpolation.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class GloballyQualifiedTypeInStringInterpolation { public static string Root => $"Prefix {(global::System.DateTime.Now)} suffix"; public static string Cast => $"Prefix {((int)global::System.DateTime.Now.Ticks)} suffix"; #if CS100 && NET60 public static string Lambda1 => $"Prefix {(() => global::System.DateTime.Now)} suffix"; #else public static string Lambda1 => $"Prefix {(global::System.Func)(() => global::System.DateTime.Now)} suffix"; #endif public static string Lambda2 => $"Prefix {((global::System.Func)(() => global::System.DateTime.Now))()} suffix"; public static string Method1 => $"Prefix {M(global::System.DateTime.Now)} suffix"; public static string Method2 => $"Prefix {(global::System.DateTime.Now.Ticks)} suffix"; public static string Method3 => $"Prefix {(global::System.DateTime.Equals(global::System.DateTime.Now, global::System.DateTime.Now))} suffix"; public static string ConditionalExpression1 => $"Prefix {(Boolean ? global::System.DateTime.Now : global::System.DateTime.UtcNow)} suffix"; public static string ConditionalExpression2 => $"Prefix {(Boolean ? global::System.DateTime.Now : global::System.DateTime.UtcNow).Ticks} suffix"; private static bool Boolean => false; private static long M(global::System.DateTime time) { return time.Ticks; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/HelloWorld.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class HelloWorld { public static void Main() { Console.WriteLine("Hello World!"); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/IndexRangeTest.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class CustomList { public int Count => 0; public int this[int index] => 0; public CustomList Slice(int start, int length) { return this; } } internal class CustomList2 { public int Count => 0; public int this[int index] => 0; public int this[Index index] => 0; public CustomList2 this[Range range] => this; public CustomList2 Slice(int start, int length) { return this; } } internal class IndexRangeTest { public static int[] GetArray() { throw null; } public static List GetList() { throw null; } public static Span GetSpan() { throw null; } public static string GetString() { throw null; } public static Index GetIndex(int i = 0) { return i; } public static Range GetRange(int i = 0) { return i..^i; } public static int GetInt(int i = 0) { return i; } public static Range[] SeveralRanges() { // Some of these are semantically identical, but we can still distinguish them in the IL code: return new Range[14] { .., 0.., ^0.., GetInt(1).., ^GetInt(2).., ..0, ..^0, ..GetInt(3), ..^GetInt(4), 0..^0, ^0..0, 0..0, GetInt(5)..GetInt(6), 0..(GetInt(7) + GetInt(8)) }; } public static void UseIndex() { Console.WriteLine(GetArray()[GetIndex()]); Console.WriteLine(GetList()[GetIndex()]); Console.WriteLine(GetSpan()[GetIndex()]); Console.WriteLine(GetString()[GetIndex()]); Console.WriteLine(GetString()?[GetIndex()]); Console.WriteLine(new CustomList()[GetIndex()]); Console.WriteLine(new CustomList2()[GetIndex()]); } public static void UseIndexFromEnd() { Console.WriteLine(GetArray()[^GetInt()]); Console.WriteLine(GetList()[^GetInt()]); Console.WriteLine(GetSpan()[^GetInt()]); Console.WriteLine(GetString()[^GetInt()]); Console.WriteLine(GetString()?[^GetInt()]); Console.WriteLine(new CustomList()[^GetInt()]); Console.WriteLine(new CustomList2()[^GetInt()]); } public static void UseIndexForWrite() { GetArray()[GetIndex()] = GetInt(); GetList()[GetIndex()] = GetInt(); GetSpan()[GetIndex()] = GetInt(); } private static void UseRef(ref int i) { } public static void UseIndexForRef() { UseRef(ref GetArray()[GetIndex()]); UseRef(ref GetArray()[^GetInt()]); UseRef(ref GetSpan()[GetIndex()]); UseRef(ref GetSpan()[^GetInt()]); } public static void UseRange() { Console.WriteLine(GetArray()[GetRange()]); //Console.WriteLine(GetList()[GetRange()]); // fails to compile Console.WriteLine(GetSpan()[GetRange()].ToString()); Console.WriteLine(GetString()[GetRange()]); Console.WriteLine(GetString()?[GetRange()]); Console.WriteLine(new CustomList()[GetRange()]); Console.WriteLine(new CustomList2()[GetRange()]); } public static void UseNewRangeFromIndex() { Console.WriteLine(GetArray()[GetIndex(1)..GetIndex(2)]); //Console.WriteLine(GetList()[GetIndex(1)..GetIndex(2)]); // fails to compile Console.WriteLine(GetSpan()[GetIndex(1)..GetIndex(2)].ToString()); Console.WriteLine(GetString()[GetIndex(1)..GetIndex(2)]); Console.WriteLine(GetString()?[GetIndex(1)..GetIndex(2)]); Console.WriteLine(new CustomList()[GetIndex(1)..GetIndex(2)]); Console.WriteLine(new CustomList2()[GetIndex(1)..GetIndex(2)]); } public static void UseNewRangeFromIntegers_BothFromStart() { Console.WriteLine(GetArray()[GetInt(1)..GetInt(2)]); //Console.WriteLine(GetList()[GetInt()..GetInt()]); // fails to compile Console.WriteLine(GetSpan()[GetInt(1)..GetInt(2)].ToString()); Console.WriteLine(GetString()[GetInt(1)..GetInt(2)]); Console.WriteLine(GetString()?[GetInt(1)..GetInt(2)]); Console.WriteLine(new CustomList()[GetInt(1)..GetInt(2)]); Console.WriteLine(new CustomList2()[GetInt(1)..GetInt(2)]); } public static void UseNewRangeFromIntegers_BothFromEnd() { Console.WriteLine(GetArray()[^GetInt(1)..^GetInt(2)]); //Console.WriteLine(GetList()[^GetInt()..^GetInt()]); // fails to compile Console.WriteLine(GetSpan()[^GetInt(1)..^GetInt(2)].ToString()); Console.WriteLine(GetString()[^GetInt(1)..^GetInt(2)]); Console.WriteLine(GetString()?[^GetInt(1)..^GetInt(2)]); Console.WriteLine(new CustomList()[^GetInt(1)..^GetInt(2)]); Console.WriteLine(new CustomList2()[^GetInt(1)..^GetInt(2)]); } public static void UseNewRangeFromIntegers_FromStartAndEnd() { Console.WriteLine(GetArray()[GetInt(1)..^GetInt(2)]); //Console.WriteLine(GetList()[GetInt()..^GetInt()]); // fails to compile Console.WriteLine(GetSpan()[GetInt(1)..^GetInt(2)].ToString()); Console.WriteLine(GetString()[GetInt(1)..^GetInt(2)]); Console.WriteLine(GetString()?[GetInt(1)..^GetInt(2)]); Console.WriteLine(new CustomList()[GetInt(1)..^GetInt(2)]); Console.WriteLine(new CustomList2()[GetInt(1)..^GetInt(2)]); } public static void UseNewRangeFromIntegers_FromEndAndStart() { Console.WriteLine(GetArray()[^GetInt(1)..GetInt(2)]); //Console.WriteLine(GetList()[^GetInt()..GetInt()]); // fails to compile Console.WriteLine(GetSpan()[^GetInt(1)..GetInt(2)].ToString()); Console.WriteLine(GetString()[^GetInt(1)..GetInt(2)]); Console.WriteLine(GetString()?[^GetInt(1)..GetInt(2)]); Console.WriteLine(new CustomList()[^GetInt(1)..GetInt(2)]); Console.WriteLine(new CustomList2()[^GetInt(1)..GetInt(2)]); } public static void UseNewRangeFromIntegers_OnlyEndPoint() { Console.WriteLine(GetArray()[..GetInt(2)]); //Console.WriteLine(GetList()[..GetInt()]); // fails to compile Console.WriteLine(GetSpan()[..GetInt(2)].ToString()); Console.WriteLine(GetString()[..GetInt(2)]); Console.WriteLine(GetString()?[..GetInt(2)]); Console.WriteLine(new CustomList()[..GetInt(2)]); Console.WriteLine(new CustomList2()[..GetInt(2)]); } public static void UseNewRangeFromIntegers_OnlyEndPoint_FromEnd() { Console.WriteLine(GetArray()[..^GetInt(2)]); //Console.WriteLine(GetList()[..^GetInt()]); // fails to compile Console.WriteLine(GetSpan()[..^GetInt(2)].ToString()); Console.WriteLine(GetString()[..^GetInt(2)]); Console.WriteLine(GetString()?[..^GetInt(2)]); Console.WriteLine(new CustomList()[..^GetInt(2)]); Console.WriteLine(new CustomList2()[..^GetInt(2)]); } public static void UseNewRangeFromIntegers_OnlyStartPoint() { Console.WriteLine(GetArray()[GetInt(1)..]); //Console.WriteLine(GetList()[GetInt()..]); // fails to compile Console.WriteLine(GetSpan()[GetInt(1)..].ToString()); Console.WriteLine(GetString()[GetInt(1)..]); Console.WriteLine(GetString()?[GetInt(1)..]); Console.WriteLine(new CustomList()[GetInt(1)..]); Console.WriteLine(new CustomList2()[GetInt(1)..]); } public static void UseNewRangeFromIntegers_OnlyStartPoint_FromEnd() { Console.WriteLine(GetArray()[^GetInt(1)..]); //Console.WriteLine(GetList()[^GetInt()..]); // fails to compile Console.WriteLine(GetSpan()[^GetInt(1)..].ToString()); Console.WriteLine(GetString()[^GetInt(1)..]); Console.WriteLine(GetString()?[^GetInt(1)..]); Console.WriteLine(new CustomList()[^GetInt(1)..]); Console.WriteLine(new CustomList2()[^GetInt(1)..]); } public static void UseConstantRange() { // Fortunately the C# compiler doesn't optimize // "str.Length - 2 - 1" here, so the normal pattern applies. Console.WriteLine(GetString()[1..2]); Console.WriteLine(GetString()[1..^1]); Console.WriteLine(GetString()[^2..^1]); Console.WriteLine(GetString()[..1]); Console.WriteLine(GetString()[..^1]); Console.WriteLine(GetString()[1..]); Console.WriteLine(GetString()[^1..]); } public static void UseWholeRange() { Console.WriteLine(GetArray()[..]); //Console.WriteLine(GetList()[..]); // fails to compile Console.WriteLine(GetSpan()[..].ToString()); Console.WriteLine(GetString()[..]); Console.WriteLine(new CustomList()[..]); Console.WriteLine(new CustomList2()[..]); } public static void UseIndexForIntIndexerWhenIndexIndexerIsAvailable() { // Same code as the compiler emits for CustomList, // but here we can't translate it back to `customList[GetIndex()]` // because that would call a different overload. CustomList2 customList = new CustomList2(); int count = customList.Count; int offset = GetIndex().GetOffset(count); Console.WriteLine(customList[offset]); } public static void UseSliceWhenRangeIndexerIsAvailable() { // Same code as the compiler emits for CustomList, // but here we can't translate it back to `customList[GetIndex()]` // because that would call a different overload. CustomList2 customList = new CustomList2(); int count = customList.Count; Range range = GetRange(); int offset = range.Start.GetOffset(count); int length = range.End.GetOffset(count) - offset; Console.WriteLine(customList.Slice(offset, length)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs ================================================ // Copyright (c) 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 // 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; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests { public static class Extensions { public static void Add(this TestCases.CustomList inst, string a, string b) { } public static void Add(this IList> collection, string key, T value, Func convert = null) { } public static void Add(this TestCases collection, string key) { } } public class TestCases { #region Types public class CustomList : IEnumerable, IEnumerable { public IEnumerator GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } public void Add(string name) { new Dictionary().Add(name, typeof(T2)); } public void Add(params int[] ints) { } } public class C { public int Z; public S Y; public List L; public S this[int index] { get { return default(S); } set { } } public S this[object key] { get { return default(S); } set { } } } public struct S { public int A; public int B; public int M() { return 42; } public S(int a) { A = a; B = 0; } } private enum MyEnum { a, b } private enum MyEnum2 { c, d } private class Data { public List FieldList = new List(); public MyEnum a { get; set; } public MyEnum b { get; set; } public List PropertyList { get; set; } #if CS60 public List ReadOnlyPropertyList { get; } #endif public Data MoreData { get; set; } public StructData NestedStruct { get; set; } public Data this[int i] { get { return null; } set { } } public Data this[int i, string j] { get { return null; } set { } } public event EventHandler TestEvent; } private struct StructData { public int Field; public int Property { get; set; } public Data MoreData { get; set; } public StructData(int initialValue) { this = default(StructData); Field = initialValue; Property = initialValue; } } public class Item { public string Text { get; set; } public decimal Value { get; set; } public decimal Value2 { get; set; } public string Value3 { get; set; } public string Value4 { get; set; } public string Value5 { get; set; } public string Value6 { get; set; } #if CS90 public Fields Value7 { get; set; } #endif } public class OtherItem { public decimal Value { get; set; } public decimal Value2 { get; set; } public decimal? Nullable { get; set; } public decimal? Nullable2 { get; set; } public decimal? Nullable3 { get; set; } public decimal? Nullable4 { get; set; } } public class OtherItem2 { public readonly OtherItem Data; public OtherItem Data2 { get; private set; } #if CS60 public OtherItem Data3 { get; } #endif } public class V3f { private float x; private float y; private float z; public V3f(float _x, float _y, float _z) { x = _x; y = _y; z = _z; } } #if CS90 public record Fields { public int A; public double B = 1.0; public object C; public dynamic D; public string S = "abc"; public Item I; } public record DerivedFields : Fields { public ConsoleKey E; } #endif public interface IData { int Property { get; set; } } #if CS90 public class Issue3392Type { public bool Flag { get; init; } public List List { get; } = new List(); public Issue3392Type(object x) { } } private class StructInitPropertiesTest { private class TypeA { public int A { get; set; } public int B { get; set; } } private struct TypeB { public int A { get; set; } public int B { get; set; } } private struct TypeC { public int A { get; init; } public int B { get; init; } } private static TypeA TestA() { return new TypeA { A = 1, B = 2 }; } private static TypeB TestB() { return new TypeB { A = 1, B = 2 }; } private static TypeC TestC() { return new TypeC { A = 1, B = 2 }; } } #endif #endregion private S s1; private S s2; #region Field initializer tests private static V3f[] Issue1336_rg0 = new V3f[3] { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }; private static V3f[,] Issue1336_rg1 = new V3f[3, 3] { { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } }; private static V3f[][] Issue1336_rg1b = new V3f[3][] { new V3f[3] { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, new V3f[3] { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, new V3f[3] { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } }; private static V3f[,][] Issue1336_rg1c = new V3f[3, 3][] { { new V3f[3] { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, new V3f[3] { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, new V3f[3] { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } }, { new V3f[3] { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, new V3f[3] { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, new V3f[3] { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } }, { new V3f[3] { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, new V3f[3] { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, new V3f[3] { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } } }; private static V3f[][,] Issue1336_rg1d = new V3f[2][,] { new V3f[3, 3] { { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } }, new V3f[3, 3] { { new V3f(1f, 1f, 1f), new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f) }, { new V3f(2f, 2f, 2f), new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f) }, { new V3f(3f, 3f, 3f), new V3f(4f, 4f, 4f), new V3f(5f, 5f, 5f) } } }; private static int[,] Issue1336_rg2 = new int[3, 3] { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } }; #if CS73 && !NET40 public static ReadOnlySpan StaticData1 => new byte[1] { 0 }; public static ReadOnlySpan StaticData3 => new byte[3] { 1, 2, 3 }; public static Span StaticData3Span => new byte[3] { 1, 2, 3 }; #endif #if CS110 && !NET40 public static ReadOnlySpan UTF8Literal => "Hello, world!"u8; public static ReadOnlySpan UTF8LiteralWithNullTerminator => "Hello, world!\0"u8; #endif #endregion #region Helper methods used to ensure initializers used within expressions work correctly private static void X(object a, object b) { } private static object Y() { return null; } public static void TestCall(int a, Thread thread) { } public static C TestCall(int a, C c) { return c; } private static int GetInt() { return 1; } private static string GetString() { return "Test"; } private static void NoOp(Guid?[] array) { } private void Data_TestEvent(object sender, EventArgs e) { throw new NotImplementedException(); } #endregion #region Array initializers public static void Array1() { X(Y(), new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); } public static void Array2(int a, int b, int c) { X(Y(), new int[5] { a, 0, b, 0, c }); } public static void NestedArray(int a, int b, int c) { X(Y(), new int[3][] { new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, new int[3] { a, b, c }, new int[6] { 1, 2, 3, 4, 5, 6 } }); } public static void NestedNullableArray(int a, int b, int c) { X(Y(), new int?[3][] { new int?[11] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, null }, new int?[4] { a, b, c, null }, new int?[7] { 1, 2, 3, 4, 5, 6, null } }); } public unsafe static void NestedPointerArray(int a, int b, int c) { X(Y(), new void*[3][] { new void*[1] { null }, new void*[2] { (void*)200, null }, new void*[2] { (void*)100, null } }); } public static void ArrayBoolean() { X(Y(), new bool[8] { true, false, true, false, false, false, true, true }); } public static void ArrayByte() { X(Y(), new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 254, 255 }); } public static void ArraySByte() { X(Y(), new sbyte[8] { -128, -127, 0, 1, 2, 3, 4, 127 }); } public static void ArrayShort() { X(Y(), new short[5] { -32768, -1, 0, 1, 32767 }); } public static void ArrayUShort() { X(Y(), new ushort[6] { 0, 1, 32767, 32768, 65534, 65535 }); } public static void ArrayInt() { X(Y(), new int[10] { 1, -2, 2000000000, 4, 5, -6, 7, 8, 9, 10 }); } public static void ArrayUInt() { X(Y(), new uint[10] { 1u, 2000000000u, 3000000000u, 4u, 5u, 6u, 7u, 8u, 9u, 10u }); } public static void ArrayLong() { X(Y(), new long[5] { -4999999999999999999L, -1L, 0L, 1L, 4999999999999999999L }); } public static void ArrayULong() { X(Y(), new ulong[10] { 1uL, 2000000000uL, 3000000000uL, 4uL, 5uL, 6uL, 7uL, 8uL, 4999999999999999999uL, 9999999999999999999uL }); } public static void ArrayFloat() { X(Y(), new float[6] { -1.5f, 0f, 1.5f, float.NegativeInfinity, float.PositiveInfinity, float.NaN }); } public static void ArrayDouble() { X(Y(), new double[6] { -1.5, 0.0, 1.5, double.NegativeInfinity, double.PositiveInfinity, double.NaN }); } public static void ArrayDecimal() { X(Y(), new decimal[6] { -100m, 0m, 100m, -79228162514264337593543950335m, 79228162514264337593543950335m, 0.0000001m }); } public static void ArrayString() { X(Y(), new string[4] { "", null, "Hello", "World" }); } public static void ArrayEnum() { X(Y(), new MyEnum[4] { MyEnum.a, MyEnum.b, MyEnum.a, MyEnum.b }); } public int[,] MultidimensionalInit() { return new int[16, 4] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 } }; } public int[][,] MultidimensionalInit2() { return new int[4][,] { new int[4, 4] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, new int[4, 4] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, new int[4, 4] { { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 } }, new int[4, 4] { { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 } } }; } public int[][,,] ArrayOfArrayOfArrayInit() { return new int[2][,,] { new int[2, 3, 3] { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }, { { 11, 12, 13 }, { 14, 15, 16 }, { 17, 18, 19 } } }, new int[2, 3, 3] { { { 21, 22, 23 }, { 24, 25, 26 }, { 27, 28, 29 } }, { { 31, 32, 33 }, { 34, 35, 36 }, { 37, 38, 39 } } } }; } public static void RecursiveArrayInitializer() { int[] array = new int[3]; array[0] = 1; array[1] = 2; array[2] = array[1] + 1; array[0] = 0; } public static void InvalidIndices(int a) { int[] array = new int[1]; array[1] = a; X(Y(), array); } public static void InvalidIndices2(int a) { #pragma warning disable 251 int[] array = new int[1]; array[-1] = a; X(Y(), array); #pragma warning restore } public static void IndicesInWrongOrder(int a, int b) { int[] array = new int[5]; array[2] = b; array[1] = a; X(Y(), array); } public int[] IndicesInWrongOrderConstantsFull() { int[] array = new int[3]; array[0] = 0; array[2] = 1; array[1] = 2; return array; } public static byte[] ReverseInitializer(int i) { byte[] array = new byte[4]; array[3] = (byte)i; array[2] = (byte)(i >> 8); array[1] = (byte)(i >> 16); array[0] = (byte)(i >> 24); return array; } public static void Issue953_MissingNullableSpecifierForArrayInitializer() { NoOp(new Guid?[1] { Guid.Empty }); } private void Issue907_Test3(string text) { X(Y(), new Dictionary { { "", text } }); } private int[] Issue1383(int i, int[] array) { array = new int[4]; array[i++] = 1; array[i++] = 2; return array; } private string[,] Issue1382a() { return new string[4, 4] { { null, "test", "hello", "world" }, { "test", null, "hello", "world" }, { "test", "hello", null, "world" }, { "test", "hello", "world", null } }; } private string[,] Issue1382b() { return new string[4, 4] { { "test", "hello", "world", null }, { "test", "hello", null, "world" }, { "test", null, "hello", "world" }, { null, "test", "hello", "world" } }; } private static void OutOfMemory() { byte[] array = new byte[int.MaxValue]; array[0] = 1; Console.WriteLine(array.Length); } #if !NET40 && CS70 public static ReadOnlySpan ReadOnlySpanInitializer_ByteArray() { return new byte[3] { 1, 2, 3 }; } public static ReadOnlySpan ReadOnlySpanInitializer_Int32Array() { return new int[3] { 1, 2, 3 }; } #endif #endregion #region Object initializers public C Test1() { C c = new C(); c.L = new List(); c.L.Add(new S(1)); return c; } public C Test1Alternative() { return TestCall(1, new C { L = new List { new S(1) } }); } public C Test2() { C c = new C(); c.Z = 1; c.Z = 2; return c; } public C Test3() { C c = new C(); c.Y = new S(1); c.Y.A = 2; return c; } public C Test3b() { return TestCall(0, new C { Z = 1, Y = { A = 2 } }); } public C Test4() { C c = new C(); c.Y.A = 1; c.Z = 2; c.Y.B = 3; return c; } public static void ObjectInitializer() { X(Y(), new Data { a = MyEnum.a }); } public static void NotAnObjectInitializer() { Data data = new Data(); data.a = MyEnum.a; X(Y(), data); } public static void NotAnObjectInitializerWithEvent() { Data data = new Data(); data.TestEvent += delegate { Console.WriteLine(); }; X(Y(), data); } public static void ObjectInitializerAssignCollectionToField() { X(Y(), new Data { a = MyEnum.a, FieldList = new List { MyEnum2.c, MyEnum2.d } }); } public static void ObjectInitializerAddToCollectionInField() { X(Y(), new Data { a = MyEnum.a, FieldList = { MyEnum2.c, MyEnum2.d } }); } public static void ObjectInitializerAssignCollectionToProperty() { X(Y(), new Data { a = MyEnum.a, PropertyList = new List { MyEnum2.c, MyEnum2.d } }); } public static void ObjectInitializerAddToCollectionInProperty() { X(Y(), new Data { a = MyEnum.a, PropertyList = { MyEnum2.c, MyEnum2.d } }); } public static void ObjectInitializerWithInitializationOfNestedObjects() { X(Y(), new Data { MoreData = { a = MyEnum.a, MoreData = { a = MyEnum.b } } }); } public static void ObjectInitializerWithInitializationOfDeeplyNestedObjects() { X(Y(), new Data { a = MyEnum.b, MoreData = { a = MyEnum.a, MoreData = { MoreData = { MoreData = { MoreData = { MoreData = { MoreData = { a = MyEnum.b } } } } } } } }); } public static void CollectionInitializerInsideObjectInitializers() { X(Y(), new Data { MoreData = new Data { a = MyEnum.a, b = MyEnum.b, PropertyList = { MyEnum2.c } } }); } public static void StructInitializer_DefaultConstructor() { X(Y(), new StructData { Field = 1, Property = 2 }); } public void InliningOfStFldTarget() { s1 = new S { A = 24, B = 42 }; s2 = new S { A = 42, B = 24 }; } public static void StructInitializer_ExplicitConstructor() { X(Y(), new StructData(0) { Field = 1, Property = 2 }); } public static void StructInitializerWithInitializationOfNestedObjects() { X(Y(), new StructData { MoreData = { a = MyEnum.a, FieldList = { MyEnum2.c, MyEnum2.d } } }); } public static void StructInitializerWithinObjectInitializer() { X(Y(), new Data { NestedStruct = new StructData(2) { Field = 1, Property = 2 } }); } public static void Issue270_NestedInitialisers() { NumberFormatInfo[] source = null; TestCall(0, new Thread(Issue270_NestedInitialisers) { Priority = ThreadPriority.BelowNormal, CurrentCulture = new CultureInfo(0) { DateTimeFormat = new DateTimeFormatInfo { ShortDatePattern = "ddmmyy" }, NumberFormat = source.Where((NumberFormatInfo format) => format.CurrencySymbol == "$").First() } }); } public OtherItem2 Issue1345() { OtherItem2 otherItem = new OtherItem2(); otherItem.Data.Nullable = 3m; return otherItem; } public OtherItem2 Issue1345b() { OtherItem2 otherItem = new OtherItem2(); otherItem.Data2.Nullable = 3m; return otherItem; } #if CS90 public Issue3392Type Issue3392(Issue3392Type x) { x = new Issue3392Type(null) { Flag = false }; x.List.AddRange(Enumerable.Range(0, 10)); return x; } #endif #if CS60 public OtherItem2 Issue1345c() { OtherItem2 otherItem = new OtherItem2(); otherItem.Data3.Nullable = 3m; return otherItem; } private Data Issue1345_FalsePositive() { return new Data { ReadOnlyPropertyList = { MyEnum2.c, MyEnum2.d } }; } #endif private void Issue1250_Test1(MyEnum value) { X(Y(), new C { Z = (int)value }); } private byte[] Issue1314() { return new byte[4] { 0, 1, 2, 255 }; } private void Issue1251_Test(List list, OtherItem otherItem) { list.Add(new Item { Text = "Text", Value = otherItem.Value, Value2 = otherItem.Value2, Value3 = otherItem.Nullable.ToString(), Value4 = otherItem.Nullable2.ToString(), Value5 = otherItem.Nullable3.ToString(), Value6 = otherItem.Nullable4.ToString() }); } private Data Issue1279(int p) { if (p == 1) { Data data = new Data(); data.a = MyEnum.a; data.TestEvent += Data_TestEvent; return data; } return null; } #if CS90 private Fields RecordWithNestedClass(Fields input) { return input with { A = 42, I = new Item { Value7 = input with { A = 43 } } }; } public DerivedFields DerivedRecordTest(DerivedFields input) { return input with { A = 42, D = 43, I = new Item { Value7 = input with { A = 44, D = 45 } } }; } #endif private TData GenericObjectInitializer() where TData : IData, new() { return new TData { Property = 42 }; } #endregion #region Collection initializer public static void ExtensionMethodInCollectionInitializer() { #if CS60 X(Y(), new CustomList { { "1", "2" } }); #else CustomList customList = new CustomList(); customList.Add("1", "2"); X(Y(), customList); #endif } public static void NoCollectionInitializerBecauseOfTypeArguments() { CustomList customList = new CustomList(); customList.Add("int"); Console.WriteLine(customList); } public static TestCases NoCollectionInitializerBecauseOfMissingIEnumerable() { TestCases testCases = new TestCases(); testCases.Add("int"); testCases.Add("string"); return testCases; } public static void CollectionInitializerWithParamsMethod() { X(Y(), new CustomList { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } }); } public static void CollectionInitializerList() { X(Y(), new List { 1, 2, 3 }); } public static object RecursiveCollectionInitializer() { List list = new List(); list.Add(list); return list; } public static void CollectionInitializerDictionary() { X(Y(), new Dictionary { { "First", 1 }, { "Second", 2 }, { "Third", 3 } }); } public static void CollectionInitializerDictionaryWithEnumTypes() { X(Y(), new Dictionary { { MyEnum.a, MyEnum2.c }, { MyEnum.b, MyEnum2.d } }); } public static void NotACollectionInitializer() { List list = new List(); list.Add(1); list.Add(2); list.Add(3); X(Y(), list); } #if CS60 public static void SimpleDictInitializer() { X(Y(), new Data { MoreData = { a = MyEnum.a, [2] = null } }); } public static void MixedObjectAndDictInitializer() { X(Y(), new Data { MoreData = { a = MyEnum.a, [GetInt()] = { a = MyEnum.b, FieldList = { MyEnum2.c }, [GetInt(), GetString()] = new Data(), [2] = null } } }); } private List> NestedListWithIndexInitializer(MyEnum myEnum) { return new List> { [0] = { 1, 2, 3 }, [1] = { (int)myEnum } }; } private void Issue1250_Test2(MyEnum value) { X(Y(), new C { [(int)value] = new S((int)value) }); } private void Issue1250_Test3(int value) { X(Y(), new C { [value] = new S(value) }); } private void Issue1250_Test4(int value) { X(Y(), new C { [(object)value] = new S(value) }); } public static List> Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) { return new List> { { "tokens", string.Join(wireDelimiter.ToString(), tokens), (Func)null }, { "alwaysAllowAdministrators", alwaysAllowAdministrators.ToString(), (Func)null }, { "delimiter", wireDelimiter.ToString(), (Func)null } }; } #endif #endregion } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs ================================================ using System; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class InlineArrayTests { [InlineArray(16)] public struct Byte16 { private byte elem; } [InlineArray(16)] public struct Generic16 { private T elem; } public byte Byte0() { return GetByte16()[0]; } public byte GenericByte0() { return GetGeneric()[0]; } public byte Byte5() { return GetByte16()[5]; } public byte GenericByte5() { return GetGeneric()[5]; } public byte ByteN() { return GetByte16()[GetIndex()]; } public byte GenericByteN() { return GetGeneric()[GetIndex()]; } public byte Byte0(Byte16 array, byte value) { return array[0] = value; } public byte GenericByte0(Generic16 array, byte value) { return array[0] = value; } public byte Byte5(Byte16 array, byte value) { return array[5] = value; } public byte GenericByte5(Generic16 array, byte value) { return array[5] = value; } public byte ByteN(Byte16 array, byte value) { return array[GetIndex()] = value; } public byte GenericByteN(Generic16 array, byte value) { return array[GetIndex()] = value; } public void Slice(Byte16 array) { Receiver(array[..8]); Receiver((ReadOnlySpan)array[..8]); ReceiverSpan(array[..8]); ReceiverReadOnlySpan(array[..8]); } // TODO //public void Slice(Byte16 array, int end) //{ // Receiver(array[..end]); // Receiver((ReadOnlySpan)array[..end]); // ReceiverSpan(array[..end]); // ReceiverReadOnlySpan(array[..end]); //} public byte VariableSplitting(Byte16 array, byte value) { return array[GetIndex()] = (array[GetIndex() + 1] = value); } public void OverloadResolution() { Receiver(GetByte16()); Receiver((object)GetByte16()); Byte16 buffer = GetByte16(); Receiver((Span)buffer); Byte16 buffer2 = GetByte16(); Receiver((ReadOnlySpan)buffer2); Byte16 buffer3 = GetByte16(); ReceiverSpan(buffer3); Byte16 buffer4 = GetByte16(); ReceiverReadOnlySpan(buffer4); } public Byte16 GetByte16() { return default(Byte16); } public Generic16 GetGeneric() { return default(Generic16); } public int GetIndex() { return 0; } public void Receiver(Span span) { } public void Receiver(ReadOnlySpan span) { } public void Receiver(Byte16 span) { } public void Receiver(object span) { } public void ReceiverSpan(Span span) { } public void ReceiverReadOnlySpan(ReadOnlySpan span) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs ================================================ // Copyright (c) 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 // 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.IO; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class InlineAssignmentTest { private int field1; private static InlineAssignmentTest field2; private int[] field3; private short field4; public int InstanceProperty { get; set; } public static int StaticProperty { get; set; } public bool BoolProperty { get; set; } public void SimpleInlineWithLocals() { int index; Console.WriteLine(GetFormat(), index = GetIndex()); Console.WriteLine(index); InlineAssignmentTest value; Console.WriteLine(GetFormat(), value = new InlineAssignmentTest()); Console.WriteLine(value); } public void SimpleInlineWithFields() { Console.WriteLine(field1 = 5); Console.WriteLine(field2 = new InlineAssignmentTest()); } public void SimpleInlineWithFields2() { Console.WriteLine(field1 = 5); Console.WriteLine(field1); Console.WriteLine(field2 = new InlineAssignmentTest()); Console.WriteLine(field2); UseShort(field4 = 6); UseShort(field4 = -10000); UseShort(field4 = (short)field1); UseShort(field4 = UseShort(0)); Console.WriteLine(field4); } public short UseShort(short s) { Console.WriteLine(s); return s; } public void ReadLoop1(TextReader r) { string value; while ((value = r.ReadLine()) != null) { Console.WriteLine(value); } } public void AccessArray(int[] a) { int num; Console.WriteLine(num = a[0]); Console.WriteLine(a[num] = num); } public int Return(ref int a) { return a = 3; } public int Array(int[] a, int i) { return a[i] = i; } public int Array2(int i) { return field3[i] = 1; } public int GetIndex() { return new Random().Next(0, 100); } public int[] GetArray() { throw new NotImplementedException(); } public string GetFormat() { return "{0}"; } public int GetValue(int value) { return value; } public int ArrayUsageWithMethods() { return GetArray()[GetIndex()] = GetValue(GetIndex()); } public int StaticPropertyTest() { return StaticProperty = GetIndex(); } public int InstancePropertyTest() { return InstanceProperty = GetIndex(); } public bool BoolPropertyTest(object x) { return BoolProperty = x != null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class InterfaceTests { public interface IA { #if CS80 && !NET40 static int Field; #endif int Property1 { get; } int Property2 { set; } int Property3 { get; set; } event EventHandler MyEvent; void Method(); #if CS80 && !NET40 static IA() { } void DefaultMethod() { Method(); PrivateMethod(); } private void PrivateMethod() { Method(); } internal void InternalMethod() { Method(); } sealed void SealedMethod() { Method(); } static void StaticMethod() { } #endif } public interface IA2 : IA { #if CS80 && !NET40 int IA.Property3 { get { return 0; } set { } } event EventHandler IA.MyEvent { add { } remove { } } new event EventHandler MyEvent { add { } remove { } } void IA.InternalMethod() { } new void Method() { } #endif } public interface IB { } public class C : IA2, IA, IB { int IA.Property1 { get { throw new NotImplementedException(); } } int IA.Property2 { set { throw new NotImplementedException(); } } int IA.Property3 { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } event EventHandler IA.MyEvent { add { } remove { } } public int Finalize() { return 0; } void IA.Method() { throw new NotImplementedException(); } } internal interface IInterfacesCannotDeclareDtors { int Finalize(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue1080.cs ================================================ using ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA; using ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.SpaceB; using ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceC; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080 { internal static class ExtensionsTest { private static void Dummy(ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.SpaceB.Type2 intf) { } private static void Test(object obj) { ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.Type2 type = obj as ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.Type2; if (type != null) { ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceC.Extensions.Extension(type); } } } } namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA { internal interface Type2 : ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.SpaceB.Type2, Type1 { } } namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceA.SpaceB { internal static class Extensions { public static void Extension(this Type1 obj) { } } internal interface Type1 { } internal interface Type2 : Type1 { } } namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1080.SpaceC { internal static class Extensions { public static void Extension(this Type1 obj) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3406.cs ================================================ internal class Issue3406 { private record struct S1(int Value); private record struct S2 { public int Value; public S2(int value) { Value = value; } public S2(int a, int b) { Value = a + b; } } private record struct S3 { public int Value; public S3(int value) { Value = value; } } // This also generates a hidden backing field private record struct S4(int value) { public int Value = value; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3439.cs ================================================ using System; using System.Collections.Generic; internal class VariableScopeTest { private class Item { public long Key; public string Value; } private void Test(List list1) { AddAction(delegate (List list2) { long num2 = 1L; foreach (string item in list1) { list2.Add(new Item { Key = num2, Value = item }); num2++; } }); int num = 1; foreach (string item2 in list1) { int preservedName = num; num++; AddAction(item2, delegate (object x) { SetValue(x, preservedName); }); } } private static void AddAction(Action> action) { } private static void AddAction(string name, Action action) { } private static void SetValue(object obj, int value) { } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3442.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3442 { public class Class : Interface { void Interface.M() { } } public interface Interface { void M() where T : Interface; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3452.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Issue3452 { private struct Data { public object Obj; } private class C1(object obj) { internal Data d = new Data { Obj = obj }; } private class C2(object obj) { public object Obj => obj; } private class C3(StringComparison comparison) { private StringComparison _comparison = comparison; internal StringComparison Test() { return _comparison; } } private struct S1(object obj) { internal Data d = new Data { Obj = obj }; } private struct S2(object obj) { public object Obj => obj; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3483.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class Issue3483 { public static int Add_Checked(int x, int y) { return x + y; } public static int Add_Unchecked(int x, int y) { return unchecked(x + y); } public static int Add_CheckedAndUnchecked_1(int x, int y, int z) { return x + unchecked(y + z); } public static int Add_CheckedAndUnchecked_2(int x, int y, int z) { unchecked { return x + checked(y + z); } } public static uint Cast_Checked(int x) { return (uint)x; } public static uint Cast_Unchecked(int x) { return unchecked((uint)x); } public static int Cast_CheckedAndUnchecked_1(int x) { return (int)unchecked((uint)x); } public static int Cast_CheckedAndUnchecked_2(int x) { unchecked { return (int)checked((uint)x); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3541.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Issue3541 { private void Test(string format) { TestLocal(); void TestLocal(int a = 0) { a.ToString(format); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_A.cs ================================================ using System; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class Issue3571_A { [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct fsResult { public static fsResult Success => default(fsResult); public static fsResult Failure => default(fsResult); public bool Succeeded => true; public bool Failed => false; public static fsResult operator +(fsResult a, fsResult b) { return default(fsResult); } } public static fsResult M() { fsResult success = fsResult.Success; fsResult fsResult2 = success + fsResult.Success; if (fsResult2.Succeeded) { return success; } Console.WriteLine("Failed"); return fsResult2 + fsResult.Failure; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_B.cs ================================================ using System; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_B { [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct fsResult { public static fsResult Success => default(fsResult); public static fsResult Failure => default(fsResult); public bool Succeeded => true; public bool Failed => false; public static fsResult operator +(fsResult a, fsResult b) { return default(fsResult); } } internal static class Issue3571_B { public static fsResult M() { fsResult success = fsResult.Success; fsResult fsResult2 = success + fsResult.Success; if (fsResult2.Succeeded) { return success; } Console.WriteLine("Failed"); return fsResult2 + fsResult.Failure; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_C.cs ================================================ using System; using System.Runtime.InteropServices; using ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_Helper; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class Issue3571_C { public static fsResult M() { fsResult success = fsResult.Success; fsResult fsResult2 = success + fsResult.Success; if (fsResult2.Succeeded) { return success; } Console.WriteLine("Failed"); return fsResult2 + fsResult.Failure; } } } namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_Helper { [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct fsResult { public static fsResult Success => default(fsResult); public static fsResult Failure => default(fsResult); public bool Succeeded => true; public bool Failed => false; public static fsResult operator +(fsResult a, fsResult b) { return default(fsResult); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3576.cs ================================================ using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class Issue3576 { public static Issue3576_Camera GetOrCreate(long key, int frameCount, Dictionary cache) { if (!cache.TryGetValue(key, out var value)) { Issue3576_GameObject issue3576_GameObject = new Issue3576_GameObject(); value = (issue3576_GameObject.AddComponent(), frameCount); value.Item1.Property = 1; issue3576_GameObject.SetActive(value: false); cache[key] = value; } else { value.Item2 = frameCount; cache[key] = value; } return value.Item1; } } internal sealed class Issue3576_Camera { public int Property { get; set; } } internal sealed class Issue3576_GameObject { public T AddComponent() { throw null; } public void SetActive(bool value) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3584.cs ================================================ using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal abstract class Issue4000 : IEnumerable, IEnumerable { public int Length; protected T[] results; #if !ROSLYN4 public T this[int i] { get { if (i >= Length || i < 0) { return default(T); } if (results[i] != null && results[i].Equals(default(T))) { results[i] = CreateIthElement(i); } return results[i]; } } #endif protected abstract T CreateIthElement(int i); public IEnumerator GetEnumerator() { for (int i = 0; i < Length; i++) { if (results[i] != null && results[i].Equals(default(T))) { results[i] = CreateIthElement(i); } yield return results[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3598.cs ================================================ using System; using System.Diagnostics; using System.Threading; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Playstation { #pragma warning disable CS0414, CS9113, CS9124 public record struct CopilotContextId { public Guid Id { get; } public CopilotContextId() { Id = Guid.NewGuid(); } public CopilotContextId(Guid id) { Id = id; } } public class CopilotContextId_Class(Guid id) { public Guid guid { get; } = id; public CopilotContextId_Class(Guid id, int value) : this(Guid.NewGuid()) { } public CopilotContextId_Class() : this(Guid.NewGuid(), 222) { } } public record CopilotContextId_RecordClass(Guid id) { public Guid guid { get; } = id; public CopilotContextId_RecordClass() : this(Guid.NewGuid()) { } } public record struct CopilotContextId_RecordStruct(Guid id) { public Guid guid { get; } = id; public CopilotContextId_RecordStruct() : this(Guid.NewGuid()) { } } public struct CopilotContextId_Struct { public Guid guid { get; } public CopilotContextId_Struct(Guid id) { guid = id; } public CopilotContextId_Struct() : this(Guid.NewGuid()) { } } public abstract record CopilotQueriedMention { public abstract ConsoleKey Type { get; } public string DisplayName { get; init; } public string FullName { get; init; } public object ProviderMoniker { get; init; } internal CopilotQueriedMention(object providerMoniker, string fullName, string displayName) { ProviderMoniker = providerMoniker; FullName = fullName; DisplayName = displayName; } } public record CopilotQueriedScopeMention : CopilotQueriedMention { public override ConsoleKey Type { get; } = ConsoleKey.Enter; public CopilotQueriedScopeMention(object providerMoniker, string fullName, string displayName) : base(providerMoniker, fullName, displayName) { } } public class DeserializationException(string response, Exception innerException) : Exception("Error occured while deserializing the response", innerException) { public string Response { get; } = response; } internal static class Ensure { public static T NotNull(T? value, string name) { if (value == null) { throw new ArgumentNullException(name); } return value; } public static string NotEmptyString(object? value, string name) { #if OPT string obj = (value as string) ?? value?.ToString(); if (obj == null) { throw new ArgumentNullException(name); } if (string.IsNullOrWhiteSpace(obj)) { throw new ArgumentException("Parameter cannot be an empty string", name); } return obj; #else string text = (value as string) ?? value?.ToString(); if (text == null) { throw new ArgumentNullException(name); } if (string.IsNullOrWhiteSpace(text)) { throw new ArgumentException("Parameter cannot be an empty string", name); } return text; #endif } } // always use primary constructor because it is indistinguishable public struct FromBinaryOperator(int dummy1, int dummy2) { public int Leet = dummy1 + dummy2; } public struct FromCall(int dummy1, int dummy2) { public int Leet = Math.Max(dummy1, dummy2); } public struct FromConvert(double dummy1, double dummy2) { public int Leet = (int)Math.Min(dummy1, dummy2); } public record NamedParameter(string name, object? value, bool encode = true) : Parameter(Ensure.NotEmptyString(name, "name"), value, encode); [DebuggerDisplay("{DebuggerDisplay()}")] public abstract record Parameter { public string? Name { get; } public object? Value { get; } public bool Encode { get; } protected virtual string ValueString => Value?.ToString() ?? "null"; protected Parameter(string? name, object? value, bool encode) { Name = name; Value = value; Encode = encode; } public sealed override string ToString() { #if OPT if (Value != null) { return Name + "=" + ValueString; } return Name ?? ""; #else return (Value == null) ? (Name ?? "") : (Name + "=" + ValueString); #endif } protected string DebuggerDisplay() { return GetType().Name.Replace("Parameter", "") + " " + ToString(); } } public class Person(string name, int age) { private readonly string _name = name; private readonly int _age = age; public string Email { get; init; } public Person(string name, int age, string email) : this(name, age) { if (string.IsNullOrEmpty(email)) { throw new ArgumentException("Email cannot be empty"); } Email = email; Console.WriteLine("Created person: " + name); } } public class PersonPrimary(string name, int age) { private readonly string _name = name; } public class PersonPrimary_CaptureParams(string name, int age) { public string GetDetails() { return $"{name}, {age}"; } } public class PersonRegular1 { private readonly string _name = "name"; private readonly int _age = 23; public PersonRegular1(string name, int age) { Thread.Sleep(1000); _age = name.Length; } } public class PersonRegular2 { private readonly string _name = "name" + Environment.GetEnvironmentVariable("Path"); private readonly int _age = Environment.GetEnvironmentVariable("Path")?.Length ?? (-1); private void Method() { Console.WriteLine("Hello"); } public PersonRegular2(string name, int age) { } } public record QueryParameter(string name, object? value, bool encode = true) : NamedParameter(name, value, encode); internal ref struct RefFields(ref int v) { public ref int Field0 = ref v; } internal struct StructWithDefaultCtor { private int X = 42; public StructWithDefaultCtor() { } } internal struct ValueFields(int v) { public int Field0 = v; } internal class WebPair1(string name) { public string Name { get; } = name; } internal class WebPair1Primary { public string Name { get; } public WebPair1Primary(string name) { Name = name; } } internal class WebPair2 { public string Name { get; } public WebPair2(string name, string? value, ref readonly object encode) { Name = name; } } internal class WebPair2Primary(string name, string? value, ref readonly object encode) { public string Name { get; } = name; } internal class WebPair3 { public string Name { get; } public string? Value { get; } private string? WebValue { get; } public WebPair3(string name, string? value, bool encode = false) { Name = name; Value = value; WebValue = (encode ? "111" : value); } } internal class WebPair3Primary(string name, string? value, bool encode = false) { public string Name { get; } = name; public string? Value { get; } = value; private string? WebValue { get; } = encode ? "111" : value; } internal class WebPair4 { public string Name { get; } public string? Value { get; } private string? WebValue { get; } private string? WebValue2 { get; } public WebPair4(string name, string? value, ref readonly object encode) { Name = name; Value = value; WebValue = ((encode == null) ? "111" : value); WebValue2 = encode.ToString(); } } internal class WebPair4Primary(string name, string? value, ref readonly object encode) { public string Name { get; } = name; public string? Value { get; } = value; private string? WebValue { get; } = (encode == null) ? "111" : value; private string? WebValue2 { get; } = encode.ToString(); } internal class WebPair5 { public string Name { get; } public WebPair5(string name, string? value) { Name = name; } } internal class WebPair5Primary(string name, string? value) { public string Name { get; } = name; } internal class WebPair6 { public string? Value { get; } public string Name { get; } private string? WebValue { get; } private string? WebValue2 { get; } public WebPair6(string name, string? value, ref readonly object encode) { Value = name; Name = value; WebValue = ((name != null) ? "111" : value); WebValue2 = ((value != null) ? name : "222"); } } internal class WebPair6Primary(string name, string? value, ref readonly object encode) { public string? Value { get; } = name; public string Name { get; } = value; private string? WebValue { get; } = (name != null) ? "111" : value; private string? WebValue2 { get; } = (value != null) ? name : "222"; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3610.cs ================================================ using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Issue3610 { private struct CtorDoubleAssignmentTest { public bool Value; public CtorDoubleAssignmentTest(string arg1, int arg2) { Value = false; Value = true; } } private struct CtorDoubleAssignmentTest2 { public bool Value; public CtorDoubleAssignmentTest2(string arg1, int arg2) { Value = true; Value = false; } } private class FieldInitTest { public bool Flag = true; public Func Action = (int a) => a; public string Value; public FieldInitTest(string value) { Value = value; } } private abstract class PCFieldInitTest(StringComparison value) { private StringComparison _value = value; public bool Func() { return _value == StringComparison.Ordinal; } } private class RecordTest { private interface IInterface { T[] Objects { get; } } protected record Record(T[] Objects) : IInterface { public Record(List objects) : this(objects.ToArray()) { } } } private abstract record RecordTest2(Guid[] Guids); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3611.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Issue3611 { private class C4(string value) { public object Obj { get; } = new object(); public string Value { get; } = value; } private class C5(C5.ValueArray array) { public struct ValueArray { private bool b; public bool[] ToArray() { return null; } } public bool[] Values = array.ToArray(); } private class BaseClass { protected BaseClass(int value) { } } private class C6(C6.Data2 data) : BaseClass(data.Value) { public struct Data2 { public int Value { get; set; } } public Data2 Data => data; } private struct S3(T v) { public T Value => v; } private interface I1 { int Number { get; } } // May be the same issue as S3 private struct S4(int number) : IComparable where T : I1 { public int CompareTo(T other) { return number.CompareTo(other.Number); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs ================================================ // Copyright (c) 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 // 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.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class T00_LiftedOperators { // C# uses 4 different patterns of IL for lifted operators: bool, other primitive types, decimal, other structs. // Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable. // Negation must not be pushed through such comparisons because it would change the semantics (except for equality/inequality). // A comparison used in a condition differs somewhat from a comparison used as a simple value. public static void BoolBasic(bool? a, bool? b) { if (a == b) { Console.WriteLine(); } if (a != b) { Console.WriteLine(); } } public static void BoolComplex(bool? a, Func x) { if (a == x()) { Console.WriteLine(); } if (a != x()) { Console.WriteLine(); } if (x() == a) { Console.WriteLine(); } if (x() != a) { Console.WriteLine(); } if (a ?? x()) { Console.WriteLine(); } } public static void BoolConst(bool? a) { if (a == true) { Console.WriteLine(); } if (a != true) { Console.WriteLine(); } if (a == false) { Console.WriteLine(); } if (a != false) { Console.WriteLine(); } if (a ?? true) { Console.WriteLine(); } #if !ROSLYN // Roslyn 3 (VS2019) started optimizing this to "a.GetValueOrDefault()" if (a ?? false) { Console.WriteLine(); } #endif } public static void BoolValueBasic(bool? a, bool? b) { Console.WriteLine(a == b); Console.WriteLine(a != b); Console.WriteLine(a & b); Console.WriteLine(a | b); Console.WriteLine(a ^ b); Console.WriteLine(a ?? b); Console.WriteLine(!a); a &= b; a |= b; a ^= b; } public static void BoolValueComplex(bool? a, Func x) { Console.WriteLine(a == x()); Console.WriteLine(a != x()); Console.WriteLine(x() == a); Console.WriteLine(x() != a); //Console.WriteLine(a & x()); // we currently can't tell the order //Console.WriteLine(a | x()); // of the operands in bool [&|] bool? Console.WriteLine(a ^ x()); Console.WriteLine(a ?? x()); //a &= x(); -- also affected by order of operand problem //a |= x(); a ^= x(); Console.WriteLine(x() & a); Console.WriteLine(x() | a); Console.WriteLine(x() ^ a); (new bool?[0])[0] ^= x(); (new bool?[0])[0] ^= a; } public static void BoolValueConst(bool? a) { Console.WriteLine(a == true); Console.WriteLine(a != true); Console.WriteLine(a == false); Console.WriteLine(a != false); Console.WriteLine(a ?? true); #if !ROSLYN // Roslyn 3 (VS2019) started optimizing this to "a.GetValueOrDefault()" Console.WriteLine(a ?? false); #endif } public static void IntBasic(int? a, int? b) { if (a == b) { Console.WriteLine(); } if (a != b) { Console.WriteLine(); } if (a > b) { Console.WriteLine(); } if (a < b) { Console.WriteLine(); } if (a >= b) { Console.WriteLine(); } if (a <= b) { Console.WriteLine(); } if (!(a > b)) { Console.WriteLine(); } if (!(a <= b)) { Console.WriteLine(); } } public static void IntComplex(int? a, Func x) { if (a == x()) { Console.WriteLine(); } if (a != x()) { Console.WriteLine(); } if (a > x()) { Console.WriteLine(); } if (x() == a) { Console.WriteLine(); } if (x() != a) { Console.WriteLine(); } if (x() > a) { Console.WriteLine(); } if (!(a > x())) { Console.WriteLine(); } if (!(a <= x())) { Console.WriteLine(); } } public static void IntConst(int? a) { if (a == 2) { Console.WriteLine(); } if (a != 2) { Console.WriteLine(); } if (a > 2) { Console.WriteLine(); } if (2 == a) { Console.WriteLine(); } if (2 != a) { Console.WriteLine(); } if (2 > a) { Console.WriteLine(); } } public static void IntValueBasic(int? a, int? b) { Console.WriteLine(a == b); Console.WriteLine(a != b); Console.WriteLine(a > b); Console.WriteLine(!(a > b)); Console.WriteLine(!(a >= b)); Console.WriteLine(a + b); Console.WriteLine(a - b); Console.WriteLine(a * b); Console.WriteLine(a / b); Console.WriteLine(a % b); Console.WriteLine(a & b); Console.WriteLine(a | b); Console.WriteLine(a ^ b); Console.WriteLine(a << b); Console.WriteLine(a >> b); Console.WriteLine(a ?? b); Console.WriteLine(-a); Console.WriteLine(~a); // TODO: //Console.WriteLine(a++); //Console.WriteLine(a--); Console.WriteLine(++a); Console.WriteLine(--a); a += b; a -= b; a *= b; a /= b; a %= b; a &= b; a |= b; a ^= b; a <<= b; a >>= b; } public static void IntValueComplex(int? a, Func x) { Console.WriteLine(a == x()); Console.WriteLine(a != x()); Console.WriteLine(a > x()); Console.WriteLine(x() == a); Console.WriteLine(x() != a); Console.WriteLine(x() > a); Console.WriteLine(a + x()); Console.WriteLine(a - x()); Console.WriteLine(a * x()); Console.WriteLine(a / x()); Console.WriteLine(a % x()); Console.WriteLine(a & x()); Console.WriteLine(a | x()); Console.WriteLine(a ^ x()); Console.WriteLine(a << x()); Console.WriteLine(a >> x()); Console.WriteLine(a ?? x()); a += x(); a -= x(); a *= x(); a /= x(); a %= x(); a &= x(); a |= x(); a ^= x(); a <<= x(); a >>= x(); Console.WriteLine(x() + a); (new int?[0])[0] += x(); } public static void IntValueConst(int? a) { Console.WriteLine(a == 2); Console.WriteLine(a != 2); Console.WriteLine(a > 2); Console.WriteLine(2 == a); Console.WriteLine(2 != a); Console.WriteLine(2 > a); Console.WriteLine(a + 2); Console.WriteLine(a - 2); Console.WriteLine(a * 2); Console.WriteLine(a / 2); Console.WriteLine(a % 2); Console.WriteLine(a & 2); Console.WriteLine(a | 2); Console.WriteLine(a ^ 2); Console.WriteLine(a << 2); Console.WriteLine(a >> 2); Console.WriteLine(a ?? 2); a += 2; a -= 2; a *= 2; a /= 2; a %= 2; a &= 2; a |= 2; a ^= 2; a <<= 2; a >>= 2; Console.WriteLine(2 + a); } public static void NumberBasic(decimal? a, decimal? b) { if (a == b) { Console.WriteLine(); } #if ROSLYN2 // Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b' if (!(a == b)) { Console.WriteLine(); } #else if (a != b) { Console.WriteLine(); } #endif if (a > b) { Console.WriteLine(); } if (a < b) { Console.WriteLine(); } if (a >= b) { Console.WriteLine(); } if (a <= b) { Console.WriteLine(); } if (!(a > b)) { Console.WriteLine(); } if (!(a < b)) { Console.WriteLine(); } } public static void NumberComplex(decimal? a, Func x) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //if (a == x()) { // Console.WriteLine(); //} //if (a != x()) { // Console.WriteLine(); //} //if (a > x()) { // Console.WriteLine(); //} //if (x() == a) { // Console.WriteLine(); //} //if (x() != a) { // Console.WriteLine(); //} //if (x() > a) { // Console.WriteLine(); //} } public static void NumberConst(decimal? a) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //if (a == 2m) { // Console.WriteLine(); //} //if (a != 2m) { // Console.WriteLine(); //} //if (a > 2m) { // Console.WriteLine(); //} //if (2m == a) { // Console.WriteLine(); //} //if (2m != a) { // Console.WriteLine(); //} //if (2m > a) { // Console.WriteLine(); //} } public static void NumberValueBasic(decimal? a, decimal? b) { Console.WriteLine(a == b); #if ROSLYN // Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b' Console.WriteLine(!(a == b)); #else Console.WriteLine(a != b); #endif Console.WriteLine(a > b); Console.WriteLine(!(a > b)); Console.WriteLine(!(a <= b)); Console.WriteLine(a + b); Console.WriteLine(a - b); Console.WriteLine(a * b); Console.WriteLine(a / b); Console.WriteLine(a % b); Console.WriteLine(a ?? b); Console.WriteLine(-a); // TODO: //Console.WriteLine(a++); //Console.WriteLine(a--); //Console.WriteLine(++a); //Console.WriteLine(--a); a += b; a -= b; a *= b; a /= b; a %= b; } public static void NumberValueComplex(decimal? a, Func x) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //Console.WriteLine(a == x()); //Console.WriteLine(a != x()); //Console.WriteLine(a > x()); //Console.WriteLine(x() == a); //Console.WriteLine(x() != a); //Console.WriteLine(x() > a); //Console.WriteLine(a + x()); //Console.WriteLine(a - x()); //Console.WriteLine(a * x()); //Console.WriteLine(a / x()); //Console.WriteLine(a % x()); //Console.WriteLine(a ?? x()); //a += x(); //a -= x(); //a *= x(); //a /= x(); //a %= x(); //Console.WriteLine(x() + a); //(new decimal?[0])[0] += x(); } public static void NumberValueConst(decimal? a) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //Console.WriteLine(a == 2m); //Console.WriteLine(a != 2m); //Console.WriteLine(a > 2m); //Console.WriteLine(2m == a); //Console.WriteLine(2m != a); //Console.WriteLine(2m > a); //Console.WriteLine(a + 2m); //Console.WriteLine(a - 2m); //Console.WriteLine(a * 2m); //Console.WriteLine(a / 2m); //Console.WriteLine(a % 2m); //Console.WriteLine(a ?? 2m); //a += 2m; //a -= 2m; //a *= 2m; //a /= 2m; //a %= 2m; //Console.WriteLine(2m + a); } public static void CompareWithImplictCast(int? a, long? b) { if (a < b) { Console.WriteLine(); } if (a == b) { Console.WriteLine(); } // TODO: unnecessary cast //if (a < 10L) { // Console.WriteLine(); //} //if (a == 10L) { // Console.WriteLine(); //} } public static void CompareWithSignChange(int? a, int? b) { if ((uint?)a < (uint?)b) { Console.WriteLine(); } // TODO: unnecessary cast //if ((uint?)a < 10) { // Console.WriteLine(); //} } public static void StructBasic(TS? a, TS? b) { if (a == b) { Console.WriteLine(); } if (a != b) { Console.WriteLine(); } if (a > b) { Console.WriteLine(); } if (a < b) { Console.WriteLine(); } if (a >= b) { Console.WriteLine(); } if (a <= b) { Console.WriteLine(); } if (!(a == b)) { Console.WriteLine(); } if (!(a != b)) { Console.WriteLine(); } if (!(a > b)) { Console.WriteLine(); } } public static void StructComplex(TS? a, Func x) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //if (a == x()) { // Console.WriteLine(); //} //if (a != x()) { // Console.WriteLine(); //} //if (a > x()) { // Console.WriteLine(); //} //if (x() == a) { // Console.WriteLine(); //} //if (x() != a) { // Console.WriteLine(); //} //if (x() > a) { // Console.WriteLine(); //} } public static void StructValueBasic(TS? a, TS? b, int? i) { Console.WriteLine(a == b); Console.WriteLine(a != b); Console.WriteLine(a > b); Console.WriteLine(!(a == b)); Console.WriteLine(!(a != b)); Console.WriteLine(!(a > b)); Console.WriteLine(a + b); Console.WriteLine(a - b); Console.WriteLine(a * b); Console.WriteLine(a / b); Console.WriteLine(a % b); Console.WriteLine(a & b); Console.WriteLine(a | b); Console.WriteLine(a ^ b); Console.WriteLine(a << i); Console.WriteLine(a >> i); Console.WriteLine(a ?? b); Console.WriteLine(+a); Console.WriteLine(-a); Console.WriteLine(!a); Console.WriteLine(~a); // TODO: //Console.WriteLine(a++); //Console.WriteLine(a--); //Console.WriteLine(++a); //Console.WriteLine(--a); //Console.WriteLine((int?)a); a += b; a -= b; a *= b; a /= b; a %= b; a &= b; a |= b; a ^= b; a <<= i; a >>= i; } public static void StructValueComplex(TS? a, Func x, Func i) { // Tests deactivated because we insert redundant casts; // TODO: revisit after decision has been made regarding the type system. //Console.WriteLine(a == x()); //Console.WriteLine(a != x()); //Console.WriteLine(a > x()); //Console.WriteLine(x() == a); //Console.WriteLine(x() != a); //Console.WriteLine(x() > a); //Console.WriteLine(a + x()); //Console.WriteLine(a - x()); //Console.WriteLine(a * x()); //Console.WriteLine(a / x()); //Console.WriteLine(a % x()); //Console.WriteLine(a & x()); //Console.WriteLine(a | x()); //Console.WriteLine(a ^ x()); //Console.WriteLine(a << i()); //Console.WriteLine(a >> i()); //Console.WriteLine(a ?? x()); //a += x(); //a -= x(); //a *= x(); //a /= x(); //a %= x(); //a &= x(); //a |= x(); //a ^= x(); //a <<= i(); //a >>= i(); //Console.WriteLine(x() + a); //(new TS?[0])[0] += x(); } public static bool RetEq(int? a, int? b) { return a == b; } public static bool RetEqConv(long? a, int? b) { return a == b; } public static bool RetEqConst(long? a) { return a == 10; } public static bool RetIneqConst(long? a) { return a != 10; } public static bool RetLt(int? a, int? b) { return a < b; } public static bool RetLtConst(int? a) { return a < 10; } public static bool RetLtConv(long? a, int? b) { return a < b; } public static bool RetNotLt(int? a, int? b) { return !(a < b); } } internal class T01_LiftedImplicitConversions { public int? ExtendI4(byte? b) { return b; } public int? ExtendToI4(sbyte? b) { return b; } public long? ExtendI8(byte? b) { return b; } public long? ExtendToI8(sbyte? b) { return b; } public long? ExtendI8(int? b) { return b; } public long? ExtendToI8(uint? b) { return b; } // TODO: unnecessary cast //public double? ToFloat(int? b) //{ // return b; //} //public long? InArithmetic(uint? b) //{ // return 100L + b; //} public long? AfterArithmetic(uint? b) { return 100 + b; } // TODO: unnecessary cast //public static double? InArithmetic2(float? nf, double? nd, float f) //{ // return nf + nd + f; //} public static long? InArithmetic3(int? a, long? b, int? c, long d) { return a + b + c + d; } } internal class T02_LiftedExplicitConversions { private static void Print(T? x) where T : struct { Console.WriteLine(x); } public static void UncheckedCasts(int? i4, long? i8, float? f) { Print((byte?)i4); Print((short?)i4); Print((uint?)i4); Print((uint?)i8); Print((uint?)f); } public static void CheckedCasts(int? i4, long? i8, float? f) { checked { Print((byte?)i4); Print((short?)i4); Print((uint?)i4); Print((uint?)i8); //Print((uint?)f); TODO } } } internal class T03_NullCoalescingTests { private static void Print(T x) { Console.WriteLine(x); } public static void Objects(object a, object b) { Print(a ?? b); } public static void Nullables(int? a, int? b) { Print(a ?? b); } public static void NullableWithNonNullableFallback(int? a, int b) { Print(a ?? b); } public static void NullableWithImplicitConversion(short? a, int? b) { Print(a ?? b); } public static void NullableWithImplicitConversionAndNonNullableFallback(short? a, int b) { // TODO: unnecessary cast //Print(a ?? b); } public static void Chain(int? a, int? b, int? c, int d) { Print(a ?? b ?? c ?? d); } public static void ChainWithImplicitConversions(int? a, short? b, long? c, byte d) { // TODO: unnecessary casts //Print(a ?? b ?? c ?? d); } public static void ChainWithComputation(int? a, short? b, long? c, byte d) { // TODO: unnecessary casts //Print((a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4)); } public static object ReturnObjects(object a, object b) { return a ?? b; } public static int? ReturnNullables(int? a, int? b) { return a ?? b; } public static int ReturnNullableWithNonNullableFallback(int? a, int b) { return a ?? b; } public static int ReturnChain(int? a, int? b, int? c, int d) { return a ?? b ?? c ?? d; } public static long ReturnChainWithImplicitConversions(int? a, short? b, long? c, byte d) { //TODO: unnecessary casts //return a ?? b ?? c ?? d; return 0L; } public static long ReturnChainWithComputation(int? a, short? b, long? c, byte d) { //TODO: unnecessary casts //return (a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4); return 0L; } } // dummy structure for testing custom operators [StructLayout(LayoutKind.Sequential, Size = 1)] public struct TS { // unary public static TS operator +(TS a) { throw null; } public static TS operator -(TS a) { throw null; } public static TS operator !(TS a) { throw null; } public static TS operator ~(TS a) { throw null; } public static TS operator ++(TS a) { throw null; } public static TS operator --(TS a) { throw null; } public static explicit operator int(TS a) { throw null; } // binary public static TS operator +(TS a, TS b) { throw null; } public static TS operator -(TS a, TS b) { throw null; } public static TS operator *(TS a, TS b) { throw null; } public static TS operator /(TS a, TS b) { throw null; } public static TS operator %(TS a, TS b) { throw null; } public static TS operator &(TS a, TS b) { throw null; } public static TS operator |(TS a, TS b) { throw null; } public static TS operator ^(TS a, TS b) { throw null; } public static TS operator <<(TS a, int b) { throw null; } public static TS operator >>(TS a, int b) { throw null; } // comparisons public static bool operator ==(TS a, TS b) { throw null; } public static bool operator !=(TS a, TS b) { throw null; } public static bool operator <(TS a, TS b) { throw null; } public static bool operator <=(TS a, TS b) { throw null; } public static bool operator >(TS a, TS b) { throw null; } public static bool operator >=(TS a, TS b) { throw null; } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs ================================================ // Copyright (c) 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 // 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; #if CS90 using System.Runtime.InteropServices; #endif namespace LocalFunctions { internal class LocalFunctions { [AttributeUsage(AttributeTargets.All)] internal class MyAttribute : Attribute { } public class Generic where T1 : struct, ICloneable, IConvertible { public int MixedLocalFunction() where T2 : ICloneable, IConvertible { #pragma warning disable CS0219 T2 t2 = default(T2); object z = this; for (int i = 0; i < 10; i++) { int i2 = 0; i2 += NonStaticMethod(0); #if CS90 [My] [return: My] int NonStaticMethod<[My] T3>([My] int unused) #else int NonStaticMethod(int unused) #endif { t2 = default(T2); int l = 0; return NonStaticMethod3() + NonStaticMethod3() + z.GetHashCode(); int NonStaticMethod3() { return i2 + l + NonStaticMethod(0) + StaticMethod(); } } } return MixedLocalFunction() + MixedLocalFunction() + StaticMethod() + StaticMethod() + NonStaticMethod2() + StaticMethod4(null) + StaticMethod5(); int NonStaticMethod2() { return GetHashCode(); } #if CS80 static int StaticMethod() where T3 : struct #else int StaticMethod() where T3 : struct #endif { return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticMethod() + StaticMethod2() + StaticMethod3(); } #if CS80 static int StaticMethod2() where T3 : struct where T4 : Enum #else int StaticMethod2() where T3 : struct where T4 : Enum #endif { return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + typeof(T4).Name.Length + StaticMethod() + StaticMethod2(); } #pragma warning disable CS8387 #if CS80 static int StaticMethod3() where T2 : IConvertible where T3 : struct where T4 : Enum #else int StaticMethod3() where T2 : IConvertible where T3 : struct where T4 : Enum #endif #pragma warning restore CS8387 { return typeof(T2).Name.Length; } #if CS80 static int StaticMethod4(T dd) #else int StaticMethod4(T dd) #endif { return 0; } #if CS80 static int StaticMethod5() #else int StaticMethod5() #endif { int k = 0; return k + NonStaticMethod4(); int NonStaticMethod4() { return k; } } #pragma warning restore CS0219 } public int MixedLocalFunction2Delegate() where T2 : ICloneable, IConvertible { T2 t2 = default(T2); object z = this; for (int i = 0; i < 10; i++) { int i2 = 0; i2 += StaticInvokeAsFunc(NonStaticMethod); int NonStaticMethod() { t2 = default(T2); int l = 0; return StaticInvokeAsFunc(NonStaticMethod3) + StaticInvokeAsFunc(NonStaticMethod3) + z.GetHashCode(); int NonStaticMethod3() { return i2 + l + StaticInvokeAsFunc(NonStaticMethod) + StaticInvokeAsFunc(StaticMethod); } } } Console.WriteLine(t2); return StaticInvokeAsFunc(MixedLocalFunction2Delegate) + StaticInvokeAsFunc(MixedLocalFunction2Delegate) + StaticInvokeAsFunc(StaticMethod) + StaticInvokeAsFunc(StaticMethod) + StaticInvokeAsFunc(NonStaticMethod2) + StaticInvokeAsFunc(StaticMethod4) + new Func(StaticMethod5)(null) + StaticInvokeAsFunc2(StaticMethod5) + new Func, int>(StaticInvokeAsFunc2)(StaticMethod5); int NonStaticMethod2() { return GetHashCode(); } #if CS80 static int StaticInvokeAsFunc(Func func) #else int StaticInvokeAsFunc(Func func) #endif { return func(); } #if CS80 static int StaticInvokeAsFunc2(Func func) #else int StaticInvokeAsFunc2(Func func) #endif { return func(default(T)); } #if CS80 static int StaticMethod() where T3 : struct #else int StaticMethod() where T3 : struct #endif { return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticInvokeAsFunc(StaticMethod) + StaticInvokeAsFunc(StaticMethod2) + StaticInvokeAsFunc(StaticMethod3); } #if CS80 static int StaticMethod2() where T3 : struct where T4 : Enum #else int StaticMethod2() where T3 : struct where T4 : Enum #endif { return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + typeof(T4).Name.Length + StaticInvokeAsFunc(StaticMethod) + StaticInvokeAsFunc(StaticMethod2); } #pragma warning disable CS8387 #if CS80 static int StaticMethod3() where T2 : IConvertible where T3 : struct where T4 : Enum #else int StaticMethod3() where T2 : IConvertible where T3 : struct where T4 : Enum #endif #pragma warning restore CS8387 { return typeof(T2).Name.Length; } #if CS80 static int StaticMethod4() #else int StaticMethod4() #endif { int k = 0; return k + StaticInvokeAsFunc(NonStaticMethod4); int NonStaticMethod4() { return k; } } #if CS80 static int StaticMethod5(T dd) #else int StaticMethod5(T dd) #endif { return 0; } } public static void Test_CaptureT() { #pragma warning disable CS0219 T2 t2 = default(T2); Method(); void Method() { t2 = default(T2); T2 t2x = t2; T3 t3 = default(T3); Method2(); void Method2() { t2 = default(T2); t2x = t2; t3 = default(T3); } } #pragma warning restore CS0219 } public void TestGenericArgs() where T2 : List { ZZ(null); ZZ3(null); #if CS80 static void Nop(T data) #else void Nop(T data) #endif { } #if CS80 static void ZZ(T3 t3) where T3 : T2 #else void ZZ(T3 t3) where T3 : T2 #endif { Nop>(t3); ZZ2(t3); ZZ4(); void ZZ4() { Nop>(t3); } } #if CS80 static void ZZ2(T3 t3) #else void ZZ2(T3 t3) #endif { Nop>((List)(object)t3); } #if CS80 static void ZZ3(T3 t3) #else void ZZ3(T3 t3) #endif { Nop>((List)(object)t3); } } #if false public void GenericArgsWithAnonymousType() { Method(); #if CS80 static void Method() #else void Method() #endif { int i = 0; var obj2 = new { A = 1 }; Method2(obj2); Method3(obj2); void Method2(T3 obj1) { //keep nested i = 0; } #if CS80 static void Method3(T3 obj1) #else void Method3(T3 obj1) #endif { } } } #if CS80 public void NameConflict() { int i = 0; Method(); void Method() { Method(); void Method() { Method(); i = 0; void Method() { i = 0; Method(); static void Method() { } } } } } #endif #endif } private int field; private Lazy nonCapturinglocalFunctionInLambda = new Lazy(delegate { return CreateValue(); #if CS80 static object CreateValue() #else object CreateValue() #endif { return null; } }); private Lazy capturinglocalFunctionInLambda = new Lazy(delegate { int x = 42; return Do(); object Do() { return CreateValue(); int CreateValue() { return x; } } }); private static void Test(int x) { } private static int GetInt(string a) { return a.Length; } private static string GetString(int a) { return a.ToString(); } public static void StaticContextNoCapture(int length) { for (int i = 0; i < length; i++) { LocalWrite("Hello " + i); } #if CS80 static void LocalWrite(string s) #else void LocalWrite(string s) #endif { Console.WriteLine(s); } } public static void StaticContextSimpleCapture(int length) { for (int i = 0; i < length; i++) { LocalWrite(); } void LocalWrite() { Console.WriteLine("Hello " + length); } } public static void StaticContextCaptureForLoopVariable(int length) { int i; for (i = 0; i < length; i++) { LocalWrite(); } void LocalWrite() { Console.WriteLine("Hello " + i + "/" + length); } } public void ContextNoCapture() { for (int i = 0; i < field; i++) { LocalWrite("Hello " + i); } #if CS80 static void LocalWrite(string s) #else void LocalWrite(string s) #endif { Console.WriteLine(s); } } public void ContextSimpleCapture() { for (int i = 0; i < field; i++) { LocalWrite(); } void LocalWrite() { Console.WriteLine("Hello " + field); } } public void ContextCaptureForLoopVariable() { int i; for (i = 0; i < field; i++) { LocalWrite(); } void LocalWrite() { Console.WriteLine("Hello " + i + "/" + field); } } public void CapturedOutsideLoop() { int i = 0; while (i < field) { i = GetInt("asdf"); LocalWrite(); } void LocalWrite() { Console.WriteLine("Hello " + i + "/" + field); } } public void CapturedInForeachLoop(IEnumerable args) { foreach (string arg2 in args) { string arg = arg2; LocalWrite(); void LocalWrite() { Console.WriteLine("Hello " + arg); } } } public void Overloading() { Test(5); LocalFunctions.Test(2); #if CS80 static void Test(int x) #else void Test(int x) #endif { Console.WriteLine("x: {0}", x); } } private void Name() { } private void LocalFunctionHidingMethod() { Action action = this.Name; Name(); action(); #if CS80 static void Name() #else void Name() #endif { } } public void NamedArgument() { Use(Get(1), Get(2), Get(3)); Use(Get(1), c: Get(2), b: Get(3)); #if CS80 static int Get(int i) #else int Get(int i) #endif { return i; } #if CS80 static void Use(int a, int b, int c) #else void Use(int a, int b, int c) #endif { Console.WriteLine(a + b + c); } } public static Func LambdaInLocalFunction() { int x = (int)Math.Pow(2.0, 10.0); return Create(); Func Create() { return () => x; } } public static Func MethodRef() { int x = (int)Math.Pow(2.0, 10.0); Enumerable.Range(1, 100).Select(LocalFunction); return null; int LocalFunction(int y) { return x * y; } } public static int Fib(int i) { return FibHelper(i); #if CS80 static int FibHelper(int n) #else int FibHelper(int n) #endif { if (n <= 0) { return 0; } return FibHelper(n - 1) + FibHelper(n - 2); } } public int MutuallyRecursiveLocalFunctions() { return B(4) + C(3); #if CS80 static int A(int i) #else int A(int i) #endif { if (i > 0) { return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1); } return 1; } #if CS80 static int B(int i) #else int B(int i) #endif { if (i > 0) { return 3 * A(i - 1) + B(i - 1); } return 1; } #if CS80 static int C(int i) #else int C(int i) #endif { if (i > 0) { return 2 * A(i - 1) + C(i - 1); } return 1; } } public static int NestedLocalFunctions(int i) { return A(); int A() { double x = Math.Pow(10.0, 2.0); return B(); int B() { return i + (int)x; } } } public static int LocalFunctionInLambda(IEnumerable xs) { return xs.First(delegate (int x) { return Do(); bool Do() { return x == 3; } }); } public static IEnumerable YieldReturn(int n) { return GetNumbers(); IEnumerable GetNumbers() { for (int i = 0; i < n; i++) { yield return i; } } } public void WriteCapturedParameter(int i) { ParamWrite(); Console.WriteLine(i); void ParamWrite() { i++; } } //public static void LocalFunctionInUsing() //{ // using (MemoryStream memoryStream = new MemoryStream()) { // Do(); // void Do() // { // memoryStream.WriteByte(42); // } // } //} public void NestedCapture1() { Method(null); #if CS80 static Action Method(Action action) #else Action Method(Action action) #endif { return Method2; void Method2(object containerBuilder) { Method3(containerBuilder); } void Method3(object containerBuilder) { action(containerBuilder); } } } public int NestedCapture2() { return Method(); #if CS80 static int Method() #else int Method() #endif { int t0 = 0; return ZZZ(); int ZZZ() { t0 = 0; int t2 = t0; return new Func(ZZZ3)(); int ZZZ3() { t0 = 0; t2 = 0; return ZZZ2(); } } int ZZZ2() { t0 = 0; int t3 = t0; return new Func(ZZZ4)(); int ZZZ4() { t0 = 0; t3 = 0; return 0; } } } } public int Issue1798_NestedCapture2() { return Method(); #if CS80 static int Method() #else int Method() #endif { int t0 = 0; return ZZZ(); int ZZZ() { t0 = 0; int t2 = t0; return ((Func)delegate { t0 = 0; t2 = 0; return ZZZ2(); })(); } int ZZZ2() { t0 = 0; int t3 = t0; #if !OPT Func func = delegate { #else return ((Func)delegate { #endif t0 = 0; t3 = 0; return 0; #if !OPT }; return func(); #else })(); #endif } } } public int Issue1798_NestedCapture2b() { return Method(); #if CS80 static int Method() #else int Method() #endif { int t0 = 0; return ZZZ() + ZZZ2(); int ZZZ() { t0 = 0; int t2 = t0; return ((Func)delegate { t0 = 0; t2 = 0; return ZZZ2(); })(); } int ZZZ2() { t0 = 0; int t3 = t0; #if !OPT Func func = delegate { #else return ((Func)delegate { #endif t0 = 0; t3 = 0; return 0; #if !OPT }; return func(); #else })(); #endif } } } #if CS90 public void Issue2196() { EnumWindows(0L, 0L); [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "EnumWindows")] static extern int EnumWindows(long hWnd, long lParam); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Lock { public void LockThis() { lock (this) { Console.WriteLine(); } } public void LockOnType() { lock (typeof(Lock)) { Console.WriteLine(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs ================================================ // Copyright (c) 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 // 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; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Loops { #region foreach public class CustomClassEnumerator { public object Current { get { throw new NotImplementedException(); } } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomClassEnumerator GetEnumerator() { return this; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct CustomStructEnumerator { public object Current { get { throw new NotImplementedException(); } } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomStructEnumerator GetEnumerator() { return this; } } public class CustomClassEnumerator { public T Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomClassEnumerator GetEnumerator() { return this; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct CustomStructEnumerator { public T Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomStructEnumerator GetEnumerator() { return this; } } public class CustomClassEnumeratorWithIDisposable : IDisposable { public object Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomClassEnumeratorWithIDisposable GetEnumerator() { return this; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct CustomStructEnumeratorWithIDisposable : IDisposable { public object Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomStructEnumeratorWithIDisposable GetEnumerator() { return this; } } public class CustomClassEnumeratorWithIDisposable : IDisposable { public T Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomClassEnumeratorWithIDisposable GetEnumerator() { return this; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct CustomStructEnumeratorWithIDisposable : IDisposable { public T Current { get { throw new NotImplementedException(); } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } public CustomStructEnumeratorWithIDisposable GetEnumerator() { return this; } } #if MCS [StructLayout(LayoutKind.Sequential, Size = 1)] #endif public struct DataItem { public int Property { get; set; } public void TestCall() { } } public class Item { } public class NonEnumerableArrayLike { private readonly int length; public Item this[int index] { get { return null; } } public int Length { get { return length; } } } private IEnumerable alternatives; private object someObject; private void TryGetItem(int id, out Item item) { item = null; } private static void Operation(ref int i) { } private static void Operation(Func f) { } public void ForEachOnField() { foreach (string alternative in alternatives) { alternative.ToLower(); } } public void ForEach(IEnumerable alternatives) { foreach (string alternative in alternatives) { alternative.ToLower(); } } public void ForEachOverList(List list) { // List has a struct as enumerator, so produces quite different IL than foreach over the IEnumerable interface foreach (string item in list) { item.ToLower(); } } public void ForEachOverNonGenericEnumerable(IEnumerable enumerable) { foreach (object item in enumerable) { item.ToString(); } } public void ForEachOverNonGenericEnumerableWithAutomaticCastValueType(IEnumerable enumerable) { foreach (int item in enumerable) { item.ToString(); } } public void ForEachOverNonGenericEnumerableWithAutomaticCastRefType(IEnumerable enumerable) { foreach (string item in enumerable) { Console.WriteLine(item); } } public void ForEachOnCustomClassEnumerator(CustomClassEnumerator e) { foreach (object item in e) { Console.WriteLine(item); } } // TODO : Needs additional pattern detection // CustomStructEnumerator does not implement IDisposable // No try-finally-Dispose is generated. //public void ForEachOnCustomStructEnumerator(CustomStructEnumerator e) //{ // foreach (object item in e) { // Console.WriteLine(item); // } //} public void ForEachOnGenericCustomClassEnumerator(CustomClassEnumerator e) { foreach (T item in e) { Console.WriteLine(item); } } // TODO : Needs additional pattern detection // CustomStructEnumerator does not implement IDisposable // No try-finally-Dispose is generated. //public void ForEachOnGenericCustomStructEnumerator(CustomStructEnumerator e) //{ // foreach (T item in e) { // Console.WriteLine(item); // } //} public void ForEachOnCustomClassEnumeratorWithIDisposable(CustomClassEnumeratorWithIDisposable e) { foreach (object item in e) { Console.WriteLine(item); } } public void ForEachOnCustomStructEnumeratorWithIDisposable(CustomStructEnumeratorWithIDisposable e) { foreach (object item in e) { Console.WriteLine(item); } } public void ForEachOnGenericCustomClassEnumeratorWithIDisposable(CustomClassEnumeratorWithIDisposable e) { foreach (T item in e) { Console.WriteLine(item); } } public void ForEachOnGenericCustomStructEnumeratorWithIDisposable(CustomStructEnumeratorWithIDisposable e) { foreach (T item in e) { Console.WriteLine(item); } } public static void NonGenericForeachWithReturnFallbackTest(IEnumerable e) { Console.WriteLine("NonGenericForeachWithReturnFallback:"); IEnumerator enumerator = e.GetEnumerator(); try { Console.WriteLine("MoveNext"); if (enumerator.MoveNext()) { object current = enumerator.Current; Console.WriteLine("please don't inline 'current'"); Console.WriteLine(current); } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } Console.WriteLine("After finally!"); } public static void ForeachWithRefUsage(List items) { foreach (int item in items) { #if ROSLYN && OPT // The variable name differs based on whether roslyn optimizes out the 'item' variable int i = item; Operation(ref i); #else int i = item; Operation(ref i); #endif } } public static void ForeachWithCapturedVariable(List items) { foreach (int item in items) { int c = item; Operation(() => c == 5); } } public static T LastOrDefault(IEnumerable items) { T result = default(T); foreach (T item in items) { result = item; } return result; } public void ForEachOverArray(string[] array) { foreach (string text in array) { Console.WriteLine(text.ToLower() + text.ToUpper()); } } public void ForOverNonArray(NonEnumerableArrayLike array) { for (int i = 0; i < array.Length; i++) { Item item = array[i]; Console.WriteLine(item.ToString() + item.ToString()); } } public unsafe void ForEachOverArrayOfPointers(int*[] array) { foreach (int* value in array) { Console.WriteLine(new IntPtr(value)); Console.WriteLine(new IntPtr(value)); } } public void ForEachBreakWhenFound(string name, ref StringComparison output) { #if MCS2 foreach (int value in Enum.GetValues(typeof(StringComparison))) { if (((StringComparison)value).ToString() == name) { output = (StringComparison)value; break; } } #elif MCS5 foreach (int value in Enum.GetValues(typeof(StringComparison))) { StringComparison stringComparison = (StringComparison)value; if (stringComparison.ToString() == name) { output = (StringComparison)value; break; } } #else foreach (StringComparison value in Enum.GetValues(typeof(StringComparison))) { if (value.ToString() == name) { output = value; break; } } #endif } public void ForEachOverListOfStruct(List items, int value) { foreach (DataItem item in items) { #if ROSLYN && OPT // The variable name differs based on whether roslyn optimizes out the 'item' variable DataItem current = item; current.Property = value; #else DataItem dataItem = item; dataItem.Property = value; #endif } } public void ForEachOverListOfStruct2(List items, int value) { foreach (DataItem item in items) { #if ROSLYN && OPT // The variable name differs based on whether roslyn optimizes out the 'item' variable DataItem current = item; current.TestCall(); current.Property = value; #else DataItem dataItem = item; dataItem.TestCall(); dataItem.Property = value; #endif } } public void ForEachOverListOfStruct3(List items, int value) { foreach (DataItem item in items) { item.TestCall(); } } #if !MCS public void ForEachOverMultiDimArray(int[,] items) { foreach (int value in items) { Console.WriteLine(value); Console.WriteLine(value); } } public void ForEachOverMultiDimArray2(int[,,] items) { foreach (int value in items) { Console.WriteLine(value); Console.WriteLine(value); } } public unsafe void ForEachOverMultiDimArray3(int*[,] items) { #if ROSLYN && OPT foreach (int* intPtr in items) { Console.WriteLine(*intPtr); Console.WriteLine(*intPtr); } #else foreach (int* ptr in items) { Console.WriteLine(*ptr); Console.WriteLine(*ptr); } #endif } #endif #endregion public void ForOverArray(string[] array) { for (int i = 0; i < array.Length; i++) { array[i].ToLower(); } } private static void AppendNamePart(string part, StringBuilder name) { foreach (char c in part) { if (c == '\\') { name.Append('\\'); } name.Append(c); } } public void NoForeachOverArray(string[] array) { for (int i = 0; i < array.Length; i++) { string value = array[i]; if (i % 5 == 0) { Console.WriteLine(value); } } } public void NestedLoops() { for (int i = 0; i < 10; i++) { if (i % 2 == 0) { for (int j = 0; j < 5; j++) { Console.WriteLine("Y"); } } else { Console.WriteLine("X"); } } } public int MultipleExits() { int num = 0; while (true) { if (num % 4 == 0) { return 4; } if (num % 7 == 0) { break; } if (num % 9 == 0) { return 5; } if (num % 11 == 0) { break; } num++; } return int.MinValue; } //public int InterestingLoop() //{ // int num = 0; // if (num % 11 == 0) { // while (true) { // if (num % 4 == 0) { // if (num % 7 == 0) { // if (num % 11 == 0) { // // use a continue here to prevent moving the if (i%7) outside the loop // continue; // } // Console.WriteLine("7"); // } else { // // this block is not part of the natural loop // Console.WriteLine("!7"); // } // break; // } // num++; // } // // This instruction is still dominated by the loop header // num = int.MinValue; // } // return num; //} public int InterestingLoop() { int num = 0; if (num % 11 == 0) { while (true) { if (num % 4 == 0) { if (num % 7 != 0) { Console.WriteLine("!7"); break; } if (num % 11 != 0) { Console.WriteLine("7"); break; } } else { num++; } } num = int.MinValue; } return num; } private bool Condition(string arg) { Console.WriteLine("Condition: " + arg); return false; } public void WhileLoop() { Console.WriteLine("Initial"); if (Condition("if")) { while (Condition("while")) { Console.WriteLine("Loop Body"); if (Condition("test")) { if (Condition("continue")) { continue; } if (!Condition("break")) { break; } } Console.WriteLine("End of loop body"); } Console.WriteLine("After loop"); } Console.WriteLine("End of method"); } //other configurations work fine, just with different labels #if OPT && !MCS public void WhileWithGoto() { while (Condition("Main Loop")) { if (Condition("Condition")) { goto IL_000f; } // TODO reorder branches with successive block? goto IL_0026; IL_000f: Console.WriteLine("Block1"); if (Condition("Condition2")) { continue; } // TODO remove redundant goto? goto IL_0026; IL_0026: Console.WriteLine("Block2"); goto IL_000f; } } #endif public void DoWhileLoop() { Console.WriteLine("Initial"); if (Condition("if")) { do { Console.WriteLine("Loop Body"); if (Condition("test")) { if (Condition("continue")) { continue; } if (!Condition("break")) { break; } } Console.WriteLine("End of loop body"); } while (Condition("while")); Console.WriteLine("After loop"); } Console.WriteLine("End of method"); } public void Issue1395(int count) { Environment.GetCommandLineArgs(); for (int i = 0; i < count; i++) { Environment.GetCommandLineArgs(); do { #if OPT || MCS IL_0013: #else IL_0016: #endif Environment.GetCommandLineArgs(); if (Condition("part1")) { Environment.GetEnvironmentVariables(); if (Condition("restart")) { #if OPT || MCS goto IL_0013; #else goto IL_0016; #endif } } else { Environment.GetLogicalDrives(); } Environment.GetCommandLineArgs(); while (count > 0) { switch (count) { case 0: case 1: case 2: Environment.GetCommandLineArgs(); break; case 3: case 5: case 6: Environment.GetEnvironmentVariables(); break; default: Environment.GetLogicalDrives(); break; } } count++; } while (Condition("do-while")); Environment.GetCommandLineArgs(); } Environment.GetCommandLineArgs(); } public void ForLoop() { Console.WriteLine("Initial"); if (Condition("if")) { for (int i = 0; Condition("for"); i++) { Console.WriteLine("Loop Body"); if (Condition("test")) { if (Condition("continue")) { continue; } if (!Condition("not-break")) { break; } } Console.WriteLine("End of loop body"); } Console.WriteLine("After loop"); } Console.WriteLine("End of method"); } public void ReturnFromDoWhileInTryFinally() { try { do { if (Condition("return")) { return; } } while (Condition("repeat")); Environment.GetCommandLineArgs(); } finally { Environment.GetCommandLineArgs(); } Environment.GetCommandLineArgs(); } public void ForLoopWithEarlyReturn(int[] ids) { for (int i = 0; i < ids.Length; i++) { Item item = null; TryGetItem(ids[i], out item); if (item == null) { break; } } } public void ForeachLoopWithEarlyReturn(List items) { foreach (object item in items) { if ((someObject = item) == null) { break; } } } public void NestedForeach(List items1, List items2) { foreach (object item in items1) { bool flag = false; foreach (object item2 in items2) { if (item2 == item) { flag = true; break; } } if (!flag) { Console.WriteLine(item); } } Console.WriteLine("end"); } public void MergeAroundContinue() { for (int i = 0; i < 20; i++) { if (i % 3 == 0) { if (i != 6) { continue; } } else if (i % 5 == 0) { if (i != 5) { continue; } } else if (i % 7 == 0) { if (i != 7) { continue; } } else if (i % 11 == 0) { continue; } Console.WriteLine(i); } Console.WriteLine("end"); } public void ForEachInSwitch(int i, IEnumerable args) { switch (i) { case 1: Console.WriteLine("one"); break; case 2: { foreach (string arg in args) { Console.WriteLine(arg); } break; } default: throw new NotImplementedException(); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/MemberTests.cs ================================================ // Copyright (c) 2008 Daniel Grunwald // // 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.Reflection; using System.Runtime.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class MemberTests { public class IndexerNonDefaultName { [IndexerName("Foo")] #if ROSLYN public int this[int index] => 0; #else #pragma warning disable format public int this[int index] { get { return 0; } } #pragma warning restore format #endif } [DefaultMember("Bar")] public class NoDefaultMember { } public const int IntConstant = 1; public const decimal DecimalConstant = 2m; private volatile int volatileField = 3; private static volatile int staticVolatileField = 4; public extern int ExternGetOnly { get; } public extern int ExternSetOnly { set; } public extern int ExternProperty { get; set; } public extern event EventHandler Event; public void UseVolatileFields() { Console.WriteLine(volatileField + staticVolatileField); volatileField++; staticVolatileField++; } public extern void ExternMethod(); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/MetadataAttributes.cs ================================================ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class MetadataAttributes { private class MethodImplAttr { [MethodImpl(MethodImplOptions.AggressiveInlining)] public extern void A(); #if !NET40 && ROSLYN3 [MethodImpl(MethodImplOptions.AggressiveOptimization)] public extern void B(); #endif [MethodImpl(MethodImplOptions.ForwardRef)] public extern void D(); [MethodImpl(MethodImplOptions.InternalCall)] public extern void E(); [MethodImpl(MethodImplOptions.NoInlining)] public extern void F(); [MethodImpl(MethodImplOptions.NoOptimization)] public extern void G(); [PreserveSig] public extern void H(); [MethodImpl(MethodImplOptions.Synchronized)] public extern void I(); [MethodImpl(MethodImplOptions.Unmanaged)] public extern void J(); [MethodImpl(MethodImplOptions.AggressiveInlining, MethodCodeType = MethodCodeType.Native)] public extern void A1(); #if !NET40 && ROSLYN3 [MethodImpl(MethodImplOptions.AggressiveOptimization, MethodCodeType = MethodCodeType.Native)] public extern void B1(); #endif [MethodImpl(MethodImplOptions.ForwardRef, MethodCodeType = MethodCodeType.Native)] public extern void D1(); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Native)] public extern void E1(); [MethodImpl(MethodImplOptions.NoInlining, MethodCodeType = MethodCodeType.Native)] public extern void F1(); [MethodImpl(MethodImplOptions.NoOptimization, MethodCodeType = MethodCodeType.Native)] public extern void G1(); [MethodImpl(MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Native)] public extern void H1(); [MethodImpl(MethodImplOptions.Synchronized, MethodCodeType = MethodCodeType.Native)] public extern void I1(); [MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType = MethodCodeType.Native)] public extern void J1(); [MethodImpl(MethodImplOptions.AggressiveInlining, MethodCodeType = MethodCodeType.OPTIL)] public extern void A2(); #if !NET40 && ROSLYN3 [MethodImpl(MethodImplOptions.AggressiveOptimization, MethodCodeType = MethodCodeType.OPTIL)] public extern void B2(); #endif [MethodImpl(MethodImplOptions.ForwardRef, MethodCodeType = MethodCodeType.OPTIL)] public extern void D2(); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.OPTIL)] public extern void E2(); [MethodImpl(MethodImplOptions.NoInlining, MethodCodeType = MethodCodeType.OPTIL)] public extern void F2(); [MethodImpl(MethodImplOptions.NoOptimization, MethodCodeType = MethodCodeType.OPTIL)] public extern void G2(); [MethodImpl(MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.OPTIL)] public extern void H2(); [MethodImpl(MethodImplOptions.Synchronized, MethodCodeType = MethodCodeType.OPTIL)] public extern void I2(); [MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType = MethodCodeType.OPTIL)] public extern void J2(); [MethodImpl(MethodImplOptions.AggressiveInlining, MethodCodeType = MethodCodeType.OPTIL)] public extern void A3(); #if !NET40 && ROSLYN3 [MethodImpl(MethodImplOptions.AggressiveOptimization, MethodCodeType = MethodCodeType.Runtime)] public extern void B3(); #endif [MethodImpl(MethodImplOptions.ForwardRef, MethodCodeType = MethodCodeType.Runtime)] public extern void D3(); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] public extern void E3(); [MethodImpl(MethodImplOptions.NoInlining, MethodCodeType = MethodCodeType.Runtime)] public extern void F3(); [MethodImpl(MethodImplOptions.NoOptimization, MethodCodeType = MethodCodeType.Runtime)] public extern void G3(); [MethodImpl(MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] public extern void H3(); [MethodImpl(MethodImplOptions.Synchronized, MethodCodeType = MethodCodeType.Runtime)] public extern void I3(); [MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType = MethodCodeType.Runtime)] public extern void J3(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/MultidimensionalArray.cs ================================================ // Copyright (c) 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 // 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. namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class MultidimensionalArray { internal class Generic where T : new() { private T[,] a = new T[20, 20]; private S[,][] b = new S[20, 20][]; public T this[int i, int j] { get { return a[i, j]; } set { a[i, j] = value; } } public void TestB(S x, ref S y) { b[5, 3] = new S[10]; b[5, 3][0] = default(S); b[5, 3][1] = x; b[5, 3][2] = y; } public void PassByReference(ref T arr) { PassByReference(ref a[10, 10]); } } public int[][,] MakeArray() { return new int[10][,]; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class NamedArguments { private class ClassWithNamedArgCtor { internal ClassWithNamedArgCtor(bool arg1 = false, bool arg2 = false) { } internal ClassWithNamedArgCtor() : this(arg2: Get(1) != 1, arg1: Get(2) == 2) { } } private class MustNotUseNamedArgsInCtor { public MustNotUseNamedArgsInCtor(string start = "", bool enable = false) { } public MustNotUseNamedArgsInCtor(bool enable, string start = "") { } public static MustNotUseNamedArgsInCtor Use() { // second overload MustNotUseNamedArgsInCall(true); // first overload MustNotUseNamedArgsInCall(); return new MustNotUseNamedArgsInCtor(true); } public static void MustNotUseNamedArgsInCall(string start = "", bool enable = false) { } public static void MustNotUseNamedArgsInCall(bool enable, string start = "") { } } public void Use(int a, int b, int c) { } public static int Get(int i) { return i; } public void Test() { Use(Get(1), Get(2), Get(3)); Use(Get(1), c: Get(2), b: Get(3)); Use(b: Get(1), a: Get(2), c: Get(3)); } public void NotNamedArgs() { int b = Get(1); Use(Get(2), b, Get(3)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class NativeInts { private const nint nint_const = 42; private const nuint nuint_const = 99u; #if CS110 && NET70 // C#11 on .NET7 no longer uses NativeIntegerAttribute, // instead nint+IntPtr are considered to be the same type. private nint intptr; private nuint uintptr; #else private IntPtr intptr; private UIntPtr uintptr; #endif private nint i; private nuint u; private int i32; private uint u32; private long i64; private ulong u64; #if !(CS110 && NET70) private (IntPtr, nint, UIntPtr, nuint) tuple_field; private (object, int, IntPtr, nint, UIntPtr, nuint) tuple_field2; private Dictionary dict1; private Dictionary dict2; private Dictionary dict3; private Dictionary dict4; #endif private Dictionary dict5; public void Convert() { i = (nint)u; u = (nuint)i; #if !(CS110 && NET70) intptr = i; intptr = (nint)u; intptr = (nint)(nuint)uintptr; uintptr = (nuint)i; uintptr = u; uintptr = (nuint)(nint)intptr; i = intptr; i = (nint)u; i = (nint)(nuint)uintptr; u = (nuint)i; u = uintptr; u = (nuint)(nint)intptr; #endif } public void Convert2() { i32 = (int)i; i = i32; #if !(CS110 && NET70) intptr = (IntPtr)i32; i64 = (long)intptr; #endif i64 = i; i = (nint)i64; u32 = (uint)i; i = (nint)u32; u64 = (uint)i; i = (nint)u64; } public void Arithmetic() { #if !(CS110 && NET70) Console.WriteLine((nint)intptr * 2); #endif Console.WriteLine(i * 2); Console.WriteLine(i + (nint)u); Console.WriteLine((nuint)i + u); } public void Shifts() { Console.WriteLine(i << i32); Console.WriteLine(i >> i32); Console.WriteLine(u >> i32); Console.WriteLine(u << i32); } public void Comparisons() { Console.WriteLine(i < i32); Console.WriteLine(i <= i32); Console.WriteLine(i > i32); Console.WriteLine(i >= i32); Console.WriteLine(i == (nint)u); Console.WriteLine(i < (nint)u); Console.WriteLine((nuint)i < u); } public void Unary() { Console.WriteLine(~i); Console.WriteLine(~u); Console.WriteLine(-i); } public unsafe int* PtrArithmetic(int* ptr) { return ptr + i; } public unsafe nint* PtrArithmetic(nint* ptr) { return ptr + u; } public object[] Boxing() { return new object[10] { 1, (nint)2, 3L, 4u, (nuint)5u, 6uL, int.MaxValue, (nint)int.MaxValue, i64, (nint)i64 }; } public NativeInts GetInstance(int i) { return this; } public void CompoundAssign() { GetInstance(0).i += i32; checked { GetInstance(1).i += i32; } GetInstance(2).u *= 2u; checked { GetInstance(3).u *= 2u; } #if !(CS110 && NET70) GetInstance(4).intptr += (nint)i32; checked { // Note: the cast is necessary here, without it we'd call IntPtr.op_Addition // but that is always unchecked. GetInstance(5).intptr += (nint)i32; } // multiplication results in compiler-error without the cast GetInstance(6).intptr *= (nint)2; #endif GetInstance(7).i += i32; GetInstance(8).i <<= i32; } #if !(CS110 && NET70) public void LocalTypeFromStore() { nint num = 42; IntPtr zero = IntPtr.Zero; nint zero2 = IntPtr.Zero; nuint num2 = 43u; nint num3 = i; IntPtr intPtr = intptr; Console.WriteLine(); zero2 = 1; Console.WriteLine(); intptr = num; intptr = zero; intptr = zero2; uintptr = num2; intptr = num3; intptr = intPtr; } #endif public void LocalTypeFromUse() { #if CS110 && NET70 nint num = intptr; nint num2 = intptr; Console.WriteLine(); intptr = num; i = num2 + 1; #else IntPtr intPtr = intptr; nint num = intptr; Console.WriteLine(); intptr = intPtr; i = num + 1; #endif } public nint NegateUnsigned(nuint x) { return (nint)(0 - x); } public bool CompareToMinus3(nuint x) { return x == unchecked((nuint)(-3)); } public nint SignedNotFittingIn32Bits() { // Explicit `unchecked` is necessary when casting oversized constant to nint return unchecked((nint)9123123123123L); } public nuint UnsignedNotFittingIn32Bits() { // Explicit `unchecked` is necessary when casting oversized constant to nuint return unchecked((nuint)9123123123123uL); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class NullPropagation { private class MyClass { public int IntVal; public readonly int ReadonlyIntVal; public MyStruct StructField; public readonly MyStruct ReadonlyStructField; public string Text; public MyClass Field; public MyClass Property { get; set; } public MyClass this[int index] => null; public MyClass Method(int arg) { return null; } public void Done() { } } private struct MyStruct { public int IntVal; public readonly int ReadonlyIntVal; public MyClass Field; public MyStruct? Property1 => null; public MyStruct Property2 => default(MyStruct); public MyStruct? this[int index] => null; public MyStruct? Method1(int arg) { return null; } public MyStruct Method2(int arg) { return default(MyStruct); } public void Done() { } } private class Container { public GenericStruct Other; } private struct GenericStruct { public T1 Field1; public T2 Field2; public Container Other; public override string ToString() { return "(" + Field1?.ToString() + ", " + Field2?.ToString() + ")"; } public int? GetTextLength() { return Field1?.ToString().Length + Field2?.ToString().Length + 4; } public string Chain1() { return Other?.Other.Other?.Other.Field1?.ToString(); } public string Chain2() { return Other?.Other.Other?.Other.Field1?.ToString()?.GetType().Name; } public int? Test2() { return Field1?.ToString().Length ?? 42; } public int? GetTextLengthNRE() { return (Field1?.ToString()).Length; } } public interface ITest { int Int(); ITest Next(); } private int GetInt() { return 9; } private string GetString() { return null; } private MyClass GetMyClass() { return null; } private MyStruct? GetMyStruct() { return null; } public string Substring() { return GetString()?.Substring(GetInt()); } public void CallSubstringAndIgnoreResult() { GetString()?.Substring(GetInt()); } private void Use(T t) { } public void CallDone() { GetMyClass()?.Done(); GetMyClass()?.Field?.Done(); GetMyClass()?.Field.Done(); GetMyClass()?.Property?.Done(); GetMyClass()?.Property.Done(); GetMyClass()?.Method(GetInt())?.Done(); GetMyClass()?.Method(GetInt()).Done(); GetMyClass()?[GetInt()]?.Done(); GetMyClass()?[GetInt()].Done(); } public void CallDoneStruct() { GetMyStruct()?.Done(); GetMyStruct()?.Field?.Done(); GetMyStruct()?.Field.Done(); GetMyStruct()?.Property1?.Done(); GetMyStruct()?.Property2.Done(); GetMyStruct()?.Method1(GetInt())?.Done(); GetMyStruct()?.Method2(GetInt()).Done(); GetMyStruct()?[GetInt()]?.Done(); } public void RequiredParentheses() { (GetMyClass()?.Field).Done(); (GetMyClass()?.Method(GetInt())).Done(); (GetMyStruct()?.Property2)?.Done(); } public int?[] ChainsOnClass() { return new int?[9] { GetMyClass()?.IntVal, GetMyClass()?.Field.IntVal, GetMyClass()?.Field?.IntVal, GetMyClass()?.Property.IntVal, GetMyClass()?.Property?.IntVal, GetMyClass()?.Method(GetInt()).IntVal, GetMyClass()?.Method(GetInt())?.IntVal, GetMyClass()?[GetInt()].IntVal, GetMyClass()?[GetInt()]?.IntVal }; } public int?[] ChainsStruct() { return new int?[8] { GetMyStruct()?.IntVal, GetMyStruct()?.Field.IntVal, GetMyStruct()?.Field?.IntVal, GetMyStruct()?.Property2.IntVal, GetMyStruct()?.Property1?.IntVal, GetMyStruct()?.Method2(GetInt()).IntVal, GetMyStruct()?.Method1(GetInt())?.IntVal, GetMyStruct()?[GetInt()]?.IntVal }; } public int CoalescingReturn() { return GetMyClass()?.IntVal ?? 1; } public void Coalescing() { Use(GetMyClass()?.IntVal ?? 1); } public void CoalescingString() { Use(GetMyClass()?.Text ?? "Hello"); } public void CallOnValueTypeField() { Use(GetMyClass()?.IntVal.ToString()); Use(GetMyStruct()?.IntVal.ToString()); Use(GetMyClass()?.ReadonlyIntVal.ToString()); Use(GetMyStruct()?.ReadonlyIntVal.ToString()); GetMyClass()?.StructField.Done(); GetMyClass()?.ReadonlyStructField.Done(); } public void InvokeDelegate(EventHandler eh) { eh?.Invoke(null, EventArgs.Empty); } public int? InvokeDelegate(Func f) { return f?.Invoke(); } private void NotNullPropagation(MyClass c) { // don't decompile this to "(c?.IntVal ?? 0) != 0" if (c != null && c.IntVal != 0) { Console.WriteLine("non-zero"); } if (c == null || c.IntVal == 0) { Console.WriteLine("null or zero"); } Console.WriteLine("end of method"); } private void Setter(MyClass c) { if (c != null) { c.IntVal = 1; } Console.WriteLine(); if (c != null) { c.Property = null; } } private static int? GenericUnconstrainedInt(T t) where T : ITest { return t?.Int(); } private static int? GenericClassConstraintInt(T t) where T : class, ITest { return t?.Int(); } private static int? GenericStructConstraintInt(T? t) where T : struct, ITest { return t?.Int(); } private static int? GenericRefUnconstrainedInt(ref T t) where T : ITest { return t?.Int(); } private static int? GenericRefClassConstraintInt(ref T t) where T : class, ITest { return t?.Int(); } private static int? GenericRefStructConstraintInt(ref T? t) where T : struct, ITest { return t?.Int(); } public int? Issue1709(object obj) { return (obj as ICollection)?.Count + (obj as ICollection)?.Count; } private static void Issue1689(List setsOfNumbers) { Console.WriteLine(setsOfNumbers?[0]?[1].ToString() == "2"); Console.WriteLine(setsOfNumbers?[1]?[1].ToString() == null); } private static dynamic DynamicNullProp(dynamic a) { return a?.b.c(1)?.d[10]; } private static string Issue3181() { #if EXPECTED_OUTPUT return ((int?)null)?.ToString(); #else int? x = null; return x?.ToString(); #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs ================================================ #nullable enable using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class T01_NullableRefTypes { private string field_string; private string? field_nullable_string; private dynamic? field_nullable_dynamic; private Dictionary field_generic; private Dictionary field_generic2; private Dictionary field_generic3; private KeyValuePair field_generic_value_type; private KeyValuePair? field_generic_nullable_value_type; private (string, string?, string) field_tuple; private string[]?[] field_array; private Dictionary<(string, string?), (int, string[]?, string?[])> field_complex; private dynamic[][,]?[,,][,,,] field_complex_nested_array; public (string A, dynamic? B) PropertyNamedTuple { get { throw new NotImplementedException(); } } public (string A, dynamic? B) this[(dynamic? C, string D) weirdIndexer] { get { throw new NotImplementedException(); } } public int GetLength1(string[] arr) { return field_string.Length + arr.Length; } public int GetLength2(string[]? arr) { return field_nullable_string.Length + arr.Length; } public int? GetLength3(string[]? arr) { return field_nullable_string?.Length + arr?.Length; } public void GenericNullable((T1?, T1, T2, T2?, T1, T1?) x) where T1 : class where T2 : struct { } public T ByRef(ref T t) { return t; } public void CallByRef(ref string a, ref string? b) { ByRef(ref a).ToString(); ByRef(ref b).ToString(); } public void Constraints() where C : class where CN : class? where NN : notnull where S : struct where D : IDisposable where DN : IDisposable? where NND : notnull, IDisposable { } } public class T02_EverythingIsNullableInHere { private string? field1; private object? field2; // value types are irrelevant for the nullability attributes: private int field3; private int? field4; public string? Property { get; set; } public event EventHandler? Event; public static int? NullConditionalOperator(T02_EverythingIsNullableInHere? x) { // This code throws if `x != null && x.field1 == null`. // But we can't decompile it to the warning-free "x?.field1!.Length", // because of https://github.com/dotnet/roslyn/issues/43659 return x?.field1.Length; } } public class T03_EverythingIsNotNullableInHere { private string field1; private object field2; // value types are irrelevant for the nullability attributes: private int field3; private int? field4; public string Property { get; set; } public event EventHandler Event; } public class T04_Dictionary where TKey : notnull { private struct Entry { public TKey key; public TValue value; } private int[]? _buckets; private Entry[]? _entries; private IEqualityComparer? _comparer; } public class T05_NullableUnconstrainedGeneric { public static TValue? Default() { return default(TValue); } public static void CallDefault() { #if OPT string? format = Default(); #else // With optimizations it's a stack slot, so ILSpy picks a nullable type. // Without optimizations it's a local, so the nullability is missing. string format = Default(); #endif int num = Default(); #if CS110 && NET70 nint num2 = Default(); #else int num2 = Default(); #endif (object, string) tuple = Default<(object, string)>(); Console.WriteLine("No inlining"); Console.WriteLine(format, num, num2, tuple); } } public class T06_ExplicitInterfaceImplementation : IEnumerable>, IEnumerable { // TODO: declaring type is not yet rendered with nullability annotations from the base type IEnumerator> IEnumerable>.GetEnumerator() { yield return new KeyValuePair("a", "b"); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } public class T07_ExplicitInterfaceImplementation : IEnumerator>, IEnumerator, IDisposable { KeyValuePair IEnumerator>.Current { get { throw new NotImplementedException(); } } object IEnumerator.Current { get { throw new NotImplementedException(); } } void IDisposable.Dispose() { throw new NotImplementedException(); } bool IEnumerator.MoveNext() { throw new NotImplementedException(); } void IEnumerator.Reset() { throw new NotImplementedException(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs ================================================ // Copyright (c) Daniel Grunwald // // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class AllOperators { public static AllOperators operator +(AllOperators a, AllOperators b) { return null; } public static AllOperators operator -(AllOperators a, AllOperators b) { return null; } public static AllOperators operator *(AllOperators a, AllOperators b) { return null; } public static AllOperators operator /(AllOperators a, AllOperators b) { return null; } #if CS110 public static AllOperators operator checked +(AllOperators a, AllOperators b) { return null; } public static AllOperators operator checked -(AllOperators a, AllOperators b) { return null; } public static AllOperators operator checked *(AllOperators a, AllOperators b) { return null; } public static AllOperators operator checked /(AllOperators a, AllOperators b) { return null; } #endif public static AllOperators operator %(AllOperators a, AllOperators b) { return null; } public static AllOperators operator &(AllOperators a, AllOperators b) { return null; } public static AllOperators operator |(AllOperators a, AllOperators b) { return null; } public static AllOperators operator ^(AllOperators a, AllOperators b) { return null; } public static AllOperators operator <<(AllOperators a, int b) { return null; } public static AllOperators operator >>(AllOperators a, int b) { return null; } #if CS110 public static AllOperators operator >>>(AllOperators a, int b) { return null; } #endif public static AllOperators operator ~(AllOperators a) { return null; } public static AllOperators operator !(AllOperators a) { return null; } public static AllOperators operator -(AllOperators a) { return null; } public static AllOperators operator +(AllOperators a) { return null; } public static AllOperators operator ++(AllOperators a) { return null; } public static AllOperators operator --(AllOperators a) { return null; } #if CS110 public static AllOperators operator checked -(AllOperators a) { return null; } public static AllOperators operator checked ++(AllOperators a) { return null; } public static AllOperators operator checked --(AllOperators a) { return null; } #endif public static bool operator true(AllOperators a) { return false; } public static bool operator false(AllOperators a) { return false; } public static bool operator ==(AllOperators a, AllOperators b) { return false; } public static bool operator !=(AllOperators a, AllOperators b) { return false; } public static bool operator <(AllOperators a, AllOperators b) { return false; } public static bool operator >(AllOperators a, AllOperators b) { return false; } public static bool operator <=(AllOperators a, AllOperators b) { return false; } public static bool operator >=(AllOperators a, AllOperators b) { return false; } public static implicit operator AllOperators(int a) { return null; } public static explicit operator int(AllOperators a) { return 0; } #if CS110 public static explicit operator checked int(AllOperators a) { return 0; } #endif } public class UseAllOperators { private AllOperators a = new AllOperators(); private AllOperators b = new AllOperators(); private AllOperators c; public void Test() { c = a + b; c = a - b; c = a * b; c = a / b; #if CS110 checked { c = a + b; c = a - b; c = a * b; c = a / b; } // force end of checked block: ++a; #endif c = a % b; c = a & b; c = a | b; c = a ^ b; c = a << 5; c = a >> 5; #if CS110 c = a >>> 5; #endif c = ~a; c = !a; c = -a; c = +a; c = ++a; c = --a; #if CS110 checked { c = -a; c = ++a; c = --a; } // force end of checked block: ++a; #endif if (a) { Console.WriteLine("a"); } if (!a) { Console.WriteLine("!a"); } if (a == b) { Console.WriteLine("a == b"); } if (a != b) { Console.WriteLine("a != b"); } if (a < b) { Console.WriteLine("a < b"); } if (a > b) { Console.WriteLine("a > b"); } if (a <= b) { Console.WriteLine("a <= b"); } if (a >= b) { Console.WriteLine("a >= b"); } int num = (int)a; #if CS110 num = checked((int)a); // force end of checked block: num = (int)a; #endif a = num; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs ================================================ // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class OptionalArguments : List { public delegate int D(int p = 10); public enum MyEnum { A, B } internal class OptionalArgumentTest { private static void Test() { Test2(); Test3(); Test4(); } private static void Test2(int a = 0) { } private static void Test3(int a = 0, int? b = null) { } private static void Test4(int? b = null, int a = 0) { } } public OptionalArguments(string name, int a = 5) { } public OptionalArguments(int num, bool flag = true) { } public void Add(string name, int a = 5) { } private void SimpleTests() { Test(); Test(5); Test(10, "Hello World!"); Decimal(); Decimal(5m); #if CS72 NamedArgument(flag: true); NamedArgument(flag: false); #endif } private void Conflicts() { OnlyDifferenceIsLastArgument(5, 3, "Hello"); OnlyDifferenceIsLastArgument(5, 3, 3.141); OnlyDifferenceIsLastArgument(5, 3, null); OnlyDifferenceIsLastArgument(5, 3, double.NegativeInfinity); OnlyDifferenceIsLastArgumentCastNecessary(10, "World", (string)null); OnlyDifferenceIsLastArgumentCastNecessary(10, "Hello", (OptionalArguments)null); DifferenceInArgumentCount(); DifferenceInArgumentCount("Hello"); DifferenceInArgumentCount("World"); } private void ParamsTests() { ParamsMethod(5, 10, 9, 8); ParamsMethod(null); ParamsMethod(5); ParamsMethod(10); ParamsMethod(null, 1, 2, 3); } private void CallerInfo() { CallerMemberName("CallerInfo"); CallerMemberName(null); CallerLineNumber(60); CallerLineNumber(0); } private void Constructor(out OptionalArguments a, out OptionalArguments b, out OptionalArguments c) { a = new OptionalArguments("Hallo"); b = new OptionalArguments(10); c = new OptionalArguments(10) { { "Test", 10 }, "Test2" }; } private static string GetStr(int unused) { return " "; } public static string Issue1567(string str1, string str2) { return string.Concat(str1.Replace('"', '\''), str2: str2.Replace('"', '\''), str1: GetStr(42)); } private void CallerMemberName([CallerMemberName] string memberName = null) { } private void CallerFilePath([CallerFilePath] string filePath = null) { } private void CallerLineNumber([CallerLineNumber] int lineNumber = 0) { } private void ParamsMethod(int a = 5, params int[] values) { } private void ParamsMethod(string a = null, params int[] values) { } private void DifferenceInArgumentCount() { } private void DifferenceInArgumentCount(string a = "Hello") { } private void Test(int a = 10, string b = "Test") { } private void Decimal(decimal d = 10m) { } private void OnlyDifferenceIsLastArgument(int a, int b, string c = null) { } private void OnlyDifferenceIsLastArgument(int a, int b, double d = double.NegativeInfinity) { } private void OnlyDifferenceIsLastArgumentCastNecessary(int a, string b, string c = null) { } private void OnlyDifferenceIsLastArgumentCastNecessary(int a, string b, OptionalArguments args = null) { } private void NamedArgument(bool flag) { } private string Get(out int a) { throw null; } public static void Definition_Enum(MyEnum p = MyEnum.A) { } public static void Definition_Enum_OutOfRangeDefault(MyEnum p = (MyEnum)(-1)) { } public static void Definition_NullableEnum(MyEnum? p = MyEnum.A) { } public static void Definition_NullableEnum_OutOfRangeDefault(MyEnum? p = (MyEnum)(-1)) { } public static void Definition_Int(int p = 0) { } public static void Definition_NullableInt(int? p = 0) { } public static void Definition_Int100(int p = 100) { } public static void Definition_NullableInt100(int? p = 100) { } #if CS90 public static void Definition_NInt(nint p = 100) { } public static void Definition_NullableNInt(nint? p = 100) { } #endif public static void Issue2920a(int x) { } public static void Issue2920b([DefaultParameterValue(3)] int x) { } public static void Issue2920c(ref int x) { } public static void Issue2920d([DefaultParameterValue(3)] ref int x) { } public static void Issue2920e(out int x) { x = 0; } public static void Issue2920f([DefaultParameterValue(3)] out int x) { x = 0; } #if CS70 public static void Issue2920g(in int x) { } public static void Issue2920h([DefaultParameterValue(3)] in int x) { } #endif public static void Issue2920i([Optional] int x) { } public static void Issue2920j(int x = 3) { } public static void Issue2920k([Optional] ref int x) { } public static void Issue2920l([Optional][DefaultParameterValue(3)] ref int x) { } public static void Issue2920m([Optional] out int x) { x = 0; } public static void Issue2920n([Optional][DefaultParameterValue(3)] out int x) { x = 0; } #if CS70 public static void Issue2920o([Optional] in int x) { } public static void Issue2920p(in int x = 3) { } #endif public static void Issue3469a([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) { } #if CS120 public static Action Issue3469b() { #pragma warning disable CS9099 // Parameter 1 has default value 'default(int)' in lambda but '' in the target delegate type return ([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) => { }; #pragma warning restore CS9099 } public static D LambdaWithOptionalParameter() { return (int x = 10) => x; } public static void Use(D d) { d(); d(42); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArgumentsDisabled.cs ================================================ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class OptionalArgumentsDisabled { public void Test() { MixedArguments("123", 0, 0); OnlyOptionalArguments(0, 0); } public void MixedArguments(string msg, int a = 0, int b = 0) { } public void OnlyOptionalArguments(int a = 0, int b = 0) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class OutVariables { public static void OutVarInShortCircuit(Dictionary d) { if (d.Count > 2 && d.TryGetValue(42, out var value)) { Console.WriteLine(value); } } public static Action CapturedOutVarInShortCircuit(Dictionary d) { // Note: needs reasoning about "definitely assigned if true" // to ensure that the value is initialized when the delegate is declared. if (d.Count > 2 && d.TryGetValue(42, out var value)) { return delegate { Console.WriteLine(value); }; } return null; } private bool TryGet(out T result) { result = default(T); return true; } public void M3() { TryGet>(out Dictionary data); Test(); int Test() { return data[0].A; } } public void GetObject(out object obj) { obj = null; } public void M4() { GetObject(out dynamic obj); obj.Method(); } public void M5() { Func func = () => TryGet(out var result) && result != null; Func func2 = () => TryGet(out var result) && result != null; func(); func2(); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/PInvoke.cs ================================================ // 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 // 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.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { // P/Invoke and marshalling attribute tests public class PInvoke { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)] public struct MarshalAsTest { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public uint[] FixedArray; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)] public int[] FixedBoolArray; [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] public string[] SafeBStrArray; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] public string FixedString; } [StructLayout(LayoutKind.Explicit)] public struct Rect { [FieldOffset(0)] public int left; [FieldOffset(4)] public int top; [FieldOffset(8)] public int right; [FieldOffset(12)] public int bottom; } #pragma warning disable CS0618 // Type or member is obsolete public static decimal MarshalAttributesOnPropertyAccessors { [return: MarshalAs(UnmanagedType.Currency)] get { throw new NotImplementedException(); } [param: MarshalAs(UnmanagedType.Currency)] set { } } #pragma warning restore CS0618 // Type or member is obsolete [DllImport("xyz.dll", CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input); [DllImport("xyz.dll")] private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar); [DllImport("xyz.dll")] private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar); [DllImport("xyz.dll")] private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar); [DllImport("xyz.dll")] private static extern void New4([MarshalAs(UnmanagedType.LPArray)] int[] ar); public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o) { } public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o) { } #if CS110 && NET70 [DllImport("ws2_32.dll", SetLastError = true)] internal static extern nint ioctlsocket([In] nint socketHandle, [In] int cmd, [In][Out] ref int argp); #else [DllImport("ws2_32.dll", SetLastError = true)] internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In][Out] ref int argp); #endif public void CallMethodWithInOutParameter() { int argp = 0; ioctlsocket(IntPtr.Zero, 0, ref argp); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ParamsCollections.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class ParamsCollections { public static void ParamsEnumerable(params IEnumerable values) { } public static void ParamsList(params List values) { } public static void ParamsReadOnlySpan(params ReadOnlySpan values) { } public static void ParamsSpan(params Span values) { // note: implicitly "scoped", "params scoped Span values" is allowed // but "scoped" is always redundant for params. } public static void ParamUnscopedSpan([UnscopedRef] params Span values) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class PatternMatching { public class X { public int? NullableIntField; public S? NullableCustomStructField; public int I { get; set; } public string Text { get; set; } public object Obj { get; set; } public S CustomStruct { get; set; } public int? NullableIntProp { get; set; } public S? NullableCustomStructProp { get; set; } } public struct S { public int I; public string Text { get; set; } public object Obj { get; set; } public S2 S2 { get; set; } } public struct S2 { public int I; public float F; public decimal D; public string Text { get; set; } public object Obj { get; set; } } public void SimpleTypePattern(object x) { if (x is string value) { Console.WriteLine(value); } } public void TypePatternWithShortcircuit(object x) { Use(F() && x is string text && text.Contains("a")); if (F() && x is string text2 && text2.Contains("a")) { Console.WriteLine(text2); } } public void TypePatternWithShortcircuitAnd(object x) { if (x is string text && text.Contains("a")) { Console.WriteLine(text); } else { Console.WriteLine(); } } public void TypePatternWithShortcircuitOr(object x) { if (!(x is string text) || text.Contains("a")) { Console.WriteLine(); } else { Console.WriteLine(text); } } public void TypePatternWithShortcircuitOr2(object x) { if (F() || !(x is string value)) { Console.WriteLine(); } else { Console.WriteLine(value); } } public void TypePatternValueTypesCondition(object x) { if (x is int num) { Console.WriteLine("Integer: " + num); } else { Console.WriteLine("else"); } } public void TypePatternValueTypesCondition2() { if (GetObject() is int num) { Console.WriteLine("Integer: " + num); } else { Console.WriteLine("else"); } } public void TypePatternValueTypesWithShortcircuitAnd(object x) { if (x is int num && num.GetHashCode() > 0) { Console.WriteLine("Positive integer: " + num); } else { Console.WriteLine("else"); } } public void TypePatternValueTypesWithShortcircuitOr(object x) { if (!(x is int value) || value.GetHashCode() > 0) { Console.WriteLine(); } else { Console.WriteLine(value); } } #if ROSLYN3 || OPT // Roslyn 2.x generates a complex infeasible path in debug builds, which RemoveInfeasiblePathTransform // currently cannot handle. Because this would increase the complexity of that transform, we ignore // this case. public void TypePatternValueTypesWithShortcircuitOr2(object x) { if (F() || !(x is int value)) { Console.WriteLine(); } else { Console.WriteLine(value); } } #endif public void TypePatternGenerics(object x) { if (x is T val) { Console.WriteLine(val.GetType().FullName); } else { Console.WriteLine("not a " + typeof(T).FullName); } } public void TypePatternGenericRefType(object x) where T : class { if (x is T val) { Console.WriteLine(val.GetType().FullName); } else { Console.WriteLine("not a " + typeof(T).FullName); } } public void TypePatternGenericValType(object x) where T : struct { if (x is T val) { Console.WriteLine(val.GetType().FullName); } else { Console.WriteLine("not a " + typeof(T).FullName); } } public void TypePatternValueTypesWithShortcircuitAndMultiUse(object x) { if (x is int num && num.GetHashCode() > 0 && num % 2 == 0) { Console.WriteLine("Positive integer: " + num); } else { Console.WriteLine("else"); } } public void TypePatternValueTypesWithShortcircuitAndMultiUse2(object x) { if ((x is int num && num.GetHashCode() > 0 && num % 2 == 0) || F()) { Console.WriteLine("true"); } else { Console.WriteLine("else"); } } public void TypePatternValueTypesWithShortcircuitAndMultiUse3(object x) { if (F() || (x is int num && num.GetHashCode() > 0 && num % 2 == 0)) { Console.WriteLine("true"); } else { Console.WriteLine("else"); } } public void TypePatternValueTypes() { Use(F() && GetObject() is int num && num.GetHashCode() > 0 && num % 2 == 0); } public static void NotTypePatternVariableUsedOutsideTrueBranch(object x) { string text = x as string; if (text != null && text.Length > 5) { Console.WriteLine("pattern matches"); } if (text != null && text.Length > 10) { Console.WriteLine("other use!"); } } public static void NotTypePatternBecauseVarIsNotDefAssignedInCaseOfFallthrough(object x) { #if OPT string obj = x as string; if (obj == null) { Console.WriteLine("pattern doesn't match"); } Console.WriteLine(obj == null); #else string text = x as string; if (text == null) { Console.WriteLine("pattern doesn't match"); } Console.WriteLine(text == null); #endif } public void GenericTypePatternInt(T x) { if (x is int value) { Console.WriteLine(value); } else { Console.WriteLine("not an int"); } } public void GenericValueTypePatternInt(T x) where T : struct { if (x is int value) { Console.WriteLine(value); } else { Console.WriteLine("not an int"); } } public void GenericRefTypePatternInt(T x) where T : class { if (x is int value) { Console.WriteLine(value); } else { Console.WriteLine("not an int"); } } public void GenericTypePatternString(T x) { if (x is string value) { Console.WriteLine(value); } else { Console.WriteLine("not a string"); } } public void GenericRefTypePatternString(T x) where T : class { if (x is string value) { Console.WriteLine(value); } else { Console.WriteLine("not a string"); } } public void GenericValueTypePatternStringRequiresCastToObject(T x) where T : struct { if ((object)x is string value) { Console.WriteLine(value); } else { Console.WriteLine("not a string"); } } #if CS80 public void RecursivePattern_Type(object x) { if (x is X { Obj: string obj }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_TypeAndConst(object x) { if (x is X { Obj: string obj, I: 42 }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_Constant(object obj) { if (obj is X { Obj: null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_StringConstant(object obj) { if (obj is X { Text: "Hello" } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_MultipleConstants(object obj) { if (obj is X { I: 42, Text: "Hello" } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_ValueTypeWithField(object obj) { if (obj is S { I: 42 } s) { Console.WriteLine("Test " + s); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_MultipleConstantsMixedWithVar(object x) { if (x is X { I: 42, Obj: var obj, Text: "Hello" }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NonTypePattern(object obj) { if (obj is X { I: 42, Text: { Length: 0 } } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePatternValueType_NonTypePatternTwoProps(object obj) { if (obj is X { I: 42, CustomStruct: { I: 0, Text: "Test" } } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NonTypePatternNotNull(object o) { if (o is X { I: 42, Text: not null, Obj: var obj } x) { Console.WriteLine("Test " + x.I + " " + obj.GetType()); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_VarLengthPattern(object obj) { if (obj is X { I: 42, Text: { Length: var length } } x) { Console.WriteLine("Test " + x.I + ": " + length); } else { Console.WriteLine("not Test"); } } public void RecursivePatternValueType_VarLengthPattern(object obj) { if (obj is S { I: 42, Text: { Length: var length } } s) { Console.WriteLine("Test " + s.I + ": " + length); } else { Console.WriteLine("not Test"); } } public void RecursivePatternValueType_VarLengthPattern_SwappedProps(object obj) { if (obj is S { Text: { Length: var length }, I: 42 } s) { Console.WriteLine("Test " + s.I + ": " + length); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_VarLengthPattern_SwappedProps(object obj) { if (obj is X { Text: { Length: var length }, I: 42 } x) { Console.WriteLine("Test " + x.I + ": " + length); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntField_Const(object obj) { if (obj is X { NullableIntField: 42 } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntField_Null(object obj) { if (obj is X { NullableIntField: null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntField_NotNull(object obj) { if (obj is X { NullableIntField: not null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntField_Var(object obj) { if (obj is X { NullableIntField: var nullableIntField }) { Console.WriteLine("Test " + nullableIntField.Value); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntProp_Const(object obj) { if (obj is X { NullableIntProp: 42 } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntProp_Null(object obj) { if (obj is X { NullableIntProp: null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntProp_NotNull(object obj) { if (obj is X { NullableIntProp: not null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableIntProp_Var(object obj) { if (obj is X { NullableIntProp: var nullableIntProp }) { Console.WriteLine("Test " + nullableIntProp.Value); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructField_Const(object obj) { if (obj is X { NullableCustomStructField: { I: 42, Obj: not null } nullableCustomStructField }) { Console.WriteLine("Test " + nullableCustomStructField.I); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructField_Null(object obj) { if (obj is X { NullableCustomStructField: null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructField_NotNull(object obj) { if (obj is X { NullableCustomStructField: not null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructField_Var(object obj) { if (obj is X { NullableCustomStructField: var nullableCustomStructField, Obj: null }) { Console.WriteLine("Test " + nullableCustomStructField.Value); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructProp_Const(object obj) { if (obj is X { NullableCustomStructProp: { I: 42, Obj: not null } nullableCustomStructProp }) { Console.WriteLine("Test " + nullableCustomStructProp.Text); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructProp_Null(object obj) { if (obj is X { NullableCustomStructProp: null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructProp_NotNull(object obj) { if (obj is X { NullableCustomStructProp: not null } x) { Console.WriteLine("Test " + x); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_NullableCustomStructProp_Var(object obj) { if (obj is X { NullableCustomStructProp: var nullableCustomStructProp, Obj: null }) { Console.WriteLine("Test " + nullableCustomStructProp.Value); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_CustomStructNested_Null(object obj) { if (obj is S { S2: { Obj: null } }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_CustomStructNested_TextLengthZero(object obj) { if (obj is S { S2: { Text: { Length: 0 } } }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_CustomStructNested_EmptyString(object obj) { if (obj is S { S2: { Text: "" } }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_CustomStructNested_Float(object obj) { if (obj is S { S2: { F: 3.141f, Obj: null } }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } public void RecursivePattern_CustomStructNested_Decimal(object obj) { if (obj is S { S2: { D: 3.141m, Obj: null } }) { Console.WriteLine("Test " + obj); } else { Console.WriteLine("not Test"); } } #endif private bool F() { return true; } private object GetObject() { throw new NotImplementedException(); } private void Use(bool x) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class PointerArithmetic { public unsafe static void AssignmentVoidPointerToIntPointer(void* ptr) { ((int*)ptr)[2] = 1; } public unsafe static int AccessVoidPointerToIntPointer(void* ptr) { return ((int*)ptr)[2]; } public unsafe static void AssignmentLongPointerToIntPointer_2(long* ptr) { ((int*)ptr)[2] = 1; } public unsafe static int AccessLongPointerToIntPointer_2(long* ptr) { return ((int*)ptr)[2]; } public unsafe static void AssignmentLongPointerToIntPointer_3(long* ptr) { ((int*)ptr)[3] = 1; } public unsafe static int AccessLongPointerToIntPointer_3(long* ptr) { return ((int*)ptr)[3]; } public unsafe static void AssignmentGuidPointerToIntPointer(Guid* ptr) { ((int*)ptr)[2] = 1; } public unsafe static int AccessGuidPointerToIntPointer(Guid* ptr) { return ((int*)ptr)[2]; } public unsafe static uint AccessGuidPointerToUIntPointer(Guid* ptr) { return ((uint*)ptr)[2]; } public unsafe static void AssignmentGuidPointerToDateTimePointer(Guid* ptr) { ((DateTime*)ptr)[2] = DateTime.Now; } public unsafe static void AssignmentGuidPointerToDateTimePointerDefault(Guid* ptr) { ((DateTime*)ptr)[2] = default(DateTime); } public unsafe static void AssignmentGuidPointerToDateTimePointer_2(Guid* ptr) { *(DateTime*)(ptr + 2) = DateTime.Now; } public unsafe static void AssignmentGuidPointerToDateTimePointerDefault_2(Guid* ptr) { *(DateTime*)(ptr + 2) = default(DateTime); } public unsafe static DateTime AccessGuidPointerToDateTimePointer(Guid* ptr) { return ((DateTime*)ptr)[2]; } public unsafe static DateTime AccessGuidPointerToDateTimePointer_2(Guid* ptr) { return *(DateTime*)(ptr + 2); } public unsafe static void AssignmentIntPointer(int* ptr) { ptr[2] = 1; } public unsafe static int AccessIntPointer(int* ptr) { return ptr[2]; } public unsafe static void AssignmentGuidPointer(Guid* ptr) { ptr[2] = Guid.NewGuid(); } public unsafe static Guid AccessGuidPointer(Guid* ptr) { return ptr[2]; } public unsafe static void AssignmentVoidPointerToGuidPointer(void* ptr) { ((Guid*)ptr)[2] = Guid.NewGuid(); } public unsafe static Guid AccessVoidPointerToGuidPointer(void* ptr) { return ((Guid*)ptr)[2]; } public unsafe static void AssignmentIntPointerToGuidPointer(int* ptr) { ((Guid*)ptr)[2] = Guid.NewGuid(); } public unsafe static void AssignmentIntPointerToGuidPointer_2(int* ptr) { *(Guid*)(ptr + 2) = Guid.NewGuid(); } public unsafe static Guid AccessIntPointerToGuidPointer(int* ptr) { return ((Guid*)ptr)[2]; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs ================================================ // Copyright (c) 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 // 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.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class PropertiesAndEvents { private interface IBase { int GetterOnly { get; } int SetterOnly { set; } int Test { get; set; } event Action Event; } private abstract class BaseClass { public abstract event EventHandler ThisIsAnAbstractEvent; } private class OtherClass : BaseClass { public override event EventHandler ThisIsAnAbstractEvent; } private class ExplicitImpl : IBase { int IBase.Test { get { throw new NotImplementedException(); } set { } } int IBase.GetterOnly { get { throw new NotImplementedException(); } } int IBase.SetterOnly { set { throw new NotImplementedException(); } } event Action IBase.Event { add { } remove { } } } private class Impl : IBase { public int GetterOnly { get { throw new NotImplementedException(); } } public int SetterOnly { set { throw new NotImplementedException(); } } public int Test { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public event Action Event; } private interface IChange { int Property { get; set; } event EventHandler Changed; } private class Change : IChange { private EventHandler Changed; int IChange.Property { get; set; } event EventHandler IChange.Changed { add { Changed = (EventHandler)Delegate.Combine(Changed, value); } remove { Changed = (EventHandler)Delegate.Remove(Changed, value); } } } [NonSerialized] private int someField; private object issue1221; public int Value { get; private set; } public int AutomaticProperty { get; set; } public int CustomProperty { get { return AutomaticProperty; } set { AutomaticProperty = value; } } private object Issue1221 { set { issue1221 = value; } } public object Item { get { return null; } set { } } #if ROSLYN public int NotAnAutoProperty => someField; #else public int NotAnAutoProperty { get { return someField; } } #endif public event EventHandler AutomaticEvent; [field: NonSerialized] public event EventHandler AutomaticEventWithInitializer = delegate { }; #if ROSLYN // Legacy csc has a bug where EventHandler is only used for the backing field public event EventHandler DynamicAutoEvent; #endif #if CS73 public event EventHandler<(int A, string B)> AutoEventWithTuple; #endif #if CS80 public event EventHandler<(int a, dynamic? b)> ComplexAutoEvent; #endif public event EventHandler CustomEvent { add { AutomaticEvent += value; } remove { AutomaticEvent -= value; } } public event EventHandler Issue3575_AutoEvent; public event EventHandler Issue3575_NonAuto; public event EventHandler Issue3575_Auto { add { } remove { } } public event EventHandler Issue3575_NonAutoEvent { add { } remove { } } public int Getter(StringBuilder b) { return b.Length; } public void Setter(StringBuilder b) { b.Capacity = 100; } public char IndexerGetter(StringBuilder b) { return b[50]; } public void IndexerSetter(StringBuilder b) { b[42] = 'b'; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.Pretty { internal class QualifierTests { private struct Test { private int dummy; private void DeclaringType(QualifierTests instance) { instance.NoParameters(); } private void DeclaringType() { StaticNoParameteres(); Parameter(null); StaticParameter(null); // The unnecessary cast is added, because we add casts before we add the qualifier. // normally it's preferable to have casts over having qualifiers, // this is an ugly edge case. QualifierTests.StaticParameter((object)null); } private void Parameter(object o) { } private static void StaticParameter(object o) { } private void Parameter(QualifierTests test) { Delegate(Parameter); Delegate(StaticParameter); Delegate(test.Parameter); Delegate(QualifierTests.StaticParameter); } private static void StaticParameter(QualifierTests test) { } private static void DeclaringTypeStatic() { } private void DeclaringTypeConflict(QualifierTests instance) { DeclaringType(); instance.DeclaringType(); fieldConflict(); instance.fieldConflict = 5; } private void DeclaringTypeConflict() { DeclaringTypeStatic(); QualifierTests.DeclaringTypeStatic(); } private void fieldConflict() { } private void Delegate(Action action) { } public string ThisQualifierWithCast() { return ((object)this).ToString(); } public override string ToString() { // decompiled as return ((ValueType)this).ToString(); return base.ToString(); } } internal class Parent { public virtual void Virtual() { } public virtual void NewVirtual() { } public void New() { } public void BaseOnly() { } } internal class Child : Parent { public override void Virtual() { base.Virtual(); } public new void NewVirtual() { base.NewVirtual(); } public new void New() { base.New(); } public void BaseQualifiers() { Virtual(); base.Virtual(); NewVirtual(); base.NewVirtual(); New(); base.New(); BaseOnly(); } } #pragma warning disable CS8981 private class i { public static void Test() { } } private class value { public static int item; public static void Test() { } } public class Root { private int prop; #if LEGACY_CSC public int Prop { get { return prop; } } #else public int Prop => prop; #endif public void M(T a) { } } public abstract class Base : Root { public new abstract int Prop { get; } public new abstract void M(T a); } public class Derived : Base { #if LEGACY_CSC public override int Prop { get { return ((Root)this).Prop; } } #else public override int Prop => ((Root)this).Prop; #endif public override void M(T a) { ((Root)this).M(a); } } private int fieldConflict; private int innerConflict; private static int PropertyValueParameterConflictsWithTypeName { get { return value.item; } set { QualifierTests.value.item = value; } } private int this[string[] Array] { get { System.Array.Sort(Array); return 0; } set { System.Array.Sort(Array); QualifierTests.value.item = value; } } private void NoParameters() { Delegate(Parameter); Delegate(StaticParameter); } private static void StaticNoParameteres() { } private void Parameter(object o) { } private static void StaticParameter(object o) { } private void DeclaringType() { } private static void DeclaringTypeStatic() { } private void conflictWithParameter() { } private void conflictWithVariable(int val) { } private void Conflicts(int conflictWithParameter) { this.conflictWithParameter(); } private void Conflicts() { int conflictWithVariable = 5; this.conflictWithVariable(conflictWithVariable); // workaround for missing identifiers in il Capturer(() => conflictWithVariable); } private void Capturing() { int fieldConflict = 5; Capturer(() => this.fieldConflict + fieldConflict); Capturer(delegate { int innerConflict = 5; return this.fieldConflict + fieldConflict + Capturer2(() => this.innerConflict + innerConflict + this.fieldConflict + fieldConflict); }); } private void Capturer(Func func) { } private int Capturer2(Func func) { return 0; } private void Delegate(Action action) { } private void ParameterConflictsWithTypeName(string[] Array) { System.Array.Sort(Array); } #if CS70 private void LocalConflictsWithLocalFunction() { int num = 0; LocalFunction(); void LocalFunction() { QualifierTests qualifierTests2 = qualifierTests(); i.Test(); Z(qualifierTests2); } QualifierTests qualifierTests() { num.ToString(); return new QualifierTests(new string[0]); } } private void Z(QualifierTests qualifierTests) { } #endif public QualifierTests(string[] Array) { System.Array.Sort(Array); } } internal static class ZExt { public static void Do(this int test) { } public static void Do(this object test) { } #if CS72 public static void Do(this ref DateTime test) { } #endif public static void Do2(this int test, DateTime date) { test.Do(); ((IEnumerable)null).Any(); ((object)null).Do(); #if CS72 date.Do(); #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs ================================================ // Copyright (c) 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 // 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; using System.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public struct Maybe { public T Value; public bool HasValue; } public static class MaybeExtensions { public static Maybe Select(this Maybe a, Func fn) { return default(Maybe); } public static Maybe Where(this Maybe a, Func predicate) { return default(Maybe); } } public class QueryExpressions { public class HbmParam { public string Name { get; set; } public string[] Text { get; set; } } public class Customer { public int CustomerID; public IEnumerable Orders; public string Name; public string Country; public string City; } public class Order { public int OrderID; public DateTime OrderDate; public Customer Customer; public int CustomerID; public decimal Total; public IEnumerable Details; } public class OrderDetail { public decimal UnitPrice; public int Quantity; } public IEnumerable customers; public IEnumerable orders; public object MultipleWhere() { return from c in customers where c.Orders.Count() > 10 where c.Country == "DE" select c; } public object SelectManyFollowedBySelect() { return from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }; } public object SelectManyFollowedByOrderBy() { return from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }; } public object MultipleSelectManyFollowedBySelect() { return from c in customers from o in c.Orders from d in o.Details select new { c.Name, o.OrderID, d.Quantity }; } public object MultipleSelectManyFollowedByLet() { return from c in customers from o in c.Orders from d in o.Details let x = (decimal)d.Quantity * d.UnitPrice select new { c.Name, o.OrderID, x }; } public object FromLetWhereSelect() { return from o in orders let t = o.Details.Sum((OrderDetail d) => d.UnitPrice * (decimal)d.Quantity) where t >= 1000m select new { OrderID = o.OrderID, Total = t }; } public object MultipleLet() { return from a in customers let b = a.Country let c = a.Name select b + c; } public object HibernateApplyGeneratorQuery() { return (from pi in customers.GetType().GetProperties() let pname = pi.Name let pvalue = pi.GetValue(customers, null) select new HbmParam { Name = pname, Text = new string[1] { (pvalue == null) ? "null" : pvalue.ToString() } }).ToArray(); } public object Join() { return from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }; } public object JoinInto() { return from c in customers join o in orders on c.CustomerID equals o.CustomerID into co let n = co.Count() where n >= 10 select new { Name = c.Name, OrderCount = n }; } public object OrderBy() { return from o in orders orderby o.Customer.Name, o.Total descending select o; } public object GroupBy() { return from c in customers group c.Name by c.Country; } public object ExplicitType() { return from Customer c in customers where c.City == "London" select c; } public object QueryContinuation() { return from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }; } public object Issue437(bool[] bools) { return from x in bools where x select (x); } #if CS60 private List Issue2545(List arglist) { return arglist?.OrderByDescending((string f) => f.Length).ThenBy((string f) => f.ToLower()).ToList(); } #endif public static IEnumerable Issue1310a(bool test) { #if ROSLYN && OPT IEnumerable obj = (test ? (from c in Enumerable.Range(0, 255) where char.IsLetter((char)c) select (char)c) : (from c in Enumerable.Range(0, 255) where char.IsDigit((char)c) select (char)c)); return obj.Concat(obj); #else IEnumerable enumerable = (test ? (from c in Enumerable.Range(0, 255) where char.IsLetter((char)c) select (char)c) : (from c in Enumerable.Range(0, 255) where char.IsDigit((char)c) select (char)c)); return enumerable.Concat(enumerable); #endif } public static Maybe Cast(Maybe a) where TB : class { return from m in a let t = m as TB where t != null select t; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Readme.txt ================================================ The files in this folder are prettiness tests for the decompiler. The NUnit class running these tests is ../PrettyTestRunner.cs. Each test case is a C# file. The test runner will: 1. Compile the file into an .exe/.dll 2. Decompile the .exe/.dll 3. Compare the resulting code with the original input code. The tests pass if the code looks exactly the same as the input code, ignoring comments, empty lines and preprocessor directives. It also ignores disabled preprocessors sections (e.g. "#if ROSLYN") when the test runs with a compiler that does not set this symbol. See Tester.GetPreprocessorSymbols() for the available symbols. ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs ================================================ using System; #if CS100 using System.Runtime.InteropServices; #endif namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class RecordClasses { public record Base(string A); public record CopyCtor(string A) { protected CopyCtor(CopyCtor _) { } } public record Derived(int B) : Base(B.ToString()); public record BaseRecordWithObject(object Id); public record DerivedRecordWithString : BaseRecordWithObject { public DerivedRecordWithString(string Id) : base(Id) { } } public record Empty; public record EmptyWithStaticField { public static readonly Empty X = new Empty(); } public record Fields { public int A; public double B = 1.0; public object C; public dynamic D; public string S = "abc"; } public record Interface(int B) : IRecord; public interface IRecord { } public record Pair { public A First { get; init; } public B Second { get; init; } } public record PairWithPrimaryCtor(A First, B Second); public record PrimaryCtor(int A, string B); public record PrimaryCtorWithAttribute([RecordTest("param")][property: RecordTest("property")][field: RecordTest("field")] int a); public record PrimaryCtorWithField(int A, string B) { public double C = 1.0; public string D = A + B; } public record PrimaryCtorWithInParameter(in int A, in string B); public record PrimaryCtorWithProperty(int A, string B) { public double C { get; init; } = 1.0; public string D { get; } = A + B; } public record Properties { public int A { get; set; } public int B { get; } public int C => 43; public object O { get; set; } public string S { get; set; } public dynamic D { get; set; } public Properties() { B = 42; } } [AttributeUsage(AttributeTargets.All)] public class RecordTestAttribute : Attribute { public RecordTestAttribute(string name) { } } public sealed record Sealed(string A); public sealed record SealedDerived(int B) : Base(B.ToString()); public class WithExpressionTests { public Fields Test(Fields input) { return input with { A = 42, B = 3.141, C = input }; } public Fields Test2(Fields input) { return input with { A = 42, B = 3.141, C = input with { A = 43 } }; } } public abstract record WithNestedRecords { public record A : WithNestedRecords { public override string AbstractProp => "A"; } public record B : WithNestedRecords { public override string AbstractProp => "B"; public int? Value { get; set; } } public record DerivedGeneric : Pair where T : struct { public bool Flag; } public abstract string AbstractProp { get; } } public abstract record BaseRecord { public string Name { get; } public object Value { get; } public bool Encode { get; } protected BaseRecord(string name, object value, bool encode) { Name = name; Value = value; Encode = encode; } } public record DerivedRecord(string name, object value, bool encode = true) : BaseRecord(string.IsNullOrEmpty(name) ? "name" : name, value, encode); public record DefaultValuesRecord() : DerivedRecord("default", 42, encode: false); public record RecordWithProtectedMember(int Value) { protected int Double => Value * 2; } public record InheritedRecordWithAdditionalMember(int Value) : RecordWithProtectedMember(Value) { public int MoreData { get; set; } } public record InheritedRecordWithAdditionalParameter(int Value, int Value2) : RecordWithProtectedMember(Value); public record BaseWithString(string S); public record DerivedWithAdditionalInt(int I) : BaseWithString(I.ToString()); public record DerivedWithNoAdditionalProperty(string S) : BaseWithString(S); public record DerivedWithAdditionalProperty(string S2) : BaseWithString(S2); public record DerivedWithAdditionalPropertyDifferentAccessor(string S) : BaseWithString(S) { public string S2 { get; set; } = S; } public record MultipleCtorsChainedNoPrimaryCtor { public int A { get; init; } public string B { get; init; } public double C { get; init; } public MultipleCtorsChainedNoPrimaryCtor(double c) { A = 0; B = null; C = c; } public MultipleCtorsChainedNoPrimaryCtor(int a) : this(3.14) { A = a; } public MultipleCtorsChainedNoPrimaryCtor(string b) : this(4.13) { B = b; } } public record UnexpectedCodeInCtor { public int A { get; init; } public string B { get; init; } public UnexpectedCodeInCtor(int A, string B) { this.A = A; this.B = B; Console.WriteLine(); } public UnexpectedCodeInCtor(int A) : this(A, null) { this.A = A; } } private record RecordWithMultipleInitializerAssignmentsInPrimaryCtor(string name, string? value, in object encode) { public string? Value { get; } = name; public string Name { get; } = value; private string? WebValue { get; } = (name != null) ? "111" : value; private string? WebValue2; } } #if CS100 internal class RecordStructs { public record struct Base(string A); public record CopyCtor(string A) { protected CopyCtor(CopyCtor _) { } } [StructLayout(LayoutKind.Sequential, Size = 1)] public record struct Empty; public record struct Fields { public int A; public double B; public object C; public dynamic D; public string S; } public record struct Interface(int B) : IRecord; public interface IRecord { } public record struct Pair { public A First { get; init; } public B Second { get; init; } } public record struct PairWithPrimaryCtor(A First, B Second); public record struct PrimaryCtor(int A, string B); public record struct MultipleCtorsNoPrimaryCtor { public Guid Id { get; } public MultipleCtorsNoPrimaryCtor() { Id = Guid.NewGuid(); } public MultipleCtorsNoPrimaryCtor(Guid id) { Id = id; } } public record struct MultipleCtorsChainedNoPrimaryCtor { public int A { get; init; } public string B { get; init; } public double C { get; init; } public MultipleCtorsChainedNoPrimaryCtor(double c) { A = 0; B = null; C = c; } public MultipleCtorsChainedNoPrimaryCtor(int a) : this(3.14) { A = a; } public MultipleCtorsChainedNoPrimaryCtor(string b) : this(4.13) { B = b; } } public record struct PrimaryCtorWithAttribute([RecordTest("param")][property: RecordTest("property")][field: RecordTest("field")] int a); public record struct PrimaryCtorWithField(int A, string B) { public double C = 1.0; public string D = A + B; } public record struct PrimaryCtorWithInParameter(in int A, in string B); public record struct PrimaryCtorWithProperty(int A, string B) { public double C { get; init; } = 1.0; public string D { get; } = A + B; } public record struct Properties { public int A { get; set; } public int B { get; } public int C => 43; public object O { get; set; } public string S { get; set; } public dynamic D { get; set; } public Properties() { A = 41; B = 42; O = null; S = "Hello"; D = null; } } public record struct PropertiesWithInitializers() { public int A { get; set; } = 41; public int B { get; } = 42; public int C => 43; public object O { get; set; } = null; public string S { get; set; } = "Hello"; public dynamic D { get; set; } = null; } [AttributeUsage(AttributeTargets.All)] public class RecordTestAttribute : Attribute { public RecordTestAttribute(string name) { } } public class WithExpressionTests { public Fields Test(Fields input) { return input with { A = 42, B = 3.141, C = input }; } public Fields Test2(Fields input) { return input with { A = 42, B = 3.141, C = input with { A = 43 } }; } } #if CS110 public record struct WithRequiredMembers { public int A { get; set; } public required double B { get; set; } public object C; public required dynamic D; } #endif public record struct RecordWithMultipleCtors { public int A { get; set; } public RecordWithMultipleCtors() { A = 42; } public RecordWithMultipleCtors(int A) { this.A = A; } public RecordWithMultipleCtors(string A) { this.A = int.Parse(A); } } public record struct RecordCtorChain(int A, string B) { #if EXPECTED_OUTPUT public double C = 0.0; #else public double C; #endif public RecordCtorChain(int A) : this(A, "default") { C = 3.14; } public RecordCtorChain(string B) : this(42, B) { C = 1.41; } } } #endif } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs ================================================ using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public abstract class ReduceNesting { public abstract bool B(int i); public abstract int I(int i); public void IfIf() { if (B(0)) { Console.WriteLine(0); return; } if (B(1)) { Console.WriteLine(1); } Console.WriteLine("end"); } public void IfSwitch() { if (B(0)) { Console.WriteLine(0); return; } Console.WriteLine("switch"); switch (I(0)) { case 0: Console.WriteLine("case 0"); break; case 1: Console.WriteLine("case 1"); break; default: Console.WriteLine("end"); break; } } public void IfSwitchSwitch() { if (B(0)) { Console.WriteLine(0); return; } Console.WriteLine("switch 0"); switch (I(1)) { case 0: Console.WriteLine("case 0"); return; case 1: Console.WriteLine("case 1"); return; } Console.WriteLine("switch 1"); switch (I(1)) { case 0: Console.WriteLine("case 0"); break; case 1: Console.WriteLine("case 1"); break; default: Console.WriteLine("end"); break; } } public void IfLoop() { if (B(0)) { Console.WriteLine(0); return; } for (int i = 0; i < 10; i++) { Console.WriteLine(i); } Console.WriteLine("end"); } public void LoopContinue() { for (int i = 0; i < 10; i++) { Console.WriteLine(i); if (B(0)) { Console.WriteLine(0); continue; } if (B(1)) { Console.WriteLine(1); } Console.WriteLine("loop-tail"); } } public void LoopBreak() { for (int i = 0; i < 10; i++) { Console.WriteLine(i); if (B(0)) { Console.WriteLine(0); continue; } if (B(1)) { Console.WriteLine(1); break; } if (B(2)) { Console.WriteLine(2); } Console.WriteLine("break"); break; } Console.WriteLine("end"); } public void LoopBreakElseIf() { for (int i = 0; i < 10; i++) { Console.WriteLine(i); if (B(0)) { Console.WriteLine(0); continue; } if (B(1)) { Console.WriteLine(1); } else if (B(2)) { Console.WriteLine(2); } break; } Console.WriteLine("end"); } public void SwitchIf() { switch (I(0)) { case 0: Console.WriteLine("case 0"); return; case 1: Console.WriteLine("case 1"); return; } if (B(0)) { Console.WriteLine(0); } Console.WriteLine("end"); } public void NestedSwitchIf() { if (B(0)) { switch (I(0)) { case 0: Console.WriteLine("case 0"); return; case 1: Console.WriteLine("case 1"); return; } if (B(1)) { Console.WriteLine(1); } } else { Console.WriteLine("else"); } } // nesting should not be reduced as maximum nesting level is 1 public void EarlyExit1() { if (!B(0)) { for (int i = 0; i < 10; i++) { Console.WriteLine(i); } Console.WriteLine("end"); } } // nesting should be reduced as maximum nesting level is 2 public void EarlyExit2() { if (B(0)) { return; } for (int i = 0; i < 10; i++) { Console.WriteLine(i); if (i % 2 == 0) { Console.WriteLine("even"); } } Console.WriteLine("end"); } // nesting should not be reduced as maximum nesting level is 1 and the else block has no more instructions than any other block public void BalancedIf() { if (B(0)) { Console.WriteLine("true"); if (B(1)) { Console.WriteLine(1); } } else { if (B(2)) { Console.WriteLine(2); } Console.WriteLine("false"); } } public string ComplexCase1(string s) { if (B(0)) { return s; } for (int i = 0; i < s.Length; i++) { if (B(1)) { Console.WriteLine(1); } else if (B(2)) { switch (i) { case 1: if (B(3)) { Console.WriteLine(3); break; } Console.WriteLine("case1"); if (B(4)) { Console.WriteLine(4); } break; case 2: case 3: Console.WriteLine("case23"); break; } Console.WriteLine(2); } else if (B(5)) { Console.WriteLine(5); } else { if (B(6)) { Console.WriteLine(6); } Console.WriteLine("else"); } } return s; } public void EarlyExitBeforeTry() { if (B(0)) { return; } try { if (B(1)) { Console.WriteLine(); } } catch { } } public void EarlyExitInTry() { try { if (B(0)) { return; } Console.WriteLine(); if (B(1)) { for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } } catch { } } public void ContinueLockInLoop() { while (B(0)) { lock (Console.Out) { if (B(1)) { continue; } Console.WriteLine(); if (B(2)) { for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } } } } public void BreakLockInLoop() { while (B(0)) { lock (Console.Out) { // Before ReduceNestingTransform, the rest of the lock body is nested in if(!B(1)) with a continue; // the B(1) case falls through to a break outside the lock if (B(1)) { break; } Console.WriteLine(); if (B(2)) { for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } // the break gets duplicated into the lock (replacing the leave) making the lock 'endpoint unreachable' and the break outside the lock is removed // After the condition is inverted, ReduceNestingTransform isn't smart enough to then move the continue out of the lock // Thus the redundant continue; continue; } } Console.WriteLine(); } public unsafe void BreakPinnedInLoop(int[] arr) { while (B(0)) { fixed (int* ptr = arr) { if (B(1)) { break; } Console.WriteLine(); if (B(2)) { for (int i = 0; i < 10; i++) { Console.WriteLine(ptr[i]); } } // Same reason as BreakLockInLoop continue; } } Console.WriteLine(); } public void CannotEarlyExitInTry() { try { if (B(0)) { Console.WriteLine(); if (B(1)) { for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } } } catch { } Console.WriteLine(); } public void EndpointUnreachableDueToEarlyExit() { using (Console.Out) { if (B(0)) { return; } do { if (B(1)) { return; } } while (B(2)); throw new Exception(); } } public void SwitchInTry() { try { switch (I(0)) { case 1: Console.WriteLine(1); return; case 2: Console.WriteLine(2); return; } Console.WriteLine(3); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } catch { throw; } } public void SwitchInTryInLoopReturn() { for (int i = 0; i < 10; i++) { try { switch (I(0)) { case 1: Console.WriteLine(1); return; case 2: Console.WriteLine(2); return; } Console.WriteLine(3); for (int j = 0; j < 10; j++) { Console.WriteLine(j); } } catch { throw; } } } public void SwitchInTryInLoopContinue() { for (int i = 0; i < 10; i++) { try { switch (I(0)) { case 1: Console.WriteLine(1); continue; case 2: Console.WriteLine(2); continue; } Console.WriteLine(3); for (int j = 0; j < 10; j++) { Console.WriteLine(j); } } catch { throw; } } } private static string ShouldNotDuplicateReturnStatementIntoTry(IDictionary dict) { string value; lock (dict) { if (!dict.TryGetValue(1, out value)) { value = "test"; dict.Add(1, value); } } return value; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class LifetimeTests { private static int staticField; public Span CreateWithoutCapture(scoped ref int value) { // Okay: value is not captured return new Span(ref staticField); } public Span CreateAndCapture(ref int value) { // Okay: value Rule 3 specifies that the safe-to-escape be limited to the ref-safe-to-escape // of the ref argument. That is the *calling method* for value hence this is not allowed. return new Span(ref value); } public Span ScopedRefSpan(scoped ref Span span) { return span; } public Span ScopedSpan(scoped Span span) { return default(Span); } public void OutSpan(out Span span) { span = default(Span); } public void Calls() { int value = 0; Span span = CreateWithoutCapture(ref value); //span = CreateAndCapture(ref value); -- would need scoped local, not yet implemented span = ScopedRefSpan(ref span); span = ScopedSpan(span); OutSpan(out span); } } internal ref struct RefFields { public ref int Field0; public ref readonly int Field1; public readonly ref int Field2; public readonly ref readonly int Field3; public int PropertyAccessingRefFieldByValue { get { return Field0; } set { Field0 = value; } } public ref int PropertyReturningRefFieldByReference => ref Field0; public void Uses(int[] array) { Field1 = ref array[0]; Field2 = array[0]; } public void ReadonlyLocal() { ref readonly int field = ref Field1; Console.WriteLine("No inlining"); field.ToString(); } public RefFields(ref int v) { Field0 = ref v; Field1 = ref v; Field2 = ref v; Field3 = ref v; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal static class Ext { public static void ExtOnRef(this ref RefLocalsAndReturns.NormalStruct s) { } public static void ExtOnIn(this in RefLocalsAndReturns.NormalStruct s) { } public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyStruct s) { } public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyStruct s) { } public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyRefStruct s) { } public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyRefStruct s) { } } internal class RefLocalsAndReturns { public struct Issue1630 { private object data; private int next; public static void Test() { Issue1630[] array = new Issue1630[1]; int num = 0; while (num >= 0) { ref Issue1630 reference = ref array[num]; Console.WriteLine(reference.data); num = reference.next; } } } public delegate ref T RefFunc(); public delegate ref readonly T ReadOnlyRefFunc(); public delegate ref TReturn RefFunc(T1 param1); public ref struct RefStruct { private int dummy; } public readonly ref struct ReadOnlyRefStruct { private readonly int dummy; } public struct NormalStruct { private readonly int dummy; private int[] arr; public int Property { get { return 1; } set { } } #if CS80 public readonly int ReadOnlyProperty { get { return 1; } set { } } public int PropertyWithReadOnlyGetter { readonly get { return 1; } set { } } public int PropertyWithReadOnlySetter { get { return 1; } readonly set { } } public readonly int ReadOnlyPropertyWithOnlyGetter { get { Console.WriteLine("No inlining"); return 1; } } public ref int RefProperty => ref arr[0]; public ref readonly int RefReadonlyProperty => ref arr[0]; public readonly ref int ReadonlyRefProperty => ref arr[0]; public readonly ref readonly int ReadonlyRefReadonlyProperty => ref arr[0]; #endif public ref readonly int this[in int index] => ref arr[index]; public event EventHandler NormalEvent; #if CS80 public readonly event EventHandler ReadOnlyEvent { add { } remove { } } #endif public void Method() { } #if CS80 public readonly void ReadOnlyMethod() { } #endif } public readonly struct ReadOnlyStruct { private readonly int Field; public void Method() { ref readonly int field = ref Field; Console.WriteLine("No inlining"); Console.WriteLine(field.GetHashCode()); } public void RefReadonlyCallVirt(RefLocalsAndReturns provider) { ref readonly NormalStruct readonlyRefInstance = ref provider.GetReadonlyRefInstance(); Console.WriteLine("No inlining"); readonlyRefInstance.Method(); } } private static int[] numbers = new int[10] { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 }; private static string[] strings = new string[2] { "Hello", "World" }; private static string NullString = ""; private static int DefaultInt = 0; public static ref T GetRef() { throw new NotImplementedException(); } public static ref readonly T GetReadonlyRef() { throw new NotImplementedException(); } public ref readonly T GetReadonlyRefInstance() { throw new NotImplementedException(); } public void CallOnRefReturn() { // Both direct calls: GetRef().Method(); GetRef().Method(); // call on a copy, not the original ref: NormalStruct normalStruct = GetRef(); normalStruct.Method(); ReadOnlyStruct readOnlyStruct = GetRef(); readOnlyStruct.Method(); } public void CallOnReadOnlyRefReturn() { // uses implicit temporary: GetReadonlyRef().Method(); // direct call: GetReadonlyRef().Method(); // call on a copy, not the original ref: ReadOnlyStruct readonlyRef = GetReadonlyRef(); readonlyRef.Method(); } public void CallOnInParam(in NormalStruct ns, in ReadOnlyStruct rs) { // uses implicit temporary: ns.Method(); // direct call: rs.Method(); // call on a copy, not the original ref: ReadOnlyStruct readOnlyStruct = rs; readOnlyStruct.Method(); } public void M(in DateTime a = default(DateTime)) { } public void M2(in T a = default(T)) { } public void M3(in T? a = null) where T : struct { } public static TReturn Invoker(RefFunc action, T1 value) { return action(value); } public static ref int FindNumber(int target) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] >= target) { return ref numbers[i]; } } return ref numbers[0]; } public static ref int LastNumber() { return ref numbers[numbers.Length - 1]; } public static ref int ElementAtOrDefault(int index) { if (index >= 0 && index < numbers.Length) { return ref numbers[index]; } return ref DefaultInt; } public static ref int LastOrDefault() { if (numbers.Length != 0) { return ref numbers[numbers.Length - 1]; } return ref DefaultInt; } public static void DoubleNumber(ref int num) { Console.WriteLine("old: " + num); num *= 2; Console.WriteLine("new: " + num); } public static ref string GetOrSetString(int index) { if (index < 0 || index >= strings.Length) { return ref NullString; } return ref strings[index]; } public void CallSiteTests(NormalStruct s, ReadOnlyStruct r, ReadOnlyRefStruct rr) { s.ExtOnIn(); s.ExtOnRef(); r.ExtOnIn(); r.ExtOnRef(); rr.ExtOnIn(); rr.ExtOnRef(); CallOnInParam(in s, in r); } public void RefReassignment(ref NormalStruct s) { ref NormalStruct reference = ref GetRef(); RefReassignment(ref reference); if (s.GetHashCode() == 0) { reference = ref GetRef(); } RefReassignment(ref reference.GetHashCode() == 4 ? ref reference : ref s); } public static void Main(string[] args) { DoubleNumber(ref args.Length == 1 ? ref numbers[0] : ref DefaultInt); DoubleNumber(ref FindNumber(32)); Console.WriteLine(string.Join(", ", numbers)); DoubleNumber(ref LastNumber()); Console.WriteLine(string.Join(", ", numbers)); Console.WriteLine(GetOrSetString(0)); GetOrSetString(0) = "Goodbye"; Console.WriteLine(string.Join(" ", strings)); GetOrSetString(5) = "Here I mutated the null value!?"; Console.WriteLine(GetOrSetString(-5)); Console.WriteLine(Invoker((int x) => ref numbers[x], 0)); Console.WriteLine(LastOrDefault()); LastOrDefault() = 10000; Console.WriteLine(ElementAtOrDefault(-5)); } #if CS120 public ref readonly int M(in int x) { return ref x; } public ref readonly int M2(ref readonly int x) { return ref x; } public void Test() { int x = 32; M(in x); M2(in x); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs ================================================ // Copyright (c) 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 // 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. // compile: // csc ShortCircuit.cs /t:Library && ildasm /text ShortCircuit.dll >ShortCircuit.il // csc ShortCircuit.cs /t:Library /o /out:ShortCircuit.opt.dll && ildasm /text ShortCircuit.opt.dll >ShortCircuit.opt.il namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public abstract class ShortCircuit { public abstract void B(bool b); public abstract bool F(int i); public abstract int GetInt(int i); public abstract void M1(); public abstract void M2(); public abstract void E(); public void ExprAnd() { B(F(0) && F(1)); } public void ExprOr() { B(F(0) || F(1)); } public void ExprCond() { B(F(0) ? F(1) : F(2)); } public void ExprCondAnd() { B((F(0) && F(1)) ? F(2) : F(3)); } public void ExprMix4A() { B(((F(0) || F(1)) && F(2)) || F(3)); } public void ExprMix4B() { B((F(0) || F(1)) && (F(2) || F(3))); } public void ExprMix4C() { B((F(0) && F(1)) || (F(2) && F(3))); } public void StmtAnd2() { if (F(0) && F(1)) { M1(); } else { M2(); } E(); } public void StmtOr2A() { if (F(0) || F(1)) { M1(); } } public void StmtOr2B() { if (F(0) || F(1)) { M1(); } else { M2(); } E(); } public void StmtAnd3() { if (F(0) && F(1) && F(2)) { M1(); } else { M2(); } E(); } public void StmtOr3() { if (F(0) || F(1) || F(2)) { M1(); } else { M2(); } E(); } public void StmtOr4() { if (GetInt(0) != 0 || GetInt(1) != 0) { M1(); } else { M2(); } E(); } public void StmtMix3A() { if ((F(0) || F(1)) && F(2)) { M1(); } } public void StmtMix3B() { if ((F(0) || F(1)) && F(2)) { M1(); } else { M2(); } } public void StmtMix4V1A() { if (((F(0) || F(1)) && F(2)) || F(3)) { M1(); } } public void StmtMix4V1B() { if (((F(0) || F(1)) && F(2)) || F(3)) { M1(); } else { M2(); } } public void StmtMix4V2A() { if ((F(0) || F(1)) && (F(2) || F(3))) { M1(); } } public void StmtMix4V2B() { if ((F(0) || F(1)) && (F(2) || F(3))) { M1(); } else { M2(); } } public void StmtMix4V3A() { if ((F(0) && F(1)) || (F(2) && F(3))) { M1(); } } public void StmtMix4V3B() { if ((F(0) && F(1)) || (F(2) && F(3))) { M1(); } else { M2(); } } public void StmtComplex() { if (F(0) && F(1) && !F(2) && (F(3) || F(4))) { M1(); } else { M2(); } E(); } public void StmtComplex2(int i) { if (i > 1000 || (i >= 1 && i <= 8) || i == 42) { M1(); } else { M2(); } E(); } public void StmtComplex3(int i) { if (i > 1000 || (i >= 1 && i <= 8) || (i >= 100 && i <= 200) || i == 42) { M1(); } else { M2(); } E(); } public void StmtComplex4(int i) { if (i > 1000 || (i >= 1 && i <= 8) || i == 42 || i == 23) { M1(); } else { M2(); } E(); } public void StmtComplex5() { if (F(0)) { if (!F(1) && !F(2)) { return; } } else if (!F(3) || !F(4)) { M2(); return; } E(); } public int StmtComplex6() { if (F(0)) { M1(); if (F(1) || F(2)) { return 1; } } return 2; } public int InferCorrectOrder() { if (F(1) || F(2)) { return 1; } return 2; } #if !OPT public void EmptyIf() { if (F(0)) { } if (!F(1)) { } if (F(4) || F(5)) { } E(); } #endif public void PreferLogicalToBitwise(bool a, bool b, int i, float f) { B(a && b); B(a && i == 1); B(i == 1 && a); B(i > i - 3 && a); B(f < 0.1f && a); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/StaticAbstractInterfaceMembers.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.StaticAbstractInterfaceMembers { internal class C : I { private string _s; static C I.P { get; set; } static event Action I.E { add { } remove { } } public C(string s) { _s = s; } static void I.M(object x) { Console.WriteLine("Implementation"); } static C I.operator +(C l, C r) { return new C(l._s + " " + r._s); } static bool I.operator ==(C l, C r) { return l._s == r._s; } static bool I.operator !=(C l, C r) { return l._s != r._s; } static implicit I.operator C(string s) { return new C(s); } static explicit I.operator string(C c) { return c._s; } } internal interface I where T : I { static abstract T P { get; set; } static abstract event Action E; static abstract void M(object x); static abstract T operator +(T l, T r); static abstract bool operator ==(T l, T r); static abstract bool operator !=(T l, T r); static abstract implicit operator T(string s); static abstract explicit operator string(T t); } public interface IAmSimple { static abstract int Capacity { get; } static abstract int Count { get; set; } static abstract int SetterOnly { set; } static abstract event EventHandler E; static abstract IAmSimple CreateI(); } internal interface IAmStatic where T : IAmStatic { static int f = 42; static T P { get; set; } static event Action E; static void M(object x) { } static IAmStatic operator +(IAmStatic l, IAmStatic r) { throw new NotImplementedException(); } } internal interface IAmVirtual where T : IAmVirtual { static virtual T P { get; set; } static virtual event Action E; static virtual void M(object x) { } static virtual T operator +(T l, T r) { throw new NotImplementedException(); } static virtual implicit operator T(string s) { return default(T); } static virtual explicit operator string(T t) { return null; } } internal class Uses { public static T TestVirtualStaticUse(T a, T b) where T : IAmVirtual { T.P = a; a = "World"; T.E += null; T.E -= null; T.M("Hello"); UseString((string)b); return a + b; } public static IAmStatic TestStaticUse(T a, T b) where T : IAmStatic { IAmStatic.f = 11; IAmStatic.P = a; IAmStatic.E += null; IAmStatic.E -= null; IAmStatic.M("Hello"); return a + b; } public static I TestAbstractStaticUse(T a, T b) where T : I { T.P = a; a = "World"; T.E += null; T.E -= null; T.M("Hello"); UseString((string)b); return a + b; } private static void UseString(string a) { } } public class X : IAmSimple { public static int Capacity { get; } public static int Count { get; set; } public static int SetterOnly { set { } } public static event EventHandler E; public static IAmSimple CreateI() { return new X(); } } public class X2 : IAmSimple { public static int Capacity { get { throw new NotImplementedException(); } } public static int Count { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public static int SetterOnly { set { throw new NotImplementedException(); } } public static event EventHandler E { add { throw new NotImplementedException(); } remove { throw new NotImplementedException(); } } public static IAmSimple CreateI() { throw new NotImplementedException(); } } internal class ZOperatorTest { public interface IGetNext where T : IGetNext { static abstract T operator ++(T other); } public struct WrappedInteger : IGetNext { public int Value; public static WrappedInteger operator ++(WrappedInteger other) { WrappedInteger result = other; result.Value++; return result; } } public void GenericUse(T t) where T : IGetNext { ++t; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/StringInterpolation.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class StringInterpolation { public static void Main(string[] args) { } public static void General(string[] args) { Console.WriteLine($"{args.Length}"); Console.WriteLine($"a{{0{args.Length}"); Console.WriteLine($"{args.Length:x}"); Console.WriteLine($"\ta{args.Length}b"); Console.WriteLine($"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}"); Console.WriteLine($"\ta{((args.Length != 0) ? 5 : 0)}"); Console.WriteLine($"\ta{args ?? args}"); Console.WriteLine($"\ta{args[0][0] == 'a'}"); Console.WriteLine($"\ta{$"a{args.Length}" == args[0]}"); Console.WriteLine($"\ta{args.Length}}}"); Console.WriteLine($"{args.Length,5:x}"); Console.WriteLine($"{args.Length,5}"); } public static void Types() { Console.WriteLine($"{(int)Get()}"); } public static void ArrayExpansionSpecialCases(object[] args) { Console.WriteLine($"args: {args}"); Console.WriteLine(string.Format("args: {0}", args)); } public static void InvalidFormatString(string[] args) { #pragma warning disable IDE0043 Console.WriteLine(string.Format("", args.Length)); Console.WriteLine(string.Format("a", args.Length)); Console.WriteLine(string.Format("}", args.Length)); Console.WriteLine(string.Format("{", args.Length)); Console.WriteLine(string.Format(":", args.Length)); Console.WriteLine(string.Format("\t", args.Length)); Console.WriteLine(string.Format("\\", args.Length)); Console.WriteLine(string.Format("\"", args.Length)); Console.WriteLine(string.Format("aa", args.Length)); Console.WriteLine(string.Format("a}", args.Length)); Console.WriteLine(string.Format("a{", args.Length)); Console.WriteLine(string.Format("a:", args.Length)); Console.WriteLine(string.Format("a\t", args.Length)); Console.WriteLine(string.Format("a\\", args.Length)); Console.WriteLine(string.Format("a\"", args.Length)); Console.WriteLine(string.Format("a{:", args.Length)); Console.WriteLine(string.Format("a{0", args.Length)); Console.WriteLine(string.Format("a{{0", args.Length)); Console.WriteLine(string.Format("}a{{0", args.Length)); Console.WriteLine(string.Format("}{", args.Length)); Console.WriteLine(string.Format("{}", args.Length)); Console.WriteLine(string.Format("{0:}", args.Length)); Console.WriteLine(string.Format("{0{a}0}", args.Length)); Console.WriteLine(string.Format("test: {0}", string.Join(",", args))); Console.WriteLine(string.Format("test: {0}}", args.Length)); #pragma warning restore } public void FormattableStrings(FormattableString s, string[] args) { s = $"{args.Length}"; s = $"a{{0{args.Length}"; s = $"{args.Length:x}"; s = $"\ta{args.Length}b"; s = $"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}"; s = $"\ta{((args.Length != 0) ? 5 : 0)}"; s = $"\ta{args ?? args}"; s = $"\ta{args[0][0] == 'a'}"; s = $"\ta{$"a{args.Length}" == args[0]}"; RequiresCast($"{args.Length}"); RequiresCast($"a{{0{args.Length}"); RequiresCast($"{args.Length:x}"); RequiresCast($"\ta{args.Length}b"); RequiresCast($"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}"); RequiresCast($"\ta{((args.Length != 0) ? 5 : 0)}"); RequiresCast($"\ta{args ?? args}"); RequiresCast($"\ta{args[0][0] == 'a'}"); RequiresCast($"\ta{$"a{args.Length}" == args[0]}"); RequiresCast((FormattableString)$"{args.Length}"); RequiresCast((FormattableString)$"a{{0{args.Length}"); RequiresCast((FormattableString)$"{args.Length:x}"); RequiresCast((FormattableString)$"\ta{args.Length}b"); RequiresCast((FormattableString)$"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}"); RequiresCast((FormattableString)$"\ta{((args.Length != 0) ? 5 : 0)}"); RequiresCast((FormattableString)$"\ta{args ?? args}"); RequiresCast((FormattableString)$"\ta{args[0][0] == 'a'}"); RequiresCast((FormattableString)$"\ta{$"a{args.Length}" == args[0]}"); RequiresCast((IFormattable)$"{args.Length}"); RequiresCast((IFormattable)$"a{{0{args.Length}"); RequiresCast((IFormattable)$"{args.Length:x}"); RequiresCast((IFormattable)$"\ta{args.Length}b"); RequiresCast((IFormattable)$"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}"); RequiresCast((IFormattable)$"\ta{((args.Length != 0) ? 5 : 0)}"); RequiresCast((IFormattable)$"\ta{args ?? args}"); RequiresCast((IFormattable)$"\ta{args[0][0] == 'a'}"); RequiresCast((IFormattable)$"\ta{$"a{args.Length}" == args[0]}"); } public void Issue1497(string[] args) { Console.WriteLine($"args[0]: {args[0].Trim(':').Trim('&').Trim(':').Trim('&')} asdf {args.Length:x} test"); } public void RequiresCast(string value) { } public void RequiresCast(FormattableString value) { } public void RequiresCast(IFormattable value) { } public string ConcatStringCharSC(string s, char c) { return s + c; } public string ConcatStringCharCS(string s, char c) { return c + s; } public string ConcatStringCharSCS(string s, char c) { return s + c + s; } public string ConcatStringCharCSS(string s, char c) { return c + s + s; } public string ConcatStringCharCSSC(string s, char c) { return c + s + s + c; } public static TReturn Get() { return default(TReturn); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs ================================================ // Copyright (c) 2021 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.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct EmptyStruct { } public class Structs { #if CS100 public StructWithDefaultCtor M() { return default(StructWithDefaultCtor); } public StructWithDefaultCtor M2() { return new StructWithDefaultCtor(); } #endif } #if CS100 public struct StructWithDefaultCtor { public int X = 42; public StructWithDefaultCtor() { } } #endif #if CS110 public struct StructWithRequiredMembers { public required string FirstName; public required string LastName { get; set; } } #endif } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs ================================================ // Copyright (c) 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 // 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; using System.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class Switch { public class SetProperty { public readonly PropertyInfo Property; public int Set { get; set; } public SetProperty(PropertyInfo property) { Property = property; } } public class ImplicitString { private readonly string s; public ImplicitString(string s) { this.s = s; } public static implicit operator string(ImplicitString v) { return v.s; } } public class ExplicitString { private readonly string s; public ExplicitString(string s) { this.s = s; } public static explicit operator string(ExplicitString v) { return v.s; } } public class ImplicitInt { private readonly int s; public ImplicitInt(int s) { this.s = s; } public static implicit operator int(ImplicitInt v) { return v.s; } } public class ImplicitConversionConflictWithLong { private readonly int s; public ImplicitConversionConflictWithLong(int s) { this.s = s; } public static implicit operator int(ImplicitConversionConflictWithLong v) { return v.s; } public static implicit operator long(ImplicitConversionConflictWithLong v) { return v.s; } } public class ImplicitConversionConflictWithString { private readonly int s; public ImplicitConversionConflictWithString(int s) { this.s = s; } public static implicit operator int(ImplicitConversionConflictWithString v) { return v.s; } public static implicit operator string(ImplicitConversionConflictWithString v) { return string.Empty; } } public class ExplicitInt { private readonly int s; public ExplicitInt(int s) { this.s = s; } public static explicit operator int(ExplicitInt v) { return v.s; } } public enum State { False, True, Null } public enum KnownColor { DarkBlue, DarkCyan, DarkGoldenrod, DarkGray, DarkGreen, DarkKhaki } private static char ch1767; #if !ROSLYN public static State SwitchOverNullableBool(bool? value) { switch (value) { case false: return State.False; case true: return State.True; case null: return State.Null; default: throw new InvalidOperationException(); } } #endif public static bool? SwitchOverNullableEnum(State? state) { switch (state) { case State.False: return false; case State.True: return true; case State.Null: return null; default: throw new InvalidOperationException(); } } public static string SparseIntegerSwitch(int i) { Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: return "-10 mln"; case -100: return "-hundred"; case -1: return "-1"; case 0: return "0"; case 1: return "1"; case 2: return "2"; case 4: return "4"; case 100: return "hundred"; case 10000: return "ten thousand"; case 10001: return "ten thousand and one"; case int.MaxValue: return "int.MaxValue"; default: return "something else"; } } public static void SparseIntegerSwitch2(int i) { switch (i) { case 4: case 10: case 11: case 13: case 21: case 29: case 33: case 49: case 50: case 55: Console.WriteLine(); break; } } public static bool SparseIntegerSwitch3(int i) { switch (i) { case 0: case 10: case 11: case 12: case 100: case 101: case 200: return true; default: return false; } } public static string SwitchOverNullableInt(int? i) { switch (i) { case null: return "null"; case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "large"; } } public static string SwitchOverNullableIntNullCaseCombined(int? i) { switch (i) { case null: case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "large"; } } public static string SwitchOverNullableIntShifted(int? i) { switch (i + 5) { case null: return "null"; case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "large"; } } public static string SwitchOverNullableIntShiftedNullCaseCombined(int? i) { switch (i + 5) { case null: case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "large"; } } public static string SwitchOverNullableIntNoNullCase(int? i) { switch (i) { case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "other"; } } public static string SwitchOverNullableIntNoNullCaseShifted(int? i) { switch (i + 5) { case 0: return "zero"; case 5: return "five"; case 10: return "ten"; default: return "other"; } } public static void SwitchOverInt(int i) { switch (i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static void SwitchOverExplicitInt(ExplicitInt i) { switch ((int)i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static void SwitchOverImplicitInt(ImplicitInt i) { switch (i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static void SwitchOverImplicitIntConflictLong(ImplicitConversionConflictWithLong i) { switch ((int)i) { case 0: Console.WriteLine("zero"); break; case 5: Console.WriteLine("five"); break; case 10: Console.WriteLine("ten"); break; case 15: Console.WriteLine("fifteen"); break; case 20: Console.WriteLine("twenty"); break; case 25: Console.WriteLine("twenty-five"); break; case 30: Console.WriteLine("thirty"); break; } } public static void SwitchOverImplicitIntConflictString(ImplicitConversionConflictWithString i) { switch ((string)i) { case "0": Console.WriteLine("zero"); break; case "5": Console.WriteLine("five"); break; case "10": Console.WriteLine("ten"); break; case "15": Console.WriteLine("fifteen"); break; case "20": Console.WriteLine("twenty"); break; case "25": Console.WriteLine("twenty-five"); break; case "30": Console.WriteLine("thirty"); break; } } // SwitchDetection.UseCSharpSwitch requires more complex heuristic to identify this when compiled with Roslyn public static void CompactSwitchOverInt(int i) { switch (i) { case 0: case 1: case 2: Console.WriteLine("012"); break; case 3: Console.WriteLine("3"); break; default: Console.WriteLine("default"); break; } Console.WriteLine("end"); } public static string ShortSwitchOverString(string text) { Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; default: return "Default"; } } public static string ShortSwitchOverStringWithNullCase(string text) { Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text); switch (text) { case "First case": return "Text1"; case "Second case": return "Text2"; case null: return "null"; default: return "Default"; } } public static string SwitchOverString1(string text) { Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case null: return null; default: return "Default"; } } public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } public static string SwitchOverImplicitString(ImplicitString s) { switch (s) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } public static string SwitchOverExplicitString(ExplicitString s) { switch ((string)s) { case "First case": return "Text1"; case "Second case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case "Seventh case": return "Text7"; case "Eighth case": return "Text8"; case "Ninth case": return "Text9"; case "Tenth case": return "Text10"; case "Eleventh case": return "Text11"; default: return "Default"; } } #if !ROSLYN public static string SwitchOverBool(bool b) { Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; case false: return bool.FalseString; default: return null; } } #endif public static void SwitchInLoop(int i) { Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: Console.WriteLine("one"); break; case 2: Console.WriteLine("two"); break; //case 3: // Console.WriteLine("three"); // continue; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); Console.WriteLine("more code"); return; } i++; } } public static void SwitchWithGoto(int i) { Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: Console.WriteLine("one"); goto default; case 2: Console.WriteLine("two"); goto case 3; case 3: Console.WriteLine("three"); break; case 4: Console.WriteLine("four"); return; default: Console.WriteLine("default"); break; } Console.WriteLine("End of method"); } // Needs to be long enough to generate a hashtable public static void SwitchWithGotoString(string s) { Console.WriteLine("SwitchWithGotoString: " + s); switch (s) { case "1": Console.WriteLine("one"); goto default; case "2": Console.WriteLine("two"); goto case "3"; case "3": Console.WriteLine("three"); break; case "4": Console.WriteLine("four"); return; case "5": Console.WriteLine("five"); return; case "6": Console.WriteLine("six"); return; case "7": Console.WriteLine("seven"); return; case "8": Console.WriteLine("eight"); return; case "9": Console.WriteLine("nine"); return; default: Console.WriteLine("default"); break; } Console.WriteLine("End of method"); } public static void SwitchWithGotoComplex(string s) { Console.WriteLine("SwitchWithGotoComplex: " + s); switch (s) { case "1": Console.WriteLine("one"); goto case "8"; case "2": Console.WriteLine("two"); goto case "3"; case "3": Console.WriteLine("three"); if (s.Length != 2) { break; } goto case "5"; case "4": Console.WriteLine("four"); goto case "5"; case "5": Console.WriteLine("five"); goto case "8"; case "6": Console.WriteLine("six"); goto case "5"; case "8": Console.WriteLine("eight"); return; // add a default case so that case "7": isn't redundant default: Console.WriteLine("default"); break; // note that goto case "7" will decompile as break; // cases with a single break have the highest IL offset and are moved to the bottom case "7": break; } Console.WriteLine("End of method"); } private static SetProperty[] GetProperties() { return new SetProperty[0]; } public static void SwitchOnStringInForLoop() { List list = new List(); List list2 = new List(); SetProperty[] properties = GetProperties(); for (int i = 0; i < properties.Length; i++) { Console.WriteLine("In for-loop"); SetProperty setProperty = properties[i]; switch (setProperty.Property.Name) { case "Name1": setProperty.Set = 1; list.Add(setProperty); break; case "Name2": setProperty.Set = 2; list.Add(setProperty); break; case "Name3": setProperty.Set = 3; list.Add(setProperty); break; case "Name4": setProperty.Set = 4; list.Add(setProperty); break; case "Name5": case "Name6": list.Add(setProperty); break; default: list2.Add(setProperty); break; } } } public static void SwitchInTryBlock(string value) { try { switch (value.Substring(5)) { case "Name1": Console.WriteLine("1"); break; case "Name2": Console.WriteLine("Name_2"); break; case "Name3": Console.WriteLine("Name_3"); break; case "Name4": Console.WriteLine("No. 4"); break; case "Name5": case "Name6": Console.WriteLine("5+6"); break; default: Console.WriteLine("default"); break; } } catch (Exception) { Console.WriteLine("catch block"); } } public static void SwitchWithComplexCondition(string[] args) { switch ((args.Length == 0) ? "dummy" : args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } public static void SwitchWithArray(string[] args) { switch (args[0]) { case "a": Console.WriteLine("a"); break; case "b": Console.WriteLine("b"); break; case "c": Console.WriteLine("c"); break; case "d": Console.WriteLine("d"); break; } Console.WriteLine("end"); } public static void SwitchWithContinue1(int i, bool b) { while (true) { switch (i) { #if OPT case 1: continue; #endif case 0: if (b) { continue; } break; case 2: if (!b) { continue; } break; #if !OPT case 1: continue; #endif } Console.WriteLine(); } } // while condition, return and break cases public static void SwitchWithContinue2(int i, bool b) { while (i < 10) { switch (i) { case 0: if (b) { Console.WriteLine("0b"); continue; } Console.WriteLine("0!b"); break; case 2: #if OPT if (b) { Console.WriteLine("2b"); return; } Console.WriteLine("2!b"); continue; #else if (!b) { Console.WriteLine("2!b"); continue; } Console.WriteLine("2b"); return; #endif default: Console.WriteLine("default"); break; case 3: break; case 1: continue; } Console.WriteLine("loop-tail"); i++; } } // for loop version public static void SwitchWithContinue3(bool b) { for (int i = 0; i < 10; i++) { switch (i) { case 0: if (b) { Console.WriteLine("0b"); continue; } Console.WriteLine("0!b"); break; case 2: #if OPT if (b) { Console.WriteLine("2b"); return; } Console.WriteLine("2!b"); continue; #else if (!b) { Console.WriteLine("2!b"); continue; } Console.WriteLine("2b"); return; #endif default: Console.WriteLine("default"); break; case 3: break; case 1: continue; } Console.WriteLine("loop-tail"); } } // foreach version public static void SwitchWithContinue4(bool b) { foreach (int item in Enumerable.Range(0, 10)) { Console.WriteLine("loop: " + item); switch (item) { case 1: if (b) { continue; } break; case 3: if (!b) { continue; } return; case 4: Console.WriteLine(4); goto case 7; case 5: Console.WriteLine(5); goto default; case 6: if (b) { continue; } goto case 3; case 7: if (item % 2 == 0) { goto case 3; } if (!b) { continue; } goto case 8; case 8: if (b) { continue; } goto case 5; default: Console.WriteLine("default"); break; case 2: continue; } Console.WriteLine("break: " + item); } } // internal if statement, loop increment block not dominated by the switch head public static void SwitchWithContinue5(bool b) { for (int i = 0; i < 10; i++) { if (i < 5) { switch (i) { case 0: if (b) { Console.WriteLine("0b"); continue; } Console.WriteLine("0!b"); break; case 2: #if OPT if (b) { Console.WriteLine("2b"); return; } Console.WriteLine("2!b"); continue; #else if (!b) { Console.WriteLine("2!b"); continue; } Console.WriteLine("2b"); return; #endif default: Console.WriteLine("default"); break; case 3: break; case 1: continue; } Console.WriteLine("break-target"); } Console.WriteLine("loop-tail"); } } // do-while loop version public static void SwitchWithContinue6(int i, bool b) { do { switch (i) { case 0: if (!b) { Console.WriteLine("0!b"); break; } Console.WriteLine("0b"); // ConditionDetection doesn't recognise Do-While continues yet continue; case 2: if (b) { Console.WriteLine("2b"); return; } Console.WriteLine("2!b"); continue; default: Console.WriteLine("default"); break; case 3: break; case 1: continue; } Console.WriteLine("loop-tail"); } while (++i < 10); } // double break from switch to loop exit requires additional pattern matching in HighLevelLoopTransform public static void SwitchWithContinue7() { for (int num = 0; num >= 0; num--) { Console.WriteLine("loop-head"); switch (num) { default: Console.WriteLine("default"); break; case 0: continue; case 1: break; } break; } Console.WriteLine("end"); } public static void SwitchWithContinueInDoubleLoop() { bool value = false; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { switch (i + j) { case 1: case 3: case 5: case 7: case 11: case 13: case 17: break; default: continue; } value = true; break; } } Console.WriteLine(value); } public static void SwitchLoopNesting() { for (int i = 0; i < 10; i++) { switch (i) { case 0: Console.WriteLine(0); break; case 1: Console.WriteLine(1); break; default: if (i % 2 == 0) { while (i % 3 != 0) { Console.WriteLine(i++); } } Console.WriteLine(); break; } if (i > 4) { Console.WriteLine("high"); } else { Console.WriteLine("low"); } } } // These decompile poorly into switch statements and should be left as is #region Overagressive Switch Use #if ROSLYN || OPT public static void SingleIf1(int i, bool a) { if (i == 1 || (i == 2 && a)) { Console.WriteLine(1); } Console.WriteLine(2); } #endif public static void SingleIf2(int i, bool a, bool b) { if (i == 1 || (i == 2 && a) || (i == 3 && b)) { Console.WriteLine(1); } Console.WriteLine(2); } public static void SingleIf3(int i, bool a, bool b) { if (a || i == 1 || (i == 2 && b)) { Console.WriteLine(1); } Console.WriteLine(2); } public static void SingleIf4(int i, bool a) { if (i == 1 || i == 2 || (i != 3 && a) || i != 4) { Console.WriteLine(1); } Console.WriteLine(2); } public static void NestedIf(int i) { if (i != 1) { if (i == 2) { Console.WriteLine(2); } Console.WriteLine("default"); } Console.WriteLine(); } public static void IfChainWithCondition(int i) { if (i == 0) { Console.WriteLine(0); } else if (i == 1) { Console.WriteLine(1); } else if (i == 2) { Console.WriteLine(2); } else if (i == 3) { Console.WriteLine(3); } else if (i == 4) { Console.WriteLine(4); } else if (i == 5 && Console.CapsLock) { Console.WriteLine("5A"); } else { Console.WriteLine("default"); } Console.WriteLine(); } public static bool SwitchlikeIf(int i, int j) { if (i != 0 && j != 0) { if (i == -1 && j == -1) { Console.WriteLine("-1, -1"); } if (i == -1 && j == 1) { Console.WriteLine("-1, 1"); } if (i == 1 && j == -1) { Console.WriteLine("1, -1"); } if (i == 1 && j == 1) { Console.WriteLine("1, 1"); } return false; } if (i != 0) { if (i == -1) { Console.WriteLine("-1, 0"); } if (i == 1) { Console.WriteLine("1, 0"); } return false; } if (j != 0) { if (j == -1) { Console.WriteLine("0, -1"); } if (j == 1) { Console.WriteLine("0, 1"); } return false; } return true; } public static bool SwitchlikeIf2(int i) { if (i != 0) { // note that using else-if in this chain creates a nice-looking switch here (as expected) if (i == 1) { Console.WriteLine(1); } if (i == 2) { Console.WriteLine(2); } if (i == 3) { Console.WriteLine(3); } return false; } return false; } public static void SingleIntervalIf(char c) { if (c >= 'A' && c <= 'Z') { Console.WriteLine("alphabet"); } Console.WriteLine("end"); } public static bool Loop8(char c, bool b, Func getChar) { if (b) { while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { c = getChar(); } } return true; } public static void Loop9(Func getChar) { char c; do { c = getChar(); } while (c != -1 && c != '\n' && c != '\u2028' && c != '\u2029'); } #endregion // Ensure correctness of SwitchDetection.UseCSharpSwitch control flow heuristics public static void SwitchWithBreakCase(int i, bool b) { if (b) { switch (i) { case 1: Console.WriteLine(1); break; default: Console.WriteLine("default"); break; case 2: break; } Console.WriteLine("b"); } Console.WriteLine("end"); } public static void SwitchWithReturnAndBreak(int i, bool b) { switch (i) { case 0: if (b) { return; } break; case 1: if (!b) { return; } break; } Console.WriteLine(); } public static int SwitchWithReturnAndBreak2(int i, bool b) { switch (i) { case 4: case 33: Console.WriteLine(); return 1; case 334: if (b) { return 2; } break; case 395: case 410: case 455: Console.WriteLine(); break; } Console.WriteLine(); return 0; } public static void SwitchWithReturnAndBreak3(int i) { switch (i) { default: return; case 0: Console.WriteLine(0); break; case 1: Console.WriteLine(1); break; } Console.WriteLine(); } public static string Issue1621(int x) { if (x == 5) { return "5"; } switch (x) { case 1: return "1"; case 2: case 6: case 7: return "2-6-7"; case 3: return "3"; case 4: return "4"; case 5: return "unreachable"; default: throw new Exception(); } } public static int Issue1602(string x) { switch (x) { case null: return 0; case "": return -1; case "A": return 65; case "B": return 66; case "C": return 67; case "D": return 68; case "E": return 69; case "F": return 70; default: throw new ArgumentOutOfRangeException(); } } public static void Issue1745(string aaa) { switch (aaa) { case "a": case "b": case "c": case "d": case "e": case "f": Console.WriteLine(aaa); break; case null: Console.WriteLine(""); break; case "": Console.WriteLine(""); break; } } public static bool DoNotRemoveAssignmentBeforeSwitch(string x, out ConsoleKey key) { #if NET40 || !ROSLYN4 key = (ConsoleKey)0; #else key = ConsoleKey.None; #endif switch (x) { case "A": key = ConsoleKey.A; break; case "B": key = ConsoleKey.B; break; case "C": key = ConsoleKey.C; break; } #if NET40 || !ROSLYN4 return key != (ConsoleKey)0; #else return key != ConsoleKey.None; #endif } public static void Issue1767(string s) { switch (s) { case "a": ch1767 = s[0]; break; case "b": ch1767 = s[0]; break; case "c": ch1767 = s[0]; break; case "d": ch1767 = s[0]; break; case "e": ch1767 = s[0]; break; case "f": ch1767 = s[0]; break; } } public static void Issue2763(int value) { switch ((KnownColor)value) { case KnownColor.DarkBlue: Console.WriteLine("DarkBlue"); break; case KnownColor.DarkCyan: Console.WriteLine("DarkCyan"); break; case KnownColor.DarkGoldenrod: Console.WriteLine("DarkGoldenrod"); break; case KnownColor.DarkGray: Console.WriteLine("DarkGray"); break; case KnownColor.DarkGreen: Console.WriteLine("DarkGreen"); break; case KnownColor.DarkKhaki: Console.WriteLine("DarkKhaki"); break; } } #if CS110 && NET70 public static string SwitchOverReadOnlySpanChar1(ReadOnlySpan text) { Console.WriteLine("SwitchOverReadOnlySpanChar1:"); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; default: return "Default"; } } public static string SwitchOverSpanChar1(Span text) { Console.WriteLine("SwitchOverSpanChar1:"); switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; default: return "Default"; } } #endif #if ROSLYN public static int Issue3577(int what) { int result = 0; switch ((long)what) { case 1L: result = 1; break; case 2L: result = 2; break; } return result; } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/SwitchExpressions.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class SwitchExpressions { public class ImplicitlyCastToString { public static implicit operator string(ImplicitlyCastToString val) { return "foo"; } } public enum State { False, True, Null } public static bool? SwitchOverNullableEnum(State? state) { return state switch { State.False => false, State.True => true, State.Null => null, _ => throw new InvalidOperationException(), }; } public static string SparseIntegerSwitch(int i) { Console.WriteLine("SparseIntegerSwitch: " + i); return i switch { -10000000 => "-10 mln", -100 => "-hundred", -1 => "-1", 0 => "0", 1 => "1", 2 => "2", 4 => "4", 100 => "hundred", 10000 => "ten thousand", 10001 => "ten thousand and one", int.MaxValue => "int.MaxValue", _ => "something else", }; } public static bool SparseIntegerSwitch3(int i) { // not using a switch expression because we'd have to duplicate the 'true' branch switch (i) { case 0: case 10: case 11: case 12: case 100: case 101: case 200: return true; default: return false; } } public static string SwitchOverNullableInt(int? i, int? j) { return (i + j) switch { null => "null", 0 => "zero", 5 => "five", 10 => "ten", _ => "large", }; } public static void SwitchOverInt(int i) { Console.WriteLine(i switch { 0 => "zero", 5 => "five", 10 => "ten", 15 => "fifteen", 20 => "twenty", 25 => "twenty-five", 30 => "thirty", _ => throw new NotImplementedException(), }); } public static string SwitchOverString1(string text) { Console.WriteLine("SwitchOverString1: " + text); return text switch { "First case" => "Text1", "Second case" => "Text2", "Third case" => "Text3", "Fourth case" => "Text4", "Fifth case" => "Text5", "Sixth case" => "Text6", null => null, _ => "Default", }; } public static string SwitchOverString2(string text) { Console.WriteLine("SwitchOverString1: " + text); // Cannot use switch expression, because "return Text2;" would need to be duplicated switch (text) { case "First case": return "Text1"; case "Second case": case "2nd case": return "Text2"; case "Third case": return "Text3"; case "Fourth case": return "Text4"; case "Fifth case": return "Text5"; case "Sixth case": return "Text6"; case null: return null; default: return "Default"; } } public static string Issue2222() { return (string)new ImplicitlyCastToString() switch { "foo" => "foo", "bar" => "bar", "quux" => "quux", _ => "default", }; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class ThrowExpressions { private class ArgumentCheckingCtor { private int initializedFromCtor = CountSheep() ?? throw new Exception("No sheep?!"); private object cacheObj = TryGetObj() ?? throw new Exception("What?"); private object simpleObj; private int? nullableInt; public ArgumentCheckingCtor(object simpleObj, int? nullableInt) { this.simpleObj = simpleObj ?? throw new ArgumentNullException("simpleObj"); this.nullableInt = nullableInt ?? throw new ArgumentNullException("nullableInt"); } public ArgumentCheckingCtor(string input) : this(input, GetIntOrNull(input ?? throw new ArgumentNullException("input"))) { } public ArgumentCheckingCtor(DataObject obj) : this(obj ?? throw new Exception(), GetIntOrNull(obj.NullableDataField?.NullableDataField.ToString() ?? throw new ArgumentNullException("input"))) { } private static int? GetIntOrNull(string v) { if (int.TryParse(v, out var result)) { return result; } return null; } private static int? CountSheep() { throw new NotImplementedException(); } private static object TryGetObj() { return null; } public override int GetHashCode() { return initializedFromCtor; } public override bool Equals(object obj) { return true; } } public class DataObject { public int IntField; public int? NullableIntField; public Data DataField; public Data? NullableDataField; public int IntProperty { get; set; } public int? NullableIntProperty { get; set; } public Data DataProperty { get; } public Data? NullableDataProperty { get; } } public struct Data { public int IntField; public int? NullableIntField; public MoreData DataField; public MoreData? NullableDataField; public int IntProperty { get; set; } public int? NullableIntProperty { get; set; } public MoreData DataProperty { get; } public MoreData? NullableDataProperty { get; } } public struct MoreData { public int IntField; public int? NullableIntField; public int IntProperty { get; set; } public int? NullableIntProperty { get; set; } } public static int IntField; public static int? NullableIntField; public static object ObjectField; public int InstIntField; public int? InstNullableIntField; public object InstObjectField; public Data DataField; public Data? NullableDataField; public DataObject DataObjectField; public static int IntProperty { get; } public static int? NullableIntProperty { get; } public static object ObjProperty { get; } public int InstIntProperty { get; } public int? InstNullableIntProperty { get; } public object InstObjProperty { get; } public Data DataProperty { get; } public Data? NullableDataProperty { get; } public DataObject DataObjectProperty { get; } public static int ReturnIntField() { return NullableIntField ?? throw new Exception(); } public static int ReturnIntProperty() { return NullableIntProperty ?? throw new Exception(); } public static object ReturnObjField() { return ObjectField ?? throw new Exception(); } public static object ReturnObjProperty() { return ObjProperty ?? throw new Exception(); } public static int ReturnIntField(ThrowExpressions inst) { return inst.InstNullableIntField ?? throw new Exception(); } public static int ReturnIntProperty(ThrowExpressions inst) { return inst.InstNullableIntProperty ?? throw new Exception(); } public static object ReturnObjField(ThrowExpressions inst) { return inst.InstObjectField ?? throw new Exception(); } public static object ReturnObjProperty(ThrowExpressions inst) { return inst.InstObjProperty ?? throw new Exception(); } public static void UseComplexNullableStruct(ThrowExpressions inst) { Use(inst.InstNullableIntField ?? throw new Exception()); Use((inst.NullableDataField ?? throw new Exception()).IntField); Use(inst.NullableDataField?.NullableIntField ?? throw new Exception()); Use((inst.NullableDataProperty ?? throw new Exception()).IntField); Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception()); Use((inst.NullableDataField ?? throw new Exception()).DataField.IntField); Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); Use((inst.NullableDataProperty ?? throw new Exception()).DataField.IntField); Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); Use((inst.NullableDataField ?? throw new Exception()).DataProperty.IntField); Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); Use((inst.NullableDataProperty ?? throw new Exception()).DataProperty.IntField); Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); Use(inst.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); Use(inst.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); Use(inst.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); Use(inst.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); } public static void UseComplexNullableObject(DataObject inst) { Use(inst?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataField?.IntField ?? throw new Exception()); Use(inst?.NullableDataField?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.IntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataField?.DataField.IntField ?? throw new Exception()); Use(inst?.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.DataField.IntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataField?.DataProperty.IntField ?? throw new Exception()); Use(inst?.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.DataProperty.IntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); Use(inst?.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); Use(inst?.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); Use(inst?.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); } public static void Use(T usage) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs ================================================ // Copyright (c) 2018 Daniel Grunwald // // 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; using System.Collections.Generic; using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class TupleTests { private abstract class OverloadResolution { public abstract void M1((long, long) a); public abstract void M1(object a); public void UseM1((int, int) a) { // M1(a); TODO: tuple conversion transform // Cast is required to avoid the overload usable via tuple conversion: M1((object)a); } } public struct GenericStruct { public T Field; public T Property { get; set; } } public ValueTuple VT0; public ValueTuple VT1; public ValueTuple VT7EmptyRest; public (int, uint) Unnamed2; public (int, int, int) Unnamed3; public (int, int, int, int) Unnamed4; public (int, int, int, int, int) Unnamed5; public (int, int, int, int, int, int) Unnamed6; public (int, int, int, int, int, int, int) Unnamed7; public (int, int, int, int, int, int, int, int) Unnamed8; public (int a, uint b) Named2; public (int a, uint b)[] Named2Array; public (int a, int b, int c, int d, int e, int f, int g, int h) Named8; public (int, int a, int, int b, int) PartiallyNamed; public ((int a, int b) x, (int, int) y, (int c, int d) z) Nested1; public ((object a, dynamic b), dynamic, (dynamic c, object d)) Nested2; public (ValueTuple a, (int x1, int x2), ValueTuple b, (int y1, int y2), (int, int) c) Nested3; public (int a, int b, int c, int d, int e, int f, int g, int h, (int i, int j)) Nested4; public Dictionary<(int a, string b), (string c, int d)> TupleDict; public List<(int, string)> List; public bool HasItems => List.Any(((int, string) a) => a.Item1 > 0); public int VT1Member => VT1.Item1; public int AccessUnnamed8 => Unnamed8.Item8; public int AccessNamed8 => Named8.h; public int AccessPartiallyNamed => PartiallyNamed.a + PartiallyNamed.Item3; public ValueTuple NewTuple1 => new ValueTuple(1); public (int a, int b) NewTuple2 => (a: 1, b: 2); public object BoxedTuple10 => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); public (uint, int) SwapUnnamed => (Unnamed2.Item2, Unnamed2.Item1); public (uint, int) SwapNamed2 => (Named2.b, Named2.a); public int TupleHash => (1, 2, 3).GetHashCode(); public int TupleHash2 => Named2.GetHashCode(); public (int, int) AccessRest => (1, 2, 3, 4, 5, 6, 7, 8, 9).Rest; public (string, object, Action) TargetTyping => (null, 1, delegate { #pragma warning disable format }); #pragma warning restore format public object NotTargetTyping => ((string)null, (object)1, (Action)delegate { #pragma warning disable format }); #pragma warning restore format public void UnnamedTupleOut(out (int, string, Action, dynamic) tuple) { tuple = (42, "Hello", Console.WriteLine, null); } public void UnnamedTupleIn(in (int, string, Action, dynamic) tuple) { } public void UnnamedTupleRef(ref (int, string, Action, dynamic) tuple) { } public void NamedTupleOut(out (int A, string B, Action C, dynamic D) tuple) { tuple = (A: 42, B: "Hello", C: Console.WriteLine, D: null); } public void NamedTupleIn(in (int A, string B, Action C, dynamic D) tuple) { } public void NamedTupleRef(ref (int A, string B, Action C, dynamic D) tuple) { } public void UseDict() { if (TupleDict.Count > 10) { TupleDict.Clear(); } // TODO: it would be nice if we could infer the name 'c' for the local string item = TupleDict[(1, "abc")].c; Console.WriteLine(item); Console.WriteLine(item); Console.WriteLine(TupleDict.Values.ToList().First().d); } private static (string, string) Issue3014a(string[] args) { return (from v in args select (Name: v, Value: v) into kvp orderby kvp.Name select kvp).First(); } private static (string, string) Issue3014b(string[] args) { return (from v in args select ((string Name, string Value))GetTuple() into kvp orderby kvp.Name select kvp).First(); (string, string) GetTuple() { return (args[0], args[1]); } } public void Issue1174() { Console.WriteLine((1, 2, 3).GetHashCode()); } public void LocalVariables((int, int) a) { (int, int) tuple = (a.Item1 + a.Item2, a.Item1 * a.Item2); Console.WriteLine(tuple.ToString()); Console.WriteLine(tuple.GetType().FullName); } public void Foreach(IEnumerable<(int, string)> input) { foreach (var item in input) { Console.WriteLine($"{item.Item1}: {item.Item2}"); } } public void ForeachNamedElements(IEnumerable<(int Index, string Data)> input) { foreach (var item in input) { Console.WriteLine($"{item.Index}: {item.Data}"); } } public void NonGenericForeach(IEnumerable input) { foreach ((string, int) item in input) { Console.WriteLine($"{item.Item1}: {item.Item2}"); } } public void CallForeach() { Foreach(new List<(int, string)> { (1, "a"), (2, "b") }); } public void DynamicTuple((dynamic A, dynamic B) a) { a.A.DynamicCall(); a.B.Dynamic = 42; } public void GenericStructWithElementNames(GenericStruct<(int A, int B)> s) { Console.WriteLine(s.Field.A + s.Property.B); } public void RefCallSites(out (int, string, Action, dynamic) tuple) { UnnamedTupleOut(out tuple); UnnamedTupleIn(in tuple); UnnamedTupleRef(ref tuple); NamedTupleOut(out tuple); NamedTupleIn(in tuple); NamedTupleRef(ref tuple); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs ================================================ // Copyright (c) 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 // 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.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class TypeAnalysisTests { private class @_ { } private byte[] byteArray; private ulong uint64Field; public int Issue1796a { get { return (int)(uint64Field & 0x7FFFFFFF); } set { uint64Field = (uint64Field & 0xFFFFFFFF80000000uL) | (ulong)value; } } public ushort Issue1796b { get { return (ushort)((uint64Field & 0xFFFF000000000000uL) >> 48); } set { uint64Field = (uint64Field & 0xFFFFFFFFFFFFL) | ((ulong)value << 48); } } public byte SubtractFrom256(byte b) { return (byte)(256 - b); } #region Shift public int LShiftInteger(int num1, int num2) { return num1 << num2; } public uint LShiftUnsignedInteger(uint num1, uint num2) { return num1 << (int)num2; } public long LShiftLong(long num1, long num2) { return num1 << (int)num2; } public ulong LShiftUnsignedLong(ulong num1, ulong num2) { return num1 << (int)num2; } public int RShiftInteger(int num1, int num2) { return num1 >> num2; } public uint RShiftUnsignedInteger(uint num1, int num2) { return num1 >> num2; } public long RShiftLong(long num1, long num2) { return num1 >> (int)num2; } public ulong RShiftUnsignedLong(ulong num1, ulong num2) { return num1 >> (int)num2; } public int ShiftByte(byte num) { return num << 8; } public int RShiftByte(byte num) { return num >> 8; } public uint RShiftByteWithZeroExtension(byte num) { // zero extend -> cast to unsigned -> unsigned shift return (uint)num >> 8; } public int RShiftByteWithZeroExtensionReturnAsSigned(byte num) { #if CS110 // zero extend -> unsigned shift return num >>> 8; #else // zero extend -> cast to unsigned -> unsigned shift -> cast to signed return (int)((uint)num >> 8); #endif } public int RShiftByteAsSByte(byte num) { return (sbyte)num >> 8; } public int RShiftSByte(sbyte num) { return num >> 8; } public uint RShiftSByteWithZeroExtension(sbyte num) { return (uint)((byte)num >> 4); } public uint RShiftSByteWithSignExtension(sbyte num) { // sign extend -> cast to unsigned -> unsigned shift return (uint)num >> 4; } public int RShiftSByteWithSignExtensionReturnAsSigned(sbyte num) { #if CS110 // sign extend -> unsigned shift return num >>> 4; #else // sign extend -> cast to unsigned -> unsigned shift -> cast to signed return (int)((uint)num >> 4); #endif } public int RShiftSByteAsByte(sbyte num) { return (byte)num >> 8; } #endregion public int GetHashCode(long num) { return (int)num ^ (int)(num >> 32); } public void TernaryOp(Random a, Random b, bool c) { if ((c ? a : b) == null) { Console.WriteLine(); } } public void OperatorIs(object o) { Console.WriteLine(o is Random); Console.WriteLine(!(o is Random)); // If we didn't escape the '_' identifier here, this would look like a discard pattern Console.WriteLine(o is @_); } public byte[] CreateArrayWithInt(int length) { return new byte[length]; } public byte[] CreateArrayWithLong(long length) { return new byte[length]; } public byte[] CreateArrayWithUInt(uint length) { return new byte[length]; } public byte[] CreateArrayWithULong(ulong length) { return new byte[length]; } public byte[] CreateArrayWithShort(short length) { return new byte[length]; } public byte[] CreateArrayWithUShort(ushort length) { return new byte[length]; } public byte UseArrayWithInt(int i) { return byteArray[i]; } public byte UseArrayWithUInt(uint i) { return byteArray[i]; } public byte UseArrayWithLong(long i) { return byteArray[i]; } public byte UseArrayWithULong(ulong i) { return byteArray[i]; } public byte UseArrayWithShort(short i) { return byteArray[i]; } public byte UseArrayWithUShort(ushort i) { return byteArray[i]; } public byte UseArrayWithCastToUShort(int i) { // Unchecked cast = truncate to 16 bits return byteArray[(ushort)i]; } public StringComparison EnumDiffNumber(StringComparison data) { return data - 1; } public int EnumDiff(StringComparison a, StringComparison b) { return Math.Abs(a - b); } public bool CompareDelegatesByValue(Action a, Action b) { return a == b; } public bool CompareDelegatesByReference(Action a, Action b) { return (object)a == b; } public bool CompareDelegateWithNull(Action a) { return a == null; } public bool CompareStringsByValue(string a, string b) { return a == b; } public bool CompareStringsByReference(string a, string b) { return (object)a == b; } public bool CompareStringWithNull(string a) { return a == null; } public bool CompareType(Type a, Type b) { return a == b; } public bool CompareTypeByReference(Type a, Type b) { return (object)a == b; } public bool CompareTypeWithNull(Type t) { return t == null; } public Attribute CallExtensionMethodViaBaseClass(Type type) { return type.GetCustomAttribute(); } public decimal ImplicitConversionToDecimal(byte v) { return v; } public decimal ImplicitConversionToDecimal(ulong v) { return v; } public bool EnumInConditionalOperator(bool b) { return string.Equals("", "", b ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } public bool MethodCallOnEnumConstant() { return AttributeTargets.All.HasFlag(AttributeTargets.Assembly); } public static string ImpossibleCast1(int i) { return (string)(object)i; } public static string ImpossibleCast2(Action a) { return (string)(object)a; } public static bool CompareLast32Bits(long a, long b) { return (int)a == (int)b; } public static bool Last32BitsAreZero(long a) { return (int)a == 0; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeMemberTests.cs ================================================ // Copyright (c) 2014 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class T01_IndexerWithGetOnly { #if ROSLYN public int this[int i] => i; #else public int this[int i] { get { return i; } } #endif } public class T02_IndexerWithSetOnly { public int this[int i] { set { } } } public class T03_IndexerWithMoreParameters { #if ROSLYN public int this[int i, string s, Type t] => 0; #else public int this[int i, string s, Type t] { get { return 0; } } #endif } public class T04_IndexerInGenericClass { #if ROSLYN public int this[T t] => 0; #else public int this[T t] { get { return 0; } } #endif } public class T05_OverloadedIndexer { #if ROSLYN public int this[int t] => 0; #else public int this[int t] { get { return 0; } } #endif public int this[string s] { get { return 0; } set { Console.WriteLine(value + " " + s); } } } public interface T06_IIndexerInInterface { int this[string s, string s2] { set; } } public interface T07_IMyInterface_IndexerInterfaceExplicitImplementation { int this[string s] { get; } } public class T07_MyClass_IndexerInterfaceExplicitImplementation : T07_IMyInterface_IndexerInterfaceExplicitImplementation { #if ROSLYN int T07_IMyInterface_IndexerInterfaceExplicitImplementation.this[string s] => 3; #else int T07_IMyInterface_IndexerInterfaceExplicitImplementation.this[string s] { get { return 3; } } #endif } public interface T08_IMyInterface_IndexerInterfaceImplementation { int this[string s] { get; } } public class T08_MyClass_IndexerInterfaceImplementation : T08_IMyInterface_IndexerInterfaceImplementation { #if ROSLYN public int this[string s] => 3; #else public int this[string s] { get { return 3; } } #endif } public interface T09_IMyInterface_MethodExplicit { void MyMethod(); } public abstract class T09_MyClass_IndexerAbstract { public abstract int this[string s, string s2] { set; } protected abstract string this[int index] { get; } } public class T09_MyClass_MethodExplicit : T09_IMyInterface_MethodExplicit { void T09_IMyInterface_MethodExplicit.MyMethod() { } } public interface T10_IMyInterface_MethodFromInterfaceVirtual { void MyMethod(); } public class T10_MyClass : T10_IMyInterface_MethodFromInterfaceVirtual { public virtual void MyMethod() { } } public interface T11_IMyInterface_MethodFromInterface { void MyMethod(); } public class T11_MyClass_MethodFromInterface : T11_IMyInterface_MethodFromInterface { public void MyMethod() { } } public interface T12_IMyInterface_MethodFromInterfaceAbstract { void MyMethod(); } public abstract class T12_MyClass_MethodFromInterfaceAbstract : T12_IMyInterface_MethodFromInterfaceAbstract { public abstract void MyMethod(); } public interface T13_IMyInterface_PropertyInterface { int MyProperty { get; set; } } public interface T14_IMyInterface_PropertyInterfaceExplicitImplementation { int MyProperty { get; set; } } public class T14_MyClass_PropertyInterfaceExplicitImplementation : T14_IMyInterface_PropertyInterfaceExplicitImplementation { int T14_IMyInterface_PropertyInterfaceExplicitImplementation.MyProperty { get { return 0; } set { } } } public interface T15_IMyInterface_PropertyInterfaceImplementation { int MyProperty { get; set; } } public class T15_MyClass_PropertyInterfaceImplementation : T15_IMyInterface_PropertyInterfaceImplementation { public int MyProperty { get { return 0; } set { } } } public class T16_MyClass_PropertyPrivateGetPublicSet { public int MyProperty { private get { return 3; } set { } } } public class T17_MyClass_PropertyPublicGetProtectedSet { public int MyProperty { get { return 3; } protected set { } } } public class T18_Base_PropertyOverrideDefaultAccessorOnly { public virtual int MyProperty { get { return 3; } protected set { } } } public class T18_Derived_PropertyOverrideDefaultAccessorOnly : T18_Base_PropertyOverrideDefaultAccessorOnly { #if ROSLYN public override int MyProperty => 4; #else public override int MyProperty { get { return 4; } } #endif } public class T19_Base_PropertyOverrideRestrictedAccessorOnly { public virtual int MyProperty { get { return 3; } protected set { } } } public class T19_Derived_PropertyOverrideRestrictedAccessorOnly : T19_Base_PropertyOverrideRestrictedAccessorOnly { public override int MyProperty { protected set { } } } public class T20_Base_PropertyOverrideOneAccessor { protected internal virtual int MyProperty { get { return 3; } protected set { } } } public class T20_DerivedNew_PropertyOverrideOneAccessor : T20_Base_PropertyOverrideOneAccessor { public new virtual int MyProperty { set { } } } public class T20_DerivedOverride_PropertyOverrideOneAccessor : T20_DerivedNew_PropertyOverrideOneAccessor { public override int MyProperty { set { } } } public class T21_Base_IndexerOverrideRestrictedAccessorOnly { public virtual int this[string s] { get { return 3; } protected set { } } protected internal virtual int this[int i] { protected get { return 2; } set { } } } public class T21_Derived_IndexerOverrideRestrictedAccessorOnly : T21_Base_IndexerOverrideRestrictedAccessorOnly { protected internal override int this[int i] { protected get { return 4; } } } public class T22_A_HideProperty { public virtual int P { get { return 0; } set { } } } public class T22_B_HideProperty : T22_A_HideProperty { private new int P { get { return 0; } set { } } } public class T22_C_HideProperty : T22_B_HideProperty { public override int P { set { } } } public class T23_A_HideMembers { public int F; #if ROSLYN public int Prop => 3; public int G => 3; #else public int Prop { get { return 3; } } public int G { get { return 3; } } #endif } public class T23_B_HideMembers : T23_A_HideMembers { #if ROSLYN public new int F => 3; public new string Prop => "a"; #else public new int F { get { return 3; } } public new string Prop { get { return "a"; } } #endif } public class T23_C_HideMembers : T23_A_HideMembers { public new int G; } public class T23_D_HideMembers : T23_A_HideMembers { public new void F() { } } public class T23_D1_HideMembers : T23_D_HideMembers { public new int F; } public class T23_E_HideMembers : T23_A_HideMembers { private new class F { } } public class T23_G_HideMembers2 { #if ROSLYN public int Item => 1; #else public int Item { get { return 1; } } #endif } public class T23_G2_HideMembers2 : T23_G_HideMembers2 { #if ROSLYN public int this[int i] => 2; #else public int this[int i] { get { return 2; } } #endif } public class T23_G3_HideMembers2 : T23_G2_HideMembers2 { #if ROSLYN public new int Item => 4; #else public new int Item { get { return 4; } } #endif } public class T23_H_HideMembers2 { #if ROSLYN public int this[int j] => 0; #else public int this[int j] { get { return 0; } } #endif } public class T23_H2_HideMembers2 : T23_H_HideMembers2 { #if ROSLYN public int Item => 2; #else public int Item { get { return 2; } } #endif } public class T23_H3_HideMembers2 : T23_H2_HideMembers2 { #if ROSLYN public new string this[int j] => null; #else public new string this[int j] { get { return null; } } #endif } public class T24_A_HideMembers2a : T24_IA_HideMembers2a { int T24_IA_HideMembers2a.this[int i] { get { throw new NotImplementedException(); } } } public class T24_A1_HideMembers2a : T24_A_HideMembers2a { #if ROSLYN public int this[int i] => 3; #else public int this[int i] { get { return 3; } } #endif } public interface T24_IA_HideMembers2a { int this[int i] { get; } } public class T25_G_HideMembers3 { public void M1(T p) { } public int M2(int t) { return 3; } } public class T25_G1_HideMembers3 : T25_G_HideMembers3 { public new int M1(int i) { return 0; } public int M2(T i) { return 2; } } public class T25_G2_HideMembers3 : T25_G_HideMembers3 { public int M1(T p) { return 4; } } public class T25_J_HideMembers3 { #if ROSLYN public int P => 2; #else public int P { get { return 2; } } #endif } public class T25_J2_HideMembers3 : T25_J_HideMembers3 { #pragma warning disable 0108 // Deliberate bad code for test case public int get_P; #pragma warning restore 0108 } public class T26_A_HideMembers4 { public void M(T t) { } } public class T26_A1_HideMembers4 : T26_A_HideMembers4 { public new void M(K t) { } public void M(int t) { } } public class T26_B_HideMembers4 { public void M() { } public void M1() { } public void M2(T t) { } } public class T26_B1_HideMembers4 : T26_B_HideMembers4 { public void M() { } public new void M1() { } public new void M2(R r) { } } public class T26_C_HideMembers4 { public void M(T t) { } } public class T26_C1_HideMembers4 : T26_C_HideMembers4 { public void M(TT t) { } } public class T27_A_HideMembers5 { public void M(int t) { } } public class T27_A1_HideMembers5 : T27_A_HideMembers5 { public void M(ref int t) { } } public class T27_B_HideMembers5 { public void M(ref int l) { } } public class T27_B1_HideMembers5 : T27_B_HideMembers5 { public void M(out int l) { l = 2; } public void M(ref long l) { } } public class T28_A_HideMemberSkipNotVisible { protected int F; #if ROSLYN protected string P => null; #else protected string P { get { return null; } } #endif } public class T28_B_HideMemberSkipNotVisible : T28_A_HideMemberSkipNotVisible { private new string F; private new int P { set { } } } public class T29_A_HideNestedClass { public class N1 { } protected class N2 { } private class N3 { } internal class N4 { } protected internal class N5 { } } public class T29_B_HideNestedClass : T29_A_HideNestedClass { public new int N1; public new int N2; public int N3; public new int N4; public new int N5; } public class T30_A_HidePropertyReservedMethod { #if ROSLYN public int P => 1; #else public int P { get { return 1; } } #endif } public class T30_B_HidePropertyReservedMethod : T30_A_HidePropertyReservedMethod { public int get_P() { return 2; } public void set_P(int value) { } } public class T31_A_HideIndexerDiffAccessor { #if ROSLYN public int this[int i] => 2; #else public int this[int i] { get { return 2; } } #endif } public class T31_B_HideIndexerDiffAccessor : T31_A_HideIndexerDiffAccessor { public new int this[int j] { set { } } } public class T32_A_HideIndexerGeneric { public virtual int this[T r] { get { return 0; } set { } } } public class T32_B_HideIndexerGeneric : T32_A_HideIndexerGeneric { private new int this[int k] { get { return 0; } set { } } } public class T32_C_HideIndexerGeneric : T32_A_HideIndexerGeneric { public override int this[T s] { set { } } } public class T32_D_HideIndexerGeneric : T32_C_HideIndexerGeneric { public new virtual int this[T s] { set { } } } public class T33_A_HideMethod { public virtual void F() { } } public class T33_B_HideMethod : T33_A_HideMethod { private new void F() { base.F(); } } public class T33_C_HideMethod : T33_B_HideMethod { public override void F() { base.F(); } } public class T34_A_HideMethodGeneric { public virtual void F(T s) { } public new static bool Equals(object o1, object o2) { return true; } } public class T34_B_HideMethodGeneric : T34_A_HideMethodGeneric { private new void F(string k) { } public void F(int i) { } } public class T34_C_HideMethodGeneric : T34_A_HideMethodGeneric { public override void F(T r) { } public void G(T t) { } } public class T34_D_HideMethodGeneric : T34_C_HideMethodGeneric { public new virtual void F(T1 k) { } public virtual void F(T2 k) { } public virtual void G(T2 t) { } } public class T35_A_HideMethodGenericSkipPrivate { public virtual void F(T t) { } } public class T35_B_HideMethodGenericSkipPrivate : T35_A_HideMethodGenericSkipPrivate { private new void F(T t) { } private void K() { } } public class T35_C_HideMethodGenericSkipPrivate : T35_B_HideMethodGenericSkipPrivate { public override void F(T tt) { } public void K() { } } public class T35_D_HideMethodGenericSkipPrivate : T35_B_HideMethodGenericSkipPrivate { public override void F(int t) { } } public class T36_A_HideMethodGeneric2 { public virtual void F(int i) { } public void K() { } } public class T36_B_HideMethodGeneric2 : T36_A_HideMethodGeneric2 { protected virtual void F(T t) { } public void K() { } } public class T36_C_HideMethodGeneric2 : T36_B_HideMethodGeneric2 { protected override void F(int k) { } public new void K() { } } public class T36_D_HideMethodGeneric2 : T36_B_HideMethodGeneric2 { public override void F(int k) { } public void L() { } } public class T36_E_HideMethodGeneric2 { public void M(T t, T2 t2) { } } public class T36_F_HideMethodGeneric2 : T36_E_HideMethodGeneric2 { public void M(T t1, T t2) { } } public class T37_C1_HideMethodDiffSignatures { public virtual void M(T arg) { } } public class T37_C2_HideMethodDiffSignatures : T37_C1_HideMethodDiffSignatures { public new virtual void M(T2 arg) { } } public class T37_C3_HideMethodDiffSignatures : T37_C2_HideMethodDiffSignatures { public new virtual void M(bool arg) { } } public class T38_A_HideMethodStatic { #if ROSLYN public int N => 0; #else public int N { get { return 0; } } #endif } public class T38_B_HideMethodStatic { public int N() { return 0; } } public class T39_A_HideEvent { public virtual event EventHandler E; public event EventHandler F; } public class T39_B_HideEvent : T39_A_HideEvent { public new virtual event EventHandler E; public new event EventHandler F; } public class T39_C_HideEvent : T39_B_HideEvent { public override event EventHandler E; } public class T40_EventVsField { public object KeyDownEvent; private event EventHandler KeyDown; public void UseObject() { Console.WriteLine(KeyDownEvent); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs ================================================ // Copyright (c) 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 // 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; #if !NET40 && ROSLYN using System.Runtime.CompilerServices; #endif using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class SizeofTest { private struct StructWithStaticField { public static object StaticObj; #if CS110 && NET70 public nint A; #else public IntPtr A; #endif } private struct UnmanagedStruct { public StructWithStaticField Value; } private struct ManagedStruct { public object Obj; } #if CS73 private unsafe int GenericMethod() where T : unmanaged { return sizeof(T); } #endif private unsafe void Test(out int s1, out int s2, out int s3) { s1 = 0; s2 = 0; s3 = 0; #if CS73 GenericMethod(); #endif s1 = sizeof(UnmanagedStruct); #if !NET40 && ROSLYN s2 = Unsafe.SizeOf(); s3 = Unsafe.SizeOf(); #endif } } public class UnsafeCode { public struct SimpleStruct { public int X; public double Y; } #if CS120 public unsafe struct ResultStruct(byte* ptr1, byte* ptr2) { public unsafe byte* ptr1 = ptr1; public unsafe byte* ptr2 = ptr2; } #else public struct ResultStruct { public unsafe byte* ptr1; public unsafe byte* ptr2; public unsafe ResultStruct(byte* ptr1, byte* ptr2) { this.ptr1 = ptr1; this.ptr2 = ptr2; } } #endif public struct StructWithFixedSizeMembers { public unsafe fixed int Integers[100]; public int NormalMember; public unsafe fixed double Doubles[200]; [Obsolete("another attribute")] public unsafe fixed byte Old[1]; } private struct Data { public Vector Position; } [StructLayout(LayoutKind.Sequential, Size = 1)] private struct Vector { public override int GetHashCode() { return 0; } } #if CS73 public class CustomPinnable { public ref int GetPinnableReference() { throw new NotImplementedException(); } } #endif public unsafe delegate void UnsafeDelegate(byte* ptr); private UnsafeDelegate unsafeDelegate; private static UnsafeDelegate staticUnsafeDelegate; #if CS60 public unsafe int* NullPointer => null; #else public unsafe int* NullPointer { get { return null; } } #endif unsafe static UnsafeCode() { staticUnsafeDelegate = UnsafeStaticMethod; } public unsafe UnsafeCode() { unsafeDelegate = UnsafeMethod; } public unsafe int SizeOf() { return sizeof(SimpleStruct); } private static void UseBool(bool b) { } private unsafe void UnsafeMethod(byte* ptr) { } private unsafe static void UnsafeStaticMethod(byte* ptr) { } public unsafe void PointerComparison(int* a, double* b) { UseBool(a == b); UseBool(a != b); UseBool(a < b); UseBool(a > b); UseBool(a <= b); UseBool(a >= b); } public unsafe void PointerComparisonWithNull(int* a) { UseBool(a == null); UseBool(a != null); } public unsafe int* PointerCast(long* p) { return (int*)p; } public unsafe long ConvertDoubleToLong(double d) { return *(long*)(&d); } public unsafe double ConvertLongToDouble(long d) { return *(double*)(&d); } public unsafe int ConvertFloatToInt(float d) { return *(int*)(&d); } public unsafe float ConvertIntToFloat(int d) { return *(float*)(&d); } public unsafe int PointerCasts() { int result = 0; *(float*)(&result) = 0.5f; ((sbyte*)(&result))[3] = 3; ((sbyte*)(&result))[3] = -1; return result; } public unsafe void PassRefParameterAsPointer(ref int p) { fixed (int* ptr = &p) { UsePointer(ptr); } } public unsafe void PassPointerAsRefParameter(int* p) { UseReference(ref *p); } public unsafe void PassPointerCastAsRefParameter(uint* p) { UseReference(ref *(int*)p); } public unsafe void AddressInMultiDimensionalArray(double[,] matrix) { fixed (double* d = &matrix[1, 2]) { PointerReferenceExpression(d); PointerReferenceExpression(d); } } public unsafe void FixedStringAccess(string text) { fixed (char* ptr = text) { for (char* ptr2 = ptr; *ptr2 == 'a'; ptr2++) { *ptr2 = 'A'; } } } public unsafe void FixedStringNoPointerUse(string text) { fixed (char* ptr = text) { } } #if !(LEGACY_CSC && OPT) // legacy csc manages to optimize out the pinned variable altogether in this case; // leaving no pinned region we could detect. public unsafe void FixedArrayNoPointerUse(int[] arr) { fixed (int* ptr = arr) { } } #endif public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val) { fixed (long* ptr = array) { *(double*)(ptr + index) = val; } } public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val) { fixed (long* ptr = &array[index]) { *(double*)ptr = val; } } public unsafe string PointerReferenceExpression(double* d) { return d->ToString(); } public unsafe string PointerReferenceExpression2(long addr) { return ((int*)addr)->ToString(); } public unsafe int* PointerArithmetic(int* p) { return p + 2; } public unsafe long* PointerArithmetic2(long* p) { return 3 + p; } public unsafe long* PointerArithmetic3(long* p) { return (long*)((byte*)p + 3); } public unsafe long* PointerArithmetic4(void* p) { return (long*)((byte*)p + 3); } public unsafe int PointerArithmetic5(void* p, byte* q, int i) { return q[i] + *(byte*)p; } public unsafe int PointerArithmetic6(SimpleStruct* p, int i) { return p[i].X; } public unsafe int* PointerArithmeticLong1(int* p, long offset) { return p + offset; } public unsafe int* PointerArithmeticLong2(int* p, long offset) { return offset + p; } public unsafe int* PointerArithmeticLong3(int* p, long offset) { return p - offset; } public unsafe SimpleStruct* PointerArithmeticLong1s(SimpleStruct* p, long offset) { return p + offset; } public unsafe SimpleStruct* PointerArithmeticLong2s(SimpleStruct* p, long offset) { return offset + p; } public unsafe SimpleStruct* PointerArithmeticLong3s(SimpleStruct* p, long offset) { return p - offset; } public unsafe int PointerSubtraction(long* p, long* q) { return (int)(p - q); } public unsafe long PointerSubtractionLong(long* p, long* q) { return p - q; } public unsafe int PointerSubtraction2(long* p, short* q) { return (int)((byte*)p - (byte*)q); } public unsafe int PointerSubtraction3(void* p, void* q) { return (int)((byte*)p - (byte*)q); } public unsafe long PointerSubtraction4(sbyte* p, sbyte* q) { return p - q; } public unsafe long PointerSubtraction5(SimpleStruct* p, SimpleStruct* q) { return p - q; } public unsafe long Issue2158a(void* p, void* q) { return (long)p - (long)q; } public unsafe long Issue2158b(sbyte* p, sbyte* q) { return (long)p - (long)q; } public unsafe long Issue2158c(int* p, int* q) { return (long)p - (long)q; } public unsafe long Issue2158d(SimpleStruct* p, SimpleStruct* q) { return (long)p - (long)q; } public unsafe double FixedMemberAccess(StructWithFixedSizeMembers* m, int i) { return (double)m->Integers[i] + m->Doubles[i]; } public unsafe double* FixedMemberBasePointer(StructWithFixedSizeMembers* m) { return m->Doubles; } public unsafe void UseFixedMemberAsPointer(StructWithFixedSizeMembers* m) { UsePointer(m->Integers); } public unsafe void UseFixedMemberAsReference(StructWithFixedSizeMembers* m) { UseReference(ref *m->Integers); UseReference(ref m->Integers[1]); } public unsafe void PinFixedMember(ref StructWithFixedSizeMembers m) { fixed (int* integers = m.Integers) { UsePointer(integers); } } private void UseReference(ref int i) { } public unsafe string UsePointer(int* ptr) { return ptr->ToString(); } public unsafe string UsePointer(double* ptr) { return ptr->ToString(); } public unsafe void FixedMultiDimArray(int[,] arr) { fixed (int* ptr = arr) { UsePointer(ptr); } } #if CS73 && !NET40 public unsafe void FixedSpan(Span span) { fixed (int* ptr = span) { UsePointer(ptr); } } #endif #if CS73 public unsafe void FixedCustomReferenceType(CustomPinnable mem) { fixed (int* ptr = mem) { UsePointer(ptr); } } public unsafe void FixedCustomReferenceTypeNoPointerUse(CustomPinnable mem) { fixed (int* ptr = mem) { Console.WriteLine("Hello World!"); } } public unsafe void FixedCustomReferenceTypeExplicitGetPinnableReference(CustomPinnable mem) { fixed (int* ptr = &mem.GetPinnableReference()) { UsePointer(ptr); } } #endif public unsafe string StackAlloc(int count) { char* ptr = stackalloc char[count]; char* ptr2 = stackalloc char[100]; for (int i = 0; i < count; i++) { ptr[i] = (char)i; ptr2[i] = '\0'; } return UsePointer((double*)ptr); } public unsafe string StackAllocStruct(int count) { SimpleStruct* ptr = stackalloc SimpleStruct[checked(count * 2)]; #if !(ROSLYN && OPT) // unused stackalloc gets optimized out by roslyn SimpleStruct* ptr2 = stackalloc SimpleStruct[10]; #endif ptr->X = count; ptr[1].X = ptr->X; for (int i = 2; i < 10; i++) { ptr[i].X = count; } return UsePointer(&ptr->Y); } unsafe ~UnsafeCode() { PassPointerAsRefParameter(NullPointer); } private unsafe void Issue990() { Data data = default(Data); Data* ptr = &data; ConvertIntToFloat(ptr->Position.GetHashCode()); } private unsafe static void Issue1021(ref byte* bytePtr, ref short* shortPtr) { bytePtr += 4; shortPtr += 2; bytePtr -= 4; shortPtr = (short*)((byte*)shortPtr - 3); } private static T Get() { return default(T); } private unsafe static ResultStruct NestedFixedBlocks(byte[] array) { try { fixed (byte* ptr = array) { fixed (byte* ptr2 = Get()) { return new ResultStruct(ptr, ptr2); } } } finally { Console.WriteLine("Finally"); } } private unsafe static object CreateBuffer(int length, byte* ptr) { throw new NotImplementedException(); } private unsafe static object Issue1386(int arraySize, bool createFirstBuffer) { if (createFirstBuffer) { byte[] array = new byte[arraySize]; Console.WriteLine("first fixed"); fixed (byte* ptr = array) { return CreateBuffer(array.Length, ptr); } } byte[] array2 = new byte[arraySize]; Console.WriteLine("second fixed"); fixed (byte* ptr2 = array2) { return CreateBuffer(array2.Length, ptr2); } } private unsafe static void Issue1499(StructWithFixedSizeMembers value, int index) { int num = value.Integers[index]; num.ToString(); } #if CS73 private unsafe static int Issue2287(ref StructWithFixedSizeMembers value) { return value.Integers[0] + value.Integers[1]; } #endif private unsafe static int Issue2305(StructWithFixedSizeMembers value, StringComparison s) { return value.Integers[(int)s]; } #if CS90 private unsafe static void* CastNIntToVoidPtr(nint intptr) { return (void*)intptr; } private unsafe static void* CastNIntToVoidPtr(nuint intptr) { return (void*)intptr; } #endif #if !(CS110 && NET70) private unsafe static void* CastToVoidPtr(IntPtr intptr) { return (void*)intptr; } private unsafe static void* CastToVoidPtr(UIntPtr intptr) { return (void*)intptr; } #endif private unsafe static void* CastToVoidPtr(int* intptr) { return intptr; } public unsafe void ConditionalPointer(bool a, int* ptr) { UsePointer(a ? ptr : null); } public unsafe void UseArrayOfPointers(int*[] arr) { for (int i = 0; i < arr.Length; i++) { arr[i] = null; } } public unsafe void PassNullPointer1() { PointerReferenceExpression(null); } public unsafe void PassNullPointer2() { UseArrayOfPointers(null); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs ================================================ // Copyright (c) 2019 Daniel Grunwald // // 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. namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class T01Issue1574 { private struct A { private bool val; public static implicit operator bool(A a) { return a.val; } } private struct C { private int val; public static implicit operator C(bool b) { return default(C); } } private C ChainedConversion() { return (bool)default(A); } public void Call_Overloaded() { Overloaded((bool)default(A)); } private void Overloaded(A a) { } private void Overloaded(bool a) { } } internal class T02BothDirectAndChainedConversionPossible { private struct A { private bool val; public static implicit operator bool(A a) { return a.val; } } private struct C { private int val; public static implicit operator C(bool b) { return default(C); } public static implicit operator C(A a) { return default(C); } public static bool operator ==(C a, C b) { return true; } public static bool operator !=(C a, C b) { return false; } } private C DirectConvert(A a) { return a; } private C IndirectConvert(A a) { return (bool)a; } private C? LiftedDirectConvert(A? a) { return a; } private C? LiftedIndirectConvert(A? a) { return (bool?)a; } private bool Compare(A a, C c) { return a == c; } private void LiftedCompare(A? a, C? c) { UseBool(a == c); UseBool(a == default(C)); UseBool(c == default(A)); } private void UseBool(bool b) { } } #if CS72 internal class T03ConversionWithInArgument { private struct T { private byte dummy; public static implicit operator T(in int val) { return default(T); } public static explicit operator T(in long val) { return default(T); } } private struct U { private byte dummy; public static implicit operator T(in U u) { return default(T); } public static explicit operator int(in U u) { return 0; } } private void UseT(T t) { } private int Test(int i, long l, U u) { UseT(i); UseT((T)l); UseT(u); return (int)u; } } #endif } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Runtime.InteropServices; using System.Threading; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class Using { [StructLayout(LayoutKind.Sequential, Size = 1)] private struct UsingStruct : IDisposable { public UsingStruct(int i) { Console.WriteLine(i); } void IDisposable.Dispose() { throw new NotImplementedException(); } } private struct TypeA_Issue3385 : IDisposable { private int dummy; public void Dispose() { } #if !ROSLYN3 public static implicit operator TypeB_Issue3385(TypeA_Issue3385 a) { return default(TypeB_Issue3385); } #else public static implicit operator TypeB_Issue3385(in TypeA_Issue3385 a) { return default(TypeB_Issue3385); } #endif } private struct TypeB_Issue3385 { private int dummy; } #if CS80 [StructLayout(LayoutKind.Sequential, Size = 1)] public ref struct UsingRefStruct { public int i; public UsingRefStruct(int i) { this.i = i; Console.WriteLine(i); } public void Dispose() { throw new NotImplementedException(); } } #endif #if LEGACY_CSC // roslyn optimizes out the try-finally; mcs has a compiler bug on using(null-literal) public void SimpleUsingNullStatement() { using (null) { Console.WriteLine("using (null)"); } } #endif public void SimpleUsingExpressionStatement() { using (new MemoryStream()) { Console.WriteLine("using-body"); } } public void SimpleUsingExpressionStatementWithDeclaration() { using (MemoryStream memoryStream = new MemoryStream()) { memoryStream.WriteByte(42); Console.WriteLine("using-body: " + memoryStream.Position); } } public void UsingStatementThatChangesTheVariable() { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); using (cancellationTokenSource) { cancellationTokenSource = new CancellationTokenSource(); } cancellationTokenSource.Cancel(); } public void UsingStatementOnStruct() { using (new UsingStruct(1)) { Console.WriteLine("using-body"); } } public void UsingStatementOnStructWithVariable() { using (UsingStruct usingStruct = new UsingStruct(2)) { Console.WriteLine("using-body: " + usingStruct); } } private void UsingStatementOnNullableStruct(UsingStruct? us) { using (us) { Console.WriteLine("using-body: " + us.ToString()); } } public void GenericUsing(T t) where T : IDisposable { using (t) { Console.WriteLine(t); } } public void GenericStructUsing(T t) where T : struct, IDisposable { using (t) { Console.WriteLine(t); } } public void GenericClassUsing(T t) where T : class, IDisposable { using (t) { Console.WriteLine(t); } } public void GenericNullableUsing(T? t) where T : struct, IDisposable { using (t) { Console.WriteLine(t); } } #if CS80 public void UsingRefStruct1(UsingRefStruct s) { using (s) { Console.WriteLine(s.i); } } #endif public static void Issue3385() { #if ROSLYN3 using (TypeA_Issue3385 a = default(TypeA_Issue3385)) #else using (TypeA_Issue3385 typeA_Issue = default(TypeA_Issue3385)) #endif { #if ROSLYN3 Empty(a); #else Empty(typeA_Issue); #endif } } private static void Empty(TypeB_Issue3385 b) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/UsingVariables.cs ================================================ // Copyright (c) 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 // 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; #if !NET40 using System.Threading.Tasks; #endif namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class UsingVariables { public IDisposable GetDisposable() { return null; } private void Use(IDisposable disposable) { } #if !NET40 public IAsyncDisposable GetAsyncDisposable() { return null; } private void Use(IAsyncDisposable asyncDisposable) { } #endif public void SimpleUsingVar() { Console.WriteLine("before using"); using IDisposable disposable = GetDisposable(); Console.WriteLine("inside using"); Use(disposable); } public void NotAUsingVar() { Console.WriteLine("before using"); using (IDisposable disposable = GetDisposable()) { Console.WriteLine("inside using"); Use(disposable); } Console.WriteLine("outside using"); } public void UsingVarInNestedBlocks(bool condition) { if (condition) { using IDisposable disposable = GetDisposable(); Console.WriteLine("inside using"); Use(disposable); } Console.WriteLine("outside using"); } public void MultipleUsingVars(IDisposable other) { Console.WriteLine("before using"); using IDisposable disposable = GetDisposable(); Console.WriteLine("inside outer using"); using IDisposable disposable2 = other; Console.WriteLine("inside inner using"); Use(disposable); Use(disposable2); } #if !NET40 public async Task SimpleUsingVarAsync() { Console.WriteLine("before using"); await using IAsyncDisposable asyncDisposable = GetAsyncDisposable(); Console.WriteLine("inside using"); Use(asyncDisposable); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public static class ValueTypes { public struct S { public int Field; public int Property { get { return Field; } set { Field = value; } } public S(int field) { Field = field; } public void SetField() { Field = 5; } public void MethodCalls() { SetField(); Test(this); Test(ref this); } private static void Test(S byVal) { } private static void Test(ref S byRef) { } public void CallOnThis() { // distinguish calls on 'this' from calls on a copy of 'this' SetField(); S s = this; s.SetField(); } public void UseField(int val) { UseField(Get().Field); } } #if CS72 public readonly struct R { public readonly int Field; public int Property { get { return Field; } set { Console.WriteLine("Setter on readonly struct"); } } public void Method() { } public void CallOnThis() { // distinguish calls on 'this' from calls on a copy of 'this' Method(); R r = this; r.Method(); } } #endif #if ROSLYN // Roslyn optimizes out the explicit default-initialization private static readonly S ReadOnlyS; private static S MutableS; #else private static readonly S ReadOnlyS = default(S); private static S MutableS = default(S); #endif private static volatile int VolatileInt; #if CS72 private static readonly R ReadOnlyR; private static R MutableR; #endif public static void CallMethodViaField() { ReadOnlyS.SetField(); MutableS.SetField(); S mutableS = MutableS; mutableS.SetField(); #if CS72 ReadOnlyR.Method(); R readOnlyR = ReadOnlyR; readOnlyR.Method(); R mutableR = MutableR; mutableR.Method(); #endif } #if !(ROSLYN && OPT) || COPY_PROPAGATION_FIXED public static S InitObj1() { S result = default(S); MakeArray(); return result; } #endif public static S InitObj2() { return default(S); } public static void InitObj3(out S p) { p = default(S); } public static S CallValueTypeCtor() { return new S(10); } public static S Copy1(S p) { return p; } public static S Copy2(ref S p) { return p; } public static void Copy3(S p, out S o) { o = p; } public static void Copy4(ref S p, out S o) { o = p; } public static void Copy4b(ref S p, out S o) { // test passing through by-ref arguments Copy4(ref p, out o); } public static void Issue56(int i, out string str) { str = "qq"; str += i; } public static void CopyAroundAndModifyField(S s) { S s2 = s; s2.Field += 10; s = s2; } private static int[] MakeArray() { return null; } public static void IncrementArrayLocation() { MakeArray()[Environment.TickCount]++; } public static bool Is(object obj) { return obj is S; } public static bool IsNullable(object obj) { return obj is S?; } public static S? As(object obj) { return obj as S?; } public static S OnlyChangeTheCopy(S p) { S s = p; s.SetField(); return p; } public static void UseRefBoolInCondition(ref bool x) { if (x) { Console.WriteLine("true"); } } public static void CompareNotEqual0IsReallyNotEqual(IComparable a) { if (a.CompareTo(0) != 0) { Console.WriteLine("true"); } } public static void CompareEqual0IsReallyEqual(IComparable a) { if (a.CompareTo(0) == 0) { Console.WriteLine("true"); } } public static T Get() { return default(T); } public static void CallOnTemporary() { // Method can be called directly on temporaries Get().MethodCalls(); // Setting a property requires a temporary to avoid // CS1612 Cannot modify the return value of 'InitObj2()' because it is not a variable S s = Get(); s.Property = 1; #if CS72 Get().Method(); R r = Get(); r.Property = 2; #endif } public static void CallOnFieldOfTemporary() { Get().Field.ToString(); } public static string CallOnIntegerConstant() { return ulong.MaxValue.ToString(); } public static void InliningDefaultValue() { Test(default(DateTime).GetType()); Test(default(DateTime).ToString()); } public static void Test(object x) { } #if CS120 public static void AcceptIn(in S o) { } public static void AcceptRefReadOnly(ref readonly S o) { } private static void Use(in S param) { AcceptIn(new S(5)); S o = new S(10); AcceptRefReadOnly(in o); AcceptIn(in param); AcceptRefReadOnly(in param); } #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNaming.cs ================================================ #if !OPT using System; #endif namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class VariableNaming { private enum MyEnum { VALUE1 = 1, VALUE2 } private class C { public string Name; public string Text; } private void Test(string text, C c) { #if CS70 _ = c.Name; #else string name = c.Name; #endif } private void Test2(string text, C c) { #if CS70 _ = c.Text; #else string text2 = c.Text; #endif } #if !OPT private void Issue1841() { C gen1 = new C(); C gen2 = new C(); C gen3 = new C(); C gen4 = new C(); } private void Issue1881() { #pragma warning disable CS0219 MyEnum enumLocal1 = MyEnum.VALUE1; MyEnum enumLocal2 = (MyEnum)0; enumLocal2 = MyEnum.VALUE1; object enumLocal3 = MyEnum.VALUE2; object enumLocal4 = new object(); enumLocal4 = MyEnum.VALUE2; ValueType enumLocal5 = MyEnum.VALUE1; ValueType enumLocal6 = (MyEnum)0; enumLocal6 = MyEnum.VALUE2; #pragma warning restore CS0219 } #endif private static void NestedForLoopTest(int sizeX, int sizeY, int[] array) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { array[y * sizeX + x] = 0; } } #if !EXPECTED_OUTPUT || (LEGACY_CSC && !OPT) for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { array[y * sizeX + x] = 1; } } #else for (int i = 0; i < sizeY; i++) { for (int j = 0; j < sizeX; j++) { array[i * sizeX + j] = 1; } } #endif } private static void NestedForLoopTest2() { for (int i = 0; i < 10; i++) { Nop(i); } #if EXPECTED_OUTPUT && !(LEGACY_CSC && !OPT) for (int j = 0; j < 10; j++) { Nop(j); } for (int k = 0; k < 10; k++) { Nop(k); } for (int l = 0; l < 10; l++) { Nop(l); } for (int m = 0; m < 10; m++) { for (int n = 0; n < 10; n++) { Nop(n); } } for (int num = 0; num < 10; num++) { for (int num2 = 0; num2 < 10; num2++) { Nop(num2); } } #else for (int i = 0; i < 10; i++) { Nop(i); } for (int i = 0; i < 10; i++) { Nop(i); } for (int i = 0; i < 10; i++) { Nop(i); } for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { Nop(j); } } for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { Nop(j); } } #endif } private static void Nop(int v) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs ================================================ using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class VariableNamingWithoutSymbols { private class C { public string Name; public string Text; } private void Test(string text, C c) { #if CS70 _ = c.Name; #else string name = c.Name; #endif } private void Test2(string text, C c) { #if CS70 _ = c.Text; #else string text2 = c.Text; #endif } private static IDisposable GetData() { return null; } private static void UseData(IDisposable data) { } private static IEnumerable GetItems() { throw null; } private static byte[] GetMemory() { throw null; } private static void Test(int item) { foreach (int item2 in GetItems()) { Console.WriteLine(item2); } } private static void Test(IDisposable data) { #if CS80 using IDisposable data2 = GetData(); UseData(data2); #else using (IDisposable data2 = GetData()) { UseData(data2); } #endif } private unsafe static void Test(byte[] memory) { fixed (byte* memory2 = GetMemory()) { Console.WriteLine(*memory2); } } private static void ForLoopNamingConflict(int i) { for (int j = 0; j < i; j++) { Console.WriteLine(i + " of " + j); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/WellKnownConstants.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class WellKnownConstants { public const byte ByteMaxValue = byte.MaxValue; public const byte ByteMinValue = 0; public const sbyte SByteMaxValue = sbyte.MaxValue; public const sbyte SByteMinValue = sbyte.MinValue; public const ushort UShortMaxValue = ushort.MaxValue; public const ushort UShortMinValue = 0; public const short ShortMaxValue = short.MinValue; public const short ShortMinValue = short.MaxValue; public const uint UIntMaxValue = uint.MaxValue; public const uint UIntMinValue = 0u; public const int IntMaxValue = int.MaxValue; public const int IntMinValue = int.MinValue; public const ulong ULongMaxValue = ulong.MaxValue; public const ulong ULongMinValue = 0uL; public const long LongMaxValue = long.MaxValue; public const long LongMinValue = long.MinValue; // This constant is (1 / (double)long.MaxValue). Note that the (double) cast involves // loss of precision: long.MaxValue is rounded up to (long.MaxValue+1), which is a power of two. // The division then is exact, resulting in the double 0x3c00000000000000. // When trying to represent this as a fraction, we get (long.MaxValue+1) as divisor, which // does not fit type long, but compares equals to long.MaxValue due to the long->double conversion. public const double Double_One_Div_LongMaxValue = 1.0842021724855044E-19; public const float FloatZero = 0f; public const float FloatMinusZero = -0f; public const float FloatNaN = float.NaN; public const float FloatPositiveInfinity = float.PositiveInfinity; public const float FloatNegativeInfinity = float.NegativeInfinity; public const float FloatMaxValue = float.MaxValue; public const float FloatMinValue = float.MinValue; public const float FloatEpsilon = float.Epsilon; public const double DoubleZero = 0.0; public const double DoubleMinusZero = -0.0; public const double DoubleNaN = double.NaN; public const double DoublePositiveInfinity = double.PositiveInfinity; public const double DoubleNegativeInfinity = double.NegativeInfinity; public const double DoubleMaxValue = double.MaxValue; public const double DoubleMinValue = double.MinValue; public const double DoubleEpsilon = double.Epsilon; public const decimal DecimalMaxValue = decimal.MaxValue; public const decimal DecimalMinValue = decimal.MinValue; public const float Float_One = 1f; public const double Double_One = 1.0; public const float Float_Two = 2f; public const double Double_Two = 2.0; public const float Float_Sixth = 1f / 6f; public const double Double_Sixth = 1.0 / 6.0; public const float Float_Tenth = 0.1f; public const double Double_Tenth = 0.1; #if ROSLYN2 && !NET40 public const float Float_PI = MathF.PI; public const float Float_HalfOfPI = MathF.PI / 2f; public const float Float_QuarterOfPI = MathF.PI / 4f; public const float Float_PITimes2 = MathF.PI * 2f; public const float Float_3QuartersOfPI = MathF.PI * 3f / 4f; public const float Float_PIDiv360 = MathF.PI / 360f; public const float Float_PIDiv16 = MathF.PI / 16f; public const float Float_PIDiv32 = MathF.PI / 32f; public const float Float_PIInverseFraction = 1f / MathF.PI; public const float Float_PIInverseFraction2 = 2f / MathF.PI; public const float Float_PIInverseFraction5 = 5f / MathF.PI; public const float Float_PITimes90 = MathF.PI * 90f; public const float Float_PITimes180 = MathF.PI * 180f; public const float Float_LooksLikePI = 3.1415925f; public const float Float_LooksLikePI2 = 3.14159f; public const float Float_LooksLikePI3 = 3.141f; public const float Float_BeforePI = 3.1415925f; public const float Float_AfterPI = 3.141593f; public const float Float_Negated_PI = -MathF.PI; public const float Float_Negated_HalfOfPI = -MathF.PI / 2f; public const float Float_Negated_QuarterOfPI = -MathF.PI / 4f; public const float Float_Negated_PITimes2 = MathF.PI * -2f; public const float Float_Negated_3QuartersOfPI = MathF.PI * -3f / 4f; public const float Float_Negated_PIDiv360 = -MathF.PI / 360f; public const float Float_Negated_PIDiv16 = -MathF.PI / 16f; public const float Float_Negated_PIDiv32 = -MathF.PI / 32f; public const float Float_Negated_PIInverseFraction = -1f / MathF.PI; public const float Float_Negated_PIInverseFraction2 = -2f / MathF.PI; public const float Float_Negated_PIInverseFraction5 = -5f / MathF.PI; public const float Float_Negated_PITimes90 = MathF.PI * -90f; public const float Float_Negated_PITimes180 = MathF.PI * -180f; public const float Float_Negated_LooksLikePI = -3.141f; public const float Float_Negated_BeforePI = -3.1415925f; public const float Float_Negated_AfterPI = -3.141593f; public const float Float_E = MathF.E; public const float Float_Negated_E = -MathF.E; #else public const float Float_PI = (float)Math.PI; public const float Float_HalfOfPI = (float)Math.PI / 2f; public const float Float_QuarterOfPI = (float)Math.PI / 4f; public const float Float_PITimes2 = (float)Math.PI * 2f; public const float Float_3QuartersOfPI = (float)Math.PI * 3f / 4f; public const float Float_PIDiv360 = (float)Math.PI / 360f; public const float Float_PIDiv16 = (float)Math.PI / 16f; public const float Float_PIDiv32 = (float)Math.PI / 32f; public const float Float_PIInverseFraction = 1f / (float)Math.PI; public const float Float_PIInverseFraction2 = 2f / (float)Math.PI; public const float Float_PIInverseFraction5 = 5f / (float)Math.PI; public const float Float_PITimes90 = (float)Math.PI * 90f; public const float Float_PITimes180 = (float)Math.PI * 180f; public const float Float_LooksLikePI = 3.1415925f; public const float Float_LooksLikePI2 = 3.14159f; public const float Float_LooksLikePI3 = 3.141f; public const float Float_BeforePI = 3.1415925f; public const float Float_AfterPI = 3.141593f; public const float Float_Negated_PI = -(float)Math.PI; public const float Float_Negated_HalfOfPI = -(float)Math.PI / 2f; public const float Float_Negated_QuarterOfPI = -(float)Math.PI / 4f; public const float Float_Negated_PITimes2 = (float)Math.PI * -2f; public const float Float_Negated_3QuartersOfPI = (float)Math.PI * -3f / 4f; public const float Float_Negated_PIDiv360 = -(float)Math.PI / 360f; public const float Float_Negated_PIDiv16 = -(float)Math.PI / 16f; public const float Float_Negated_PIDiv32 = -(float)Math.PI / 32f; public const float Float_Negated_PIInverseFraction = -1f / (float)Math.PI; public const float Float_Negated_PIInverseFraction2 = -2f / (float)Math.PI; public const float Float_Negated_PIInverseFraction5 = -5f / (float)Math.PI; public const float Float_Negated_PITimes90 = (float)Math.PI * -90f; public const float Float_Negated_PITimes180 = (float)Math.PI * -180f; public const float Float_Negated_LooksLikePI = -3.141f; public const float Float_Negated_BeforePI = -3.1415925f; public const float Float_Negated_AfterPI = -3.141593f; public const float Float_E = (float)Math.E; public const float Float_Negated_E = -(float)Math.E; #endif public const double Double_PI = Math.PI; public const double Double_HalfOfPI = Math.PI / 2.0; public const double Double_QuarterOfPI = Math.PI / 4.0; public const double Double_PITimes2 = Math.PI * 2.0; public const double Double_3QuartersOfPI = Math.PI * 3.0 / 4.0; public const double Double_PIDiv360 = Math.PI / 360.0; public const double Double_PIDiv16 = Math.PI / 16.0; public const double Double_PIDiv32 = Math.PI / 32.0; public const double Double_PIInverseFraction = 1.0 / Math.PI; public const double Double_PIInverseFraction2 = 2.0 / Math.PI; public const double Double_PIInverseFraction5 = 5.0 / Math.PI; public const double Double_PITimes90 = Math.PI * 90.0; public const double Double_PITimes180 = Math.PI * 180.0; public const double Double_LooksLikePI = 3.1415926; public const double Double_LooksLikePI2 = 3.14159; public const double Double_LooksLikePI3 = 3.141; public const double Double_BeforePI = 3.1415926535897927; public const double Double_AfterPI = 3.1415926535897936; public const double Double_Negated_PI = -Math.PI; public const double Double_Negated_HalfOfPI = -Math.PI / 2.0; public const double Double_Negated_QuarterOfPI = -Math.PI / 4.0; public const double Double_Negated_PITimes2 = Math.PI * -2.0; public const double Double_Negated_3QuartersOfPI = Math.PI * -3.0 / 4.0; public const double Double_Negated_PIDiv360 = -Math.PI / 360.0; public const double Double_Negated_PIDiv16 = -Math.PI / 16.0; public const double Double_Negated_PIDiv32 = -Math.PI / 32.0; public const double Double_Negated_PIInverseFraction = -1.0 / Math.PI; public const double Double_Negated_PIInverseFraction2 = -2.0 / Math.PI; public const double Double_Negated_PIInverseFraction5 = -5.0 / Math.PI; public const double Double_Negated_PITimes90 = Math.PI * -90.0; public const double Double_Negated_PITimes180 = Math.PI * -180.0; public const double Double_Negated_LooksLikePI = -3.141; public const double Double_Negated_BeforePI = -3.1415926535897927; public const double Double_Negated_AfterPI = -3.1415926535897936; public const double Double_E = Math.E; public const double Double_BeforeE = 2.7182818284590446; public const double Double_AfterE = 2.7182818284590455; public const double Double_Negated_E = -Math.E; public const double Double_Negated_BeforeE = -2.7182818284590446; public const double Double_Negated_AfterE = -2.7182818284590455; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal struct StructWithYieldReturn { private int val; public IEnumerable Count() { yield return val++; yield return val++; } } public class YieldReturnPrettyTest { private int fieldOnThis; public static IEnumerable YieldChars { get { yield return 'a'; yield return 'b'; yield return 'c'; } } internal static void Print(string name, IEnumerator enumerator) { Console.WriteLine(name + ": Test start"); while (enumerator.MoveNext()) { Console.WriteLine(name + ": " + enumerator.Current); } } public static IEnumerable SimpleYieldReturn() { yield return "A"; yield return "B"; yield return "C"; } public static IEnumerator SimpleYieldReturnEnumerator() { yield return "A"; yield return "B"; yield return "C"; } public IEnumerable YieldReturnParameters(int p) { yield return p; yield return fieldOnThis; } public IEnumerator YieldReturnParametersEnumerator(int p) { yield return p; yield return fieldOnThis; } public static IEnumerable YieldReturnInLoop() { for (int i = 0; i < 100; i++) { yield return i; } } public static IEnumerable YieldReturnWithTryFinally() { yield return 0; try { yield return 1; } finally { Console.WriteLine("Finally!"); } yield return 2; } public static IEnumerable YieldReturnInLock1(object o) { lock (o) { yield return 1; } } public static IEnumerable YieldReturnInLock2(object o) { lock (o) { yield return 1; o = null; yield return 2; } } public static IEnumerable YieldReturnWithNestedTryFinally(bool breakInMiddle) { Console.WriteLine("Start of method - 1"); yield return "Start of method"; Console.WriteLine("Start of method - 2"); try { Console.WriteLine("Within outer try - 1"); yield return "Within outer try"; Console.WriteLine("Within outer try - 2"); try { Console.WriteLine("Within inner try - 1"); yield return "Within inner try"; Console.WriteLine("Within inner try - 2"); if (breakInMiddle) { Console.WriteLine("Breaking..."); yield break; } Console.WriteLine("End of inner try - 1"); yield return "End of inner try"; Console.WriteLine("End of inner try - 2"); } finally { Console.WriteLine("Inner Finally"); } Console.WriteLine("End of outer try - 1"); yield return "End of outer try"; Console.WriteLine("End of outer try - 2"); } finally { Console.WriteLine("Outer Finally"); } Console.WriteLine("End of method - 1"); yield return "End of method"; Console.WriteLine("End of method - 2"); } public static IEnumerable YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable input) { // outer try-finally block foreach (string line in input) { // nested try-finally block try { yield return line; } finally { Console.WriteLine("Processed " + line); } } yield return "A"; yield return "B"; yield return "C"; yield return "D"; yield return "E"; yield return "F"; // outer try-finally block foreach (string item in input) { yield return item.ToUpper(); } } public static IEnumerable> YieldReturnWithAnonymousMethods1(IEnumerable input) { foreach (string line in input) { yield return () => line; } } public static IEnumerable> YieldReturnWithAnonymousMethods2(IEnumerable input) { foreach (string item in input) { string copy = item; yield return () => copy; } } public static IEnumerable GetEvenNumbers(int n) { for (int i = 0; i < n; i++) { if (i % 2 == 0) { yield return i; } } } public static IEnumerable ExceptionHandling() { yield return 'a'; try { Console.WriteLine("1 - try"); } catch (Exception) { Console.WriteLine("1 - catch"); } yield return 'b'; try { try { Console.WriteLine("2 - try"); } finally { Console.WriteLine("2 - finally"); } yield return 'c'; } finally { Console.WriteLine("outer finally"); } } public static IEnumerable YieldBreakInCatch() { yield return 0; try { Console.WriteLine("In Try"); } catch { // yield return is not allowed in catch, but yield break is yield break; } yield return 1; } public static IEnumerable YieldBreakInCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); } catch { // yield return is not allowed in catch, but yield break is // Note that pre-roslyn, this code triggers a compiler bug: // If the finally block throws an exception, it ends up getting // called a second time. yield break; } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } catch { Console.WriteLine("Catch"); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryFinallyInTryFinally(bool b) { try { yield return 0; try { Console.WriteLine("In Try"); if (b) { // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } } finally { Console.WriteLine("Inner Finally"); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakOnly() { yield break; } public static IEnumerable UnconditionalThrowInTryFinally() { // Here, MoveNext() doesn't call the finally methods at all // (only indirectly via Dispose()) try { yield return 0; throw new NotImplementedException(); } finally { Console.WriteLine("Finally"); } } public static IEnumerable NestedTryFinallyStartingOnSamePosition() { // The first user IL instruction is already in 2 nested try blocks. try { try { yield return 0; } finally { Console.WriteLine("Inner Finally"); } } finally { Console.WriteLine("Outer Finally"); } } public static IEnumerable LocalInFinally(T a) where T : IDisposable { yield return 1; try { yield return 2; } finally { T val = a; val.Dispose(); val.Dispose(); } yield return 3; } public static IEnumerable GenericYield() where T : new() { T val = new T(); for (int i = 0; i < 3; i++) { yield return val; } } public static IEnumerable MultipleYieldBreakInTryFinally(int i) { try { if (i == 2) { yield break; } while (i < 40) { if (i % 2 == 0) { yield break; } i++; yield return i; } } finally { Console.WriteLine("finally"); } Console.WriteLine("normal exit"); } internal IEnumerable ForLoopWithYieldReturn(int end, int evil) { // This loop needs to pick the implicit "yield break;" as exit point // in order to produce pretty code; not the "throw" which would // be a less-pretty option. for (int i = 0; i < end; i++) { if (i == evil) { throw new InvalidOperationException("Found evil number"); } yield return i; } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/.gitignore ================================================ /*.res /*.dll /*.exe /*.pdb ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class DisplayClass { public Program thisField; public int field1; public string field2; } public class NestedDisplayClass { public DisplayClass field3; public int field1; public string field2; } public class Program { public int Rand() { throw new NotImplementedException(); } public void Test1() { int field1 = 42; string field2 = "Hello World!"; Console.WriteLine("{0} {1}", field1, field2); } public void Test2() { DisplayClass displayClass = new DisplayClass { field1 = 42, field2 = "Hello World!" }; Console.WriteLine("{0} {1}", displayClass.field1, displayClass.GetHashCode()); } public void Test3() { DisplayClass displayClass = new DisplayClass { field1 = 42, field2 = "Hello World!" }; Console.WriteLine("{0} {1}", displayClass.field1, displayClass); } public void Test4() { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = 42, field2 = "Hello World!" }; int field1 = 4711; string field2 = "ILSpy"; DisplayClass field3; if (displayClass.field1 > 100) { field3 = displayClass; } else { field3 = null; } Console.WriteLine("{0} {1}", displayClass, field3); } public void Test5() { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = 42, field2 = "Hello World!" }; int field1 = 4711; string field2 = "ILSpy"; DisplayClass field3; if (displayClass.field1 > 100) { field3 = displayClass; } else { field3 = null; } Console.WriteLine("{0} {1}", field2 + field1, field3); } public void Issue1898(int i) { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = i }; int field5 = default(int); string field4 = default(string); DisplayClass field3 = default(DisplayClass); while (true) { switch (Rand()) { case 1: field5 = Rand(); continue; case 2: field4 = Rand().ToString(); continue; case 3: field3 = displayClass; continue; } Console.WriteLine(field5); Console.WriteLine(field4); Console.WriteLine(field3); } } public void Test6(int i) { int field1 = i; string field2 = "Hello World!"; if (i < 0) { i = -i; } Console.WriteLine("{0} {1}", field1, field2); } public void Test6b(int i) { int num = i; int field1 = num; string field2 = "Hello World!"; if (num < 0) { num = -num; } Console.WriteLine("{0} {1}", field1, field2); } public void Test7(int i) { int field1 = i; string field2 = "Hello World!"; Console.WriteLine("{0} {1} {2}", field1++, field2, i); } public void Test8(int i) { int field1 = i; string field2 = "Hello World!"; i = 42; Console.WriteLine("{0} {1}", field1, field2); } public void Test8b(int i) { int num = i; int field1 = num; string field2 = "Hello World!"; num = 42; Console.WriteLine("{0} {1}", field1, field2); } // public void Test9() // { // Program thisField = this; // int field1 = 1; // string field2 = "Hello World!"; // thisField = new Program(); // Console.WriteLine("{0} {1}", this, thisField); // } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class DisplayClass { public Program thisField; public int field1; public string field2; } public class NestedDisplayClass { public DisplayClass field3; public int field1; public string field2; } public class Program { public int Rand() { throw new NotImplementedException(); } public void Test1() { DisplayClass displayClass = new DisplayClass { field1 = 42, field2 = "Hello World!" }; Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2); } public void Test2() { DisplayClass displayClass = new DisplayClass { field1 = 42, field2 = "Hello World!" }; Console.WriteLine("{0} {1}", displayClass.field1, displayClass.GetHashCode()); } public void Test3() { DisplayClass displayClass = new DisplayClass { field1 = 42, field2 = "Hello World!" }; Console.WriteLine("{0} {1}", displayClass.field1, displayClass); } public void Test4() { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = 42, field2 = "Hello World!" }; NestedDisplayClass nested = new NestedDisplayClass { field1 = 4711, field2 = "ILSpy" }; if (displayClass.field1 > 100) { nested.field3 = displayClass; } else { nested.field3 = null; } Console.WriteLine("{0} {1}", displayClass, nested.field3); } public void Test5() { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = 42, field2 = "Hello World!" }; NestedDisplayClass nested = new NestedDisplayClass { field1 = 4711, field2 = "ILSpy" }; if (displayClass.field1 > 100) { nested.field3 = displayClass; } else { nested.field3 = null; } Console.WriteLine("{0} {1}", nested.field2 + nested.field1, nested.field3); } public void Issue1898(int i) { DisplayClass displayClass = new DisplayClass { thisField = this, field1 = i }; NestedDisplayClass nested = new NestedDisplayClass(); while (true) { switch (Rand()) { case 1: nested.field1 = Rand(); break; case 2: nested.field2 = Rand().ToString(); break; case 3: nested.field3 = displayClass; break; default: Console.WriteLine(nested.field1); Console.WriteLine(nested.field2); Console.WriteLine(nested.field3); break; } } } public void Test6(int i) { DisplayClass displayClass = new DisplayClass { field1 = i, field2 = "Hello World!" }; if (i < 0) { i = -i; } Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2); } public void Test6b(int i) { int num = i; DisplayClass displayClass = new DisplayClass { field1 = num, field2 = "Hello World!" }; if (num < 0) { num = -num; } Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2); } public void Test7(int i) { DisplayClass displayClass = new DisplayClass { field1 = i, field2 = "Hello World!" }; Console.WriteLine("{0} {1} {2}", displayClass.field1++, displayClass.field2, i); } public void Test8(int i) { DisplayClass displayClass = new DisplayClass { field1 = i, field2 = "Hello World!" }; i = 42; Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2); } public void Test8b(int i) { int num = i; DisplayClass displayClass = new DisplayClass { field1 = num, field2 = "Hello World!" }; num = 42; Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2); } // public void Test9() // { // DisplayClass displayClass = new DisplayClass { // thisField = this, // field1 = 1, // field2 = "Hello World!" // }; // displayClass.thisField = new Program(); // Console.WriteLine("{0} {1}", this, displayClass.thisField); // } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp4B37 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp4B37.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method DisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3 .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NestedDisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program extends [mscorlib]System.Object { .method public hidebysig instance int32 Rand() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method Program::Rand .method public hidebysig instance void Test1() cil managed { // Code size 55 (0x37) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0030: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0035: nop IL_0036: ret } // end of method Program::Test1 .method public hidebysig instance void Test2() cil managed { // Code size 60 (0x3c) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: callvirt instance int32 [mscorlib]System.Object::GetHashCode() IL_0030: box [mscorlib]System.Int32 IL_0035: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003a: nop IL_003b: ret } // end of method Program::Test2 .method public hidebysig instance void Test3() cil managed { // Code size 50 (0x32) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0030: nop IL_0031: ret } // end of method Program::Test3 .method public hidebysig instance void Test4() cil managed { // Code size 114 (0x72) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, bool V_2) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldc.i4.s 42 IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0015: dup IL_0016: ldstr "Hello World!" IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0020: stloc.0 IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0026: dup IL_0027: ldc.i4 0x1267 IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0031: dup IL_0032: ldstr "ILSpy" IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003c: stloc.1 IL_003d: ldloc.0 IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0043: ldc.i4.s 100 IL_0045: cgt IL_0047: stloc.2 IL_0048: ldloc.2 IL_0049: brfalse.s IL_0056 IL_004b: nop IL_004c: ldloc.1 IL_004d: ldloc.0 IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0053: nop IL_0054: br.s IL_005f IL_0056: nop IL_0057: ldloc.1 IL_0058: ldnull IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_005e: nop IL_005f: ldstr "{0} {1}" IL_0064: ldloc.0 IL_0065: ldloc.1 IL_0066: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_006b: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0070: nop IL_0071: ret } // end of method Program::Test4 .method public hidebysig instance void Test5() cil managed { // Code size 135 (0x87) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, bool V_2) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldc.i4.s 42 IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0015: dup IL_0016: ldstr "Hello World!" IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0020: stloc.0 IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0026: dup IL_0027: ldc.i4 0x1267 IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0031: dup IL_0032: ldstr "ILSpy" IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003c: stloc.1 IL_003d: ldloc.0 IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0043: ldc.i4.s 100 IL_0045: cgt IL_0047: stloc.2 IL_0048: ldloc.2 IL_0049: brfalse.s IL_0056 IL_004b: nop IL_004c: ldloc.1 IL_004d: ldloc.0 IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0053: nop IL_0054: br.s IL_005f IL_0056: nop IL_0057: ldloc.1 IL_0058: ldnull IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_005e: nop IL_005f: ldstr "{0} {1}" IL_0064: ldloc.1 IL_0065: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_006a: ldloc.1 IL_006b: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0070: call instance string [mscorlib]System.Int32::ToString() IL_0075: call string [mscorlib]System.String::Concat(string, string) IL_007a: ldloc.1 IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0080: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0085: nop IL_0086: ret } // end of method Program::Test5 .method public hidebysig instance void Issue1898(int32 i) cil managed { // Code size 151 (0x97) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, int32 V_2, int32 V_3, int32 V_4, bool V_5) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldarg.1 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: stloc.0 IL_0015: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_001a: stloc.1 IL_001b: br.s IL_0092 IL_001d: nop IL_001e: ldarg.0 IL_001f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0024: stloc.3 IL_0025: ldloc.3 IL_0026: stloc.2 IL_0027: ldloc.2 IL_0028: ldc.i4.1 IL_0029: sub IL_002a: switch ( IL_003d, IL_004b, IL_0062) IL_003b: br.s IL_006b IL_003d: ldloc.1 IL_003e: ldarg.0 IL_003f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0044: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0049: br.s IL_0091 IL_004b: ldloc.1 IL_004c: ldarg.0 IL_004d: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0052: stloc.s V_4 IL_0054: ldloca.s V_4 IL_0056: call instance string [mscorlib]System.Int32::ToString() IL_005b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0060: br.s IL_0091 IL_0062: ldloc.1 IL_0063: ldloc.0 IL_0064: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0069: br.s IL_0091 IL_006b: ldloc.1 IL_006c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0071: call void [mscorlib]System.Console::WriteLine(int32) IL_0076: nop IL_0077: ldloc.1 IL_0078: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_007d: call void [mscorlib]System.Console::WriteLine(string) IL_0082: nop IL_0083: ldloc.1 IL_0084: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0089: call void [mscorlib]System.Console::WriteLine(object) IL_008e: nop IL_008f: br.s IL_0091 IL_0091: nop IL_0092: ldc.i4.1 IL_0093: stloc.s V_5 IL_0095: br.s IL_001d } // end of method Program::Issue1898 .method public hidebysig instance void Test6(int32 i) cil managed { // Code size 68 (0x44) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, bool V_1) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldarg.1 IL_001a: ldc.i4.0 IL_001b: clt IL_001d: stloc.1 IL_001e: ldloc.1 IL_001f: brfalse.s IL_0027 IL_0021: nop IL_0022: ldarg.1 IL_0023: neg IL_0024: starg.s i IL_0026: nop IL_0027: ldstr "{0} {1}" IL_002c: ldloc.0 IL_002d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0032: box [mscorlib]System.Int32 IL_0037: ldloc.0 IL_0038: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003d: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0042: nop IL_0043: ret } // end of method Program::Test6 .method public hidebysig instance void Test6b(int32 i) cil managed { // Code size 69 (0x45) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1, bool V_2) IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0008: dup IL_0009: ldloc.0 IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000f: dup IL_0010: ldstr "Hello World!" IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldc.i4.0 IL_001d: clt IL_001f: stloc.2 IL_0020: ldloc.2 IL_0021: brfalse.s IL_0028 IL_0023: nop IL_0024: ldloc.0 IL_0025: neg IL_0026: stloc.0 IL_0027: nop IL_0028: ldstr "{0} {1}" IL_002d: ldloc.1 IL_002e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0033: box [mscorlib]System.Int32 IL_0038: ldloc.1 IL_0039: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003e: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0043: nop IL_0044: ret } // end of method Program::Test6b .method public hidebysig instance void Test7(int32 i) cil managed { // Code size 71 (0x47) .maxstack 4 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, int32 V_1) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1} {2}" IL_001e: ldloc.0 IL_001f: dup IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldc.i4.1 IL_0028: add IL_0029: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002e: ldloc.1 IL_002f: box [mscorlib]System.Int32 IL_0034: ldloc.0 IL_0035: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003a: ldarg.1 IL_003b: box [mscorlib]System.Int32 IL_0040: call void [mscorlib]System.Console::WriteLine(string, object, object, object) IL_0045: nop IL_0046: ret } // end of method Program::Test7 .method public hidebysig instance void Test8(int32 i) cil managed { // Code size 58 (0x3a) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldc.i4.s 42 IL_001b: starg.s i IL_001d: ldstr "{0} {1}" IL_0022: ldloc.0 IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0028: box [mscorlib]System.Int32 IL_002d: ldloc.0 IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0033: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0038: nop IL_0039: ret } // end of method Program::Test8 .method public hidebysig instance void Test8b(int32 i) cil managed { // Code size 59 (0x3b) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0008: dup IL_0009: ldloc.0 IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000f: dup IL_0010: ldstr "Hello World!" IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001a: stloc.1 IL_001b: ldc.i4.s 42 IL_001d: stloc.0 IL_001e: ldstr "{0} {1}" IL_0023: ldloc.1 IL_0024: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0029: box [mscorlib]System.Int32 IL_002e: ldloc.1 IL_002f: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0034: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0039: nop IL_003a: ret } // end of method Program::Test8b .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Program::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp96F5 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp96F5.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method DisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3 .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NestedDisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program extends [mscorlib]System.Object { .method public hidebysig instance int32 Rand() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0005: throw } // end of method Program::Rand .method public hidebysig instance void Test1() cil managed { // Code size 53 (0x35) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_002f: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0034: ret } // end of method Program::Test1 .method public hidebysig instance void Test2() cil managed { // Code size 58 (0x3a) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: callvirt instance int32 [mscorlib]System.Object::GetHashCode() IL_002f: box [mscorlib]System.Int32 IL_0034: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0039: ret } // end of method Program::Test2 .method public hidebysig instance void Test3() cil managed { // Code size 48 (0x30) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002f: ret } // end of method Program::Test3 .method public hidebysig instance void Test4() cil managed { // Code size 104 (0x68) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldc.i4.s 42 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: dup IL_0015: ldstr "Hello World!" IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001f: stloc.0 IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0025: dup IL_0026: ldc.i4 0x1267 IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0030: dup IL_0031: ldstr "ILSpy" IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003b: stloc.1 IL_003c: ldloc.0 IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0042: ldc.i4.s 100 IL_0044: ble.s IL_004f IL_0046: ldloc.1 IL_0047: ldloc.0 IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_004d: br.s IL_0056 IL_004f: ldloc.1 IL_0050: ldnull IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0056: ldstr "{0} {1}" IL_005b: ldloc.0 IL_005c: ldloc.1 IL_005d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0062: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0067: ret } // end of method Program::Test4 .method public hidebysig instance void Test5() cil managed { // Code size 125 (0x7d) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldc.i4.s 42 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: dup IL_0015: ldstr "Hello World!" IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001f: stloc.0 IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0025: dup IL_0026: ldc.i4 0x1267 IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0030: dup IL_0031: ldstr "ILSpy" IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003b: stloc.1 IL_003c: ldloc.0 IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0042: ldc.i4.s 100 IL_0044: ble.s IL_004f IL_0046: ldloc.1 IL_0047: ldloc.0 IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_004d: br.s IL_0056 IL_004f: ldloc.1 IL_0050: ldnull IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0056: ldstr "{0} {1}" IL_005b: ldloc.1 IL_005c: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0061: ldloc.1 IL_0062: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0067: call instance string [mscorlib]System.Int32::ToString() IL_006c: call string [mscorlib]System.String::Concat(string, string) IL_0071: ldloc.1 IL_0072: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0077: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_007c: ret } // end of method Program::Test5 .method public hidebysig instance void Issue1898(int32 i) cil managed { // Code size 135 (0x87) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, int32 V_2, int32 V_3) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldarg.1 IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0013: stloc.0 IL_0014: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0019: stloc.1 IL_001a: ldarg.0 IL_001b: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0020: stloc.2 IL_0021: ldloc.2 IL_0022: ldc.i4.1 IL_0023: sub IL_0024: switch ( IL_0037, IL_0045, IL_005b) IL_0035: br.s IL_0064 IL_0037: ldloc.1 IL_0038: ldarg.0 IL_0039: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_003e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0043: br.s IL_001a IL_0045: ldloc.1 IL_0046: ldarg.0 IL_0047: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_004c: stloc.3 IL_004d: ldloca.s V_3 IL_004f: call instance string [mscorlib]System.Int32::ToString() IL_0054: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0059: br.s IL_001a IL_005b: ldloc.1 IL_005c: ldloc.0 IL_005d: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0062: br.s IL_001a IL_0064: ldloc.1 IL_0065: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_006a: call void [mscorlib]System.Console::WriteLine(int32) IL_006f: ldloc.1 IL_0070: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0075: call void [mscorlib]System.Console::WriteLine(string) IL_007a: ldloc.1 IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0080: call void [mscorlib]System.Console::WriteLine(object) IL_0085: br.s IL_001a } // end of method Program::Issue1898 .method public hidebysig instance void Test6(int32 i) cil managed { // Code size 60 (0x3c) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldarg.1 IL_0019: ldc.i4.0 IL_001a: bge.s IL_0020 IL_001c: ldarg.1 IL_001d: neg IL_001e: starg.s i IL_0020: ldstr "{0} {1}" IL_0025: ldloc.0 IL_0026: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002b: box [mscorlib]System.Int32 IL_0030: ldloc.0 IL_0031: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0036: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003b: ret } // end of method Program::Test6 .method public hidebysig instance void Test6b(int32 i) cil managed { // Code size 61 (0x3d) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0007: dup IL_0008: ldloc.0 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.1 IL_001a: ldloc.0 IL_001b: ldc.i4.0 IL_001c: bge.s IL_0021 IL_001e: ldloc.0 IL_001f: neg IL_0020: stloc.0 IL_0021: ldstr "{0} {1}" IL_0026: ldloc.1 IL_0027: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002c: box [mscorlib]System.Int32 IL_0031: ldloc.1 IL_0032: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0037: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003c: ret } // end of method Program::Test6b .method public hidebysig instance void Test7(int32 i) cil managed { // Code size 69 (0x45) .maxstack 4 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, int32 V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldstr "{0} {1} {2}" IL_001d: ldloc.0 IL_001e: dup IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: ldc.i4.1 IL_0027: add IL_0028: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002d: ldloc.1 IL_002e: box [mscorlib]System.Int32 IL_0033: ldloc.0 IL_0034: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0039: ldarg.1 IL_003a: box [mscorlib]System.Int32 IL_003f: call void [mscorlib]System.Console::WriteLine(string, object, object, object) IL_0044: ret } // end of method Program::Test7 .method public hidebysig instance void Test8(int32 i) cil managed { // Code size 56 (0x38) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldc.i4.s 42 IL_001a: starg.s i IL_001c: ldstr "{0} {1}" IL_0021: ldloc.0 IL_0022: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0027: box [mscorlib]System.Int32 IL_002c: ldloc.0 IL_002d: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0032: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0037: ret } // end of method Program::Test8 .method public hidebysig instance void Test8b(int32 i) cil managed { // Code size 57 (0x39) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0007: dup IL_0008: ldloc.0 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.1 IL_001a: ldc.i4.s 42 IL_001c: stloc.0 IL_001d: ldstr "{0} {1}" IL_0022: ldloc.1 IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0028: box [mscorlib]System.Int32 IL_002d: ldloc.1 IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0033: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0038: ret } // end of method Program::Test8b .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Program::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly AggressiveScalarReplacementOfAggregates { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module AggressiveScalarReplacementOfAggregates.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method DisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3 .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NestedDisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program extends [mscorlib]System.Object { .method public hidebysig instance int32 Rand() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0005: throw } // end of method Program::Rand .method public hidebysig instance void Test1() cil managed { // Code size 53 (0x35) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_002f: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0034: ret } // end of method Program::Test1 .method public hidebysig instance void Test2() cil managed { // Code size 58 (0x3a) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: callvirt instance int32 [mscorlib]System.Object::GetHashCode() IL_002f: box [mscorlib]System.Int32 IL_0034: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0039: ret } // end of method Program::Test2 .method public hidebysig instance void Test3() cil managed { // Code size 48 (0x30) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldc.i4.s 42 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1}" IL_001e: ldloc.0 IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: box [mscorlib]System.Int32 IL_0029: ldloc.0 IL_002a: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002f: ret } // end of method Program::Test3 .method public hidebysig instance void Test4() cil managed { // Code size 104 (0x68) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldc.i4.s 42 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: dup IL_0015: ldstr "Hello World!" IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001f: stloc.0 IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0025: dup IL_0026: ldc.i4 0x1267 IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0030: dup IL_0031: ldstr "ILSpy" IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003b: stloc.1 IL_003c: ldloc.0 IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0042: ldc.i4.s 100 IL_0044: ble.s IL_004f IL_0046: ldloc.1 IL_0047: ldloc.0 IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_004d: br.s IL_0056 IL_004f: ldloc.1 IL_0050: ldnull IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0056: ldstr "{0} {1}" IL_005b: ldloc.0 IL_005c: ldloc.1 IL_005d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0062: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0067: ret } // end of method Program::Test4 .method public hidebysig instance void Test5() cil managed { // Code size 125 (0x7d) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldc.i4.s 42 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: dup IL_0015: ldstr "Hello World!" IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001f: stloc.0 IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0025: dup IL_0026: ldc.i4 0x1267 IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0030: dup IL_0031: ldstr "ILSpy" IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003b: stloc.1 IL_003c: ldloc.0 IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0042: ldc.i4.s 100 IL_0044: ble.s IL_004f IL_0046: ldloc.1 IL_0047: ldloc.0 IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_004d: br.s IL_0056 IL_004f: ldloc.1 IL_0050: ldnull IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0056: ldstr "{0} {1}" IL_005b: ldloc.1 IL_005c: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0061: ldloc.1 IL_0062: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0067: call instance string [mscorlib]System.Int32::ToString() IL_006c: call string [mscorlib]System.String::Concat(string, string) IL_0071: ldloc.1 IL_0072: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0077: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_007c: ret } // end of method Program::Test5 .method public hidebysig instance void Issue1898(int32 i) cil managed { // Code size 135 (0x87) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, int32 V_2, int32 V_3) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.0 IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000c: dup IL_000d: ldarg.1 IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0013: stloc.0 IL_0014: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0019: stloc.1 IL_001a: ldarg.0 IL_001b: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0020: stloc.2 IL_0021: ldloc.2 IL_0022: ldc.i4.1 IL_0023: sub IL_0024: switch ( IL_0037, IL_0045, IL_005b) IL_0035: br.s IL_0064 IL_0037: ldloc.1 IL_0038: ldarg.0 IL_0039: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_003e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0043: br.s IL_001a IL_0045: ldloc.1 IL_0046: ldarg.0 IL_0047: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_004c: stloc.3 IL_004d: ldloca.s V_3 IL_004f: call instance string [mscorlib]System.Int32::ToString() IL_0054: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0059: br.s IL_001a IL_005b: ldloc.1 IL_005c: ldloc.0 IL_005d: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0062: br.s IL_001a IL_0064: ldloc.1 IL_0065: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_006a: call void [mscorlib]System.Console::WriteLine(int32) IL_006f: ldloc.1 IL_0070: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0075: call void [mscorlib]System.Console::WriteLine(string) IL_007a: ldloc.1 IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0080: call void [mscorlib]System.Console::WriteLine(object) IL_0085: br.s IL_001a } // end of method Program::Issue1898 .method public hidebysig instance void Test6(int32 i) cil managed { // Code size 60 (0x3c) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldarg.1 IL_0019: ldc.i4.0 IL_001a: bge.s IL_0020 IL_001c: ldarg.1 IL_001d: neg IL_001e: starg.s i IL_0020: ldstr "{0} {1}" IL_0025: ldloc.0 IL_0026: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002b: box [mscorlib]System.Int32 IL_0030: ldloc.0 IL_0031: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0036: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003b: ret } // end of method Program::Test6 .method public hidebysig instance void Test6b(int32 i) cil managed { // Code size 61 (0x3d) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0007: dup IL_0008: ldloc.0 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.1 IL_001a: ldloc.0 IL_001b: ldc.i4.0 IL_001c: bge.s IL_0021 IL_001e: ldloc.0 IL_001f: neg IL_0020: stloc.0 IL_0021: ldstr "{0} {1}" IL_0026: ldloc.1 IL_0027: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002c: box [mscorlib]System.Int32 IL_0031: ldloc.1 IL_0032: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0037: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003c: ret } // end of method Program::Test6b .method public hidebysig instance void Test7(int32 i) cil managed { // Code size 69 (0x45) .maxstack 4 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, int32 V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldstr "{0} {1} {2}" IL_001d: ldloc.0 IL_001e: dup IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: ldc.i4.1 IL_0027: add IL_0028: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002d: ldloc.1 IL_002e: box [mscorlib]System.Int32 IL_0033: ldloc.0 IL_0034: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0039: ldarg.1 IL_003a: box [mscorlib]System.Int32 IL_003f: call void [mscorlib]System.Console::WriteLine(string, object, object, object) IL_0044: ret } // end of method Program::Test7 .method public hidebysig instance void Test8(int32 i) cil managed { // Code size 56 (0x38) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0005: dup IL_0006: ldarg.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000c: dup IL_000d: ldstr "Hello World!" IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0017: stloc.0 IL_0018: ldc.i4.s 42 IL_001a: starg.s i IL_001c: ldstr "{0} {1}" IL_0021: ldloc.0 IL_0022: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0027: box [mscorlib]System.Int32 IL_002c: ldloc.0 IL_002d: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0032: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0037: ret } // end of method Program::Test8 .method public hidebysig instance void Test8b(int32 i) cil managed { // Code size 57 (0x39) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0007: dup IL_0008: ldloc.0 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.1 IL_001a: ldc.i4.s 42 IL_001c: stloc.0 IL_001d: ldstr "{0} {1}" IL_0022: ldloc.1 IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0028: box [mscorlib]System.Int32 IL_002d: ldloc.1 IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0033: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0038: ret } // end of method Program::Test8b .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Program::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly AggressiveScalarReplacementOfAggregates { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module AggressiveScalarReplacementOfAggregates.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method DisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass extends [mscorlib]System.Object { .field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3 .field public int32 field1 .field public string field2 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NestedDisplayClass::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program extends [mscorlib]System.Object { .method public hidebysig instance int32 Rand() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method Program::Rand .method public hidebysig instance void Test1() cil managed { // Code size 55 (0x37) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0030: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0035: nop IL_0036: ret } // end of method Program::Test1 .method public hidebysig instance void Test2() cil managed { // Code size 60 (0x3c) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: callvirt instance int32 [mscorlib]System.Object::GetHashCode() IL_0030: box [mscorlib]System.Int32 IL_0035: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_003a: nop IL_003b: ret } // end of method Program::Test2 .method public hidebysig instance void Test3() cil managed { // Code size 50 (0x32) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldc.i4.s 42 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000e: dup IL_000f: ldstr "Hello World!" IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0019: stloc.0 IL_001a: ldstr "{0} {1}" IL_001f: ldloc.0 IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: box [mscorlib]System.Int32 IL_002a: ldloc.0 IL_002b: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0030: nop IL_0031: ret } // end of method Program::Test3 .method public hidebysig instance void Test4() cil managed { // Code size 114 (0x72) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, bool V_2) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldc.i4.s 42 IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0015: dup IL_0016: ldstr "Hello World!" IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0020: stloc.0 IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0026: dup IL_0027: ldc.i4 0x1267 IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0031: dup IL_0032: ldstr "ILSpy" IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003c: stloc.1 IL_003d: ldloc.0 IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0043: ldc.i4.s 100 IL_0045: cgt IL_0047: stloc.2 IL_0048: ldloc.2 IL_0049: brfalse.s IL_0056 IL_004b: nop IL_004c: ldloc.1 IL_004d: ldloc.0 IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0053: nop IL_0054: br.s IL_005f IL_0056: nop IL_0057: ldloc.1 IL_0058: ldnull IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_005e: nop IL_005f: ldstr "{0} {1}" IL_0064: ldloc.0 IL_0065: ldloc.1 IL_0066: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_006b: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0070: nop IL_0071: ret } // end of method Program::Test4 .method public hidebysig instance void Test5() cil managed { // Code size 135 (0x87) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, bool V_2) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldc.i4.s 42 IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0015: dup IL_0016: ldstr "Hello World!" IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0020: stloc.0 IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_0026: dup IL_0027: ldc.i4 0x1267 IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0031: dup IL_0032: ldstr "ILSpy" IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_003c: stloc.1 IL_003d: ldloc.0 IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0043: ldc.i4.s 100 IL_0045: cgt IL_0047: stloc.2 IL_0048: ldloc.2 IL_0049: brfalse.s IL_0056 IL_004b: nop IL_004c: ldloc.1 IL_004d: ldloc.0 IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0053: nop IL_0054: br.s IL_005f IL_0056: nop IL_0057: ldloc.1 IL_0058: ldnull IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_005e: nop IL_005f: ldstr "{0} {1}" IL_0064: ldloc.1 IL_0065: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_006a: ldloc.1 IL_006b: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0070: call instance string [mscorlib]System.Int32::ToString() IL_0075: call string [mscorlib]System.String::Concat(string, string) IL_007a: ldloc.1 IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0080: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0085: nop IL_0086: ret } // end of method Program::Test5 .method public hidebysig instance void Issue1898(int32 i) cil managed { // Code size 151 (0x97) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1, int32 V_2, int32 V_3, int32 V_4, bool V_5) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField IL_000d: dup IL_000e: ldarg.1 IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0014: stloc.0 IL_0015: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor() IL_001a: stloc.1 IL_001b: br.s IL_0092 IL_001d: nop IL_001e: ldarg.0 IL_001f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0024: stloc.3 IL_0025: ldloc.3 IL_0026: stloc.2 IL_0027: ldloc.2 IL_0028: ldc.i4.1 IL_0029: sub IL_002a: switch ( IL_003d, IL_004b, IL_0062) IL_003b: br.s IL_006b IL_003d: ldloc.1 IL_003e: ldarg.0 IL_003f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0044: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0049: br.s IL_0091 IL_004b: ldloc.1 IL_004c: ldarg.0 IL_004d: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand() IL_0052: stloc.s V_4 IL_0054: ldloca.s V_4 IL_0056: call instance string [mscorlib]System.Int32::ToString() IL_005b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_0060: br.s IL_0091 IL_0062: ldloc.1 IL_0063: ldloc.0 IL_0064: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0069: br.s IL_0091 IL_006b: ldloc.1 IL_006c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1 IL_0071: call void [mscorlib]System.Console::WriteLine(int32) IL_0076: nop IL_0077: ldloc.1 IL_0078: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2 IL_007d: call void [mscorlib]System.Console::WriteLine(string) IL_0082: nop IL_0083: ldloc.1 IL_0084: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3 IL_0089: call void [mscorlib]System.Console::WriteLine(object) IL_008e: nop IL_008f: br.s IL_0091 IL_0091: nop IL_0092: ldc.i4.1 IL_0093: stloc.s V_5 IL_0095: br.s IL_001d } // end of method Program::Issue1898 .method public hidebysig instance void Test6(int32 i) cil managed { // Code size 68 (0x44) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, bool V_1) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldarg.1 IL_001a: ldc.i4.0 IL_001b: clt IL_001d: stloc.1 IL_001e: ldloc.1 IL_001f: brfalse.s IL_0027 IL_0021: nop IL_0022: ldarg.1 IL_0023: neg IL_0024: starg.s i IL_0026: nop IL_0027: ldstr "{0} {1}" IL_002c: ldloc.0 IL_002d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0032: box [mscorlib]System.Int32 IL_0037: ldloc.0 IL_0038: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003d: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0042: nop IL_0043: ret } // end of method Program::Test6 .method public hidebysig instance void Test6b(int32 i) cil managed { // Code size 69 (0x45) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1, bool V_2) IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0008: dup IL_0009: ldloc.0 IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000f: dup IL_0010: ldstr "Hello World!" IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldc.i4.0 IL_001d: clt IL_001f: stloc.2 IL_0020: ldloc.2 IL_0021: brfalse.s IL_0028 IL_0023: nop IL_0024: ldloc.0 IL_0025: neg IL_0026: stloc.0 IL_0027: nop IL_0028: ldstr "{0} {1}" IL_002d: ldloc.1 IL_002e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0033: box [mscorlib]System.Int32 IL_0038: ldloc.1 IL_0039: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003e: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0043: nop IL_0044: ret } // end of method Program::Test6b .method public hidebysig instance void Test7(int32 i) cil managed { // Code size 71 (0x47) .maxstack 4 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0, int32 V_1) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldstr "{0} {1} {2}" IL_001e: ldloc.0 IL_001f: dup IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldc.i4.1 IL_0028: add IL_0029: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_002e: ldloc.1 IL_002f: box [mscorlib]System.Int32 IL_0034: ldloc.0 IL_0035: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_003a: ldarg.1 IL_003b: box [mscorlib]System.Int32 IL_0040: call void [mscorlib]System.Console::WriteLine(string, object, object, object) IL_0045: nop IL_0046: ret } // end of method Program::Test7 .method public hidebysig instance void Test8(int32 i) cil managed { // Code size 58 (0x3a) .maxstack 3 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0) IL_0000: nop IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0006: dup IL_0007: ldarg.1 IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000d: dup IL_000e: ldstr "Hello World!" IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0018: stloc.0 IL_0019: ldc.i4.s 42 IL_001b: starg.s i IL_001d: ldstr "{0} {1}" IL_0022: ldloc.0 IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0028: box [mscorlib]System.Int32 IL_002d: ldloc.0 IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0033: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0038: nop IL_0039: ret } // end of method Program::Test8 .method public hidebysig instance void Test8b(int32 i) cil managed { // Code size 59 (0x3b) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1) IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor() IL_0008: dup IL_0009: ldloc.0 IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_000f: dup IL_0010: ldstr "Hello World!" IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_001a: stloc.1 IL_001b: ldc.i4.s 42 IL_001d: stloc.0 IL_001e: ldstr "{0} {1}" IL_0023: ldloc.1 IL_0024: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1 IL_0029: box [mscorlib]System.Int32 IL_002e: ldloc.1 IL_002f: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2 IL_0034: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0039: nop IL_003a: ret } // end of method Program::Test8b .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Program::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.Expected.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [CompilerGenerated] internal sealed class _003CPrivateImplementationDetails_003E { [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 12)] private struct __StaticArrayInitTypeSize_003D12 { } internal static readonly __StaticArrayInitTypeSize_003D12 _4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D/* Not supported: data(01 00 00 00 02 00 00 00 03 00 00 00) */; } namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class NoArrayInitializers { public int[] LiteralArray() { int[] array = new int[3]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); return array; } public int[] VariableArray(int a, int b) { int[] array = new int[2]; array[0] = a; array[1] = b; return array; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.cs ================================================ // Copyright (c) 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 // 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; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class NoArrayInitializers { public int[] LiteralArray() { return new[] { 1, 2, 3 }; } public int[] VariableArray(int a, int b) { return new[] { a, b }; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp9704 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp9704.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers extends [mscorlib]System.Object { .method public hidebysig instance int32[] LiteralArray() cil managed { // Code size 23 (0x17) .maxstack 3 .locals init (int32[] V_0) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=12' ''::'4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method NoArrayInitializers::LiteralArray .method public hidebysig instance int32[] VariableArray(int32 a, int32 b) cil managed { // Code size 20 (0x14) .maxstack 4 .locals init (int32[] V_0) IL_0000: nop IL_0001: ldc.i4.2 IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldc.i4.0 IL_0009: ldarg.1 IL_000a: stelem.i4 IL_000b: dup IL_000c: ldc.i4.1 IL_000d: ldarg.2 IL_000e: stelem.i4 IL_000f: stloc.0 IL_0010: br.s IL_0012 IL_0012: ldloc.0 IL_0013: ret } // end of method NoArrayInitializers::VariableArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoArrayInitializers::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers .class private auto ansi sealed '' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=12' extends [mscorlib]System.ValueType { .pack 1 .size 12 } // end of class '__StaticArrayInitTypeSize=12' .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=12' '4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' at I_000026C0 } // end of class '' // ============================================================= .data cil I_000026C0 = bytearray ( 01 00 00 00 02 00 00 00 03 00 00 00) // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp9705 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp9705.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers extends [mscorlib]System.Object { .method public hidebysig instance int32[] LiteralArray() cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: ldc.i4.3 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=12' ''::'4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' IL_000c: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0011: ret } // end of method NoArrayInitializers::LiteralArray .method public hidebysig instance int32[] VariableArray(int32 a, int32 b) cil managed { // Code size 15 (0xf) .maxstack 8 IL_0000: ldc.i4.2 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.1 IL_0009: stelem.i4 IL_000a: dup IL_000b: ldc.i4.1 IL_000c: ldarg.2 IL_000d: stelem.i4 IL_000e: ret } // end of method NoArrayInitializers::VariableArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoArrayInitializers::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers .class private auto ansi sealed '' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=12' extends [mscorlib]System.ValueType { .pack 1 .size 12 } // end of class '__StaticArrayInitTypeSize=12' .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=12' '4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' at I_00002690 } // end of class '' // ============================================================= .data cil I_00002690 = bytearray ( 01 00 00 00 02 00 00 00 03 00 00 00) // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.opt.roslyn.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoArrayInitializers { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoArrayInitializers.dll // MVID: {C6AD59A2-7245-425B-A6BB-29F93FAB1116} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x00FD0000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers extends [mscorlib]System.Object { .method public hidebysig instance int32[] LiteralArray() cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: ldc.i4.3 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=12' ''::'4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' IL_000c: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0011: ret } // end of method NoArrayInitializers::LiteralArray .method public hidebysig instance int32[] VariableArray(int32 a, int32 b) cil managed { // Code size 15 (0xf) .maxstack 8 IL_0000: ldc.i4.2 IL_0001: newarr [mscorlib]System.Int32 IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.1 IL_0009: stelem.i4 IL_000a: dup IL_000b: ldc.i4.1 IL_000c: ldarg.2 IL_000d: stelem.i4 IL_000e: ret } // end of method NoArrayInitializers::VariableArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoArrayInitializers::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers .class private auto ansi sealed '' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=12' extends [mscorlib]System.ValueType { .pack 1 .size 12 } // end of class '__StaticArrayInitTypeSize=12' .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=12' '4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' at I_00002690 } // end of class '' // ============================================================= .data cil I_00002690 = bytearray ( 01 00 00 00 02 00 00 00 03 00 00 00) // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoArrayInitializers.roslyn.il ================================================ // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoArrayInitializers { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoArrayInitializers.dll // MVID: {C1B38171-55C8-4DDA-ACC8-78EFA1168D40} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x03D10000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers extends [mscorlib]System.Object { .method public hidebysig instance int32[] LiteralArray() cil managed { // Code size 23 (0x17) .maxstack 3 .locals init (int32[] V_0) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=12' ''::'4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method NoArrayInitializers::LiteralArray .method public hidebysig instance int32[] VariableArray(int32 a, int32 b) cil managed { // Code size 20 (0x14) .maxstack 4 .locals init (int32[] V_0) IL_0000: nop IL_0001: ldc.i4.2 IL_0002: newarr [mscorlib]System.Int32 IL_0007: dup IL_0008: ldc.i4.0 IL_0009: ldarg.1 IL_000a: stelem.i4 IL_000b: dup IL_000c: ldc.i4.1 IL_000d: ldarg.2 IL_000e: stelem.i4 IL_000f: stloc.0 IL_0010: br.s IL_0012 IL_0012: ldloc.0 IL_0013: ret } // end of method NoArrayInitializers::VariableArray .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoArrayInitializers::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoArrayInitializers .class private auto ansi sealed '' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=12' extends [mscorlib]System.ValueType { .pack 1 .size 12 } // end of class '__StaticArrayInitTypeSize=12' .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=12' '4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D' at I_000026C0 } // end of class '' // ============================================================= .data cil I_000026C0 = bytearray ( 01 00 00 00 02 00 00 00 03 00 00 00) // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.Expected.cs ================================================ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class NoDecimalConstants { [DecimalConstant(1, 0, 0u, 0u, 10u)] private static readonly decimal constant = 1.0m; private void MethodWithOptionalParameter([Optional][DecimalConstant(1, 0, 0u, 0u, 10u)] decimal parameter) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public class NoDecimalConstants { private const decimal constant = 1.0m; private void MethodWithOptionalParameter(decimal parameter = 1.0m) { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpA278 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpA278.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants extends [mscorlib]System.Object { .field private static initonly valuetype [mscorlib]System.Decimal constant .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) .method private hidebysig instance void MethodWithOptionalParameter([opt] valuetype [mscorlib]System.Decimal parameter) cil managed { .param [1] .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method NoDecimalConstants::MethodWithOptionalParameter .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoDecimalConstants::.ctor .method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 17 (0x11) .maxstack 8 IL_0000: ldc.i4.s 10 IL_0002: ldc.i4.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_000b: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants::constant IL_0010: ret } // end of method NoDecimalConstants::.cctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpA279 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpA279.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants extends [mscorlib]System.Object { .field private static initonly valuetype [mscorlib]System.Decimal constant .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) .method private hidebysig instance void MethodWithOptionalParameter([opt] valuetype [mscorlib]System.Decimal parameter) cil managed { .param [1] .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method NoDecimalConstants::MethodWithOptionalParameter .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoDecimalConstants::.ctor .method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 17 (0x11) .maxstack 8 IL_0000: ldc.i4.s 10 IL_0002: ldc.i4.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_000b: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants::constant IL_0010: ret } // end of method NoDecimalConstants::.cctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoDecimalConstants { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoDecimalConstants.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants extends [mscorlib]System.Object { .field private static initonly valuetype [mscorlib]System.Decimal constant .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) .method private hidebysig instance void MethodWithOptionalParameter([opt] valuetype [mscorlib]System.Decimal parameter) cil managed { .param [1] .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method NoDecimalConstants::MethodWithOptionalParameter .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoDecimalConstants::.ctor .method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 17 (0x11) .maxstack 8 IL_0000: ldc.i4.s 10 IL_0002: ldc.i4.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_000b: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants::constant IL_0010: ret } // end of method NoDecimalConstants::.cctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoDecimalConstants.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoDecimalConstants { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoDecimalConstants.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants extends [mscorlib]System.Object { .field private static initonly valuetype [mscorlib]System.Decimal constant .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) .method private hidebysig instance void MethodWithOptionalParameter([opt] valuetype [mscorlib]System.Decimal parameter) cil managed { .param [1] .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 ) // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method NoDecimalConstants::MethodWithOptionalParameter .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoDecimalConstants::.ctor .method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 17 (0x11) .maxstack 8 IL_0000: ldc.i4.s 10 IL_0002: ldc.i4.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.1 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_000b: stsfld valuetype [mscorlib]System.Decimal ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants::constant IL_0010: ret } // end of method NoDecimalConstants::.cctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoDecimalConstants // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs ================================================ using System; using System.Runtime.CompilerServices; [assembly: Extension] namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { [Extension] internal static class NoExtensionMethods { [Extension] internal unsafe static Func AsFunc(T value) where T : class { return new Func(value, (nint)(delegate*)(&Return)); } [Extension] private static T Return(T value) { return value; } internal static Func ExtensionMethodAsStaticFunc() { return Return; } internal unsafe static Func ExtensionMethodBoundToNull() { return new Func(null, (nint)(delegate*)(&Return)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs ================================================ using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal static class NoExtensionMethods { internal static Func AsFunc(this T value) where T : class { return new Func(value.Return); } private static T Return(this T value) { return value; } internal static Func ExtensionMethodAsStaticFunc() { return Return; } internal static Func ExtensionMethodBoundToNull() { return ((object)null).Return; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpA29B { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpA29B.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method assembly hidebysig static class [mscorlib]System.Func`1 AsFunc(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 23 (0x17) .maxstack 2 .locals init (class [mscorlib]System.Func`1 V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: box !!T IL_0007: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_000d: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method NoExtensionMethods::AsFunc .method private hidebysig static !!T Return(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 7 (0x7) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: br.s IL_0005 IL_0005: ldloc.0 IL_0006: ret } // end of method NoExtensionMethods::Return .method assembly hidebysig static class [mscorlib]System.Func`2 ExtensionMethodAsStaticFunc() cil managed { // Code size 18 (0x12) .maxstack 2 .locals init (class [mscorlib]System.Func`2 V_0) IL_0000: nop IL_0001: ldnull IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0008: newobj instance void class [mscorlib]System.Func`2::.ctor(object, native int) IL_000d: stloc.0 IL_000e: br.s IL_0010 IL_0010: ldloc.0 IL_0011: ret } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc .method assembly hidebysig static class [mscorlib]System.Func`1 ExtensionMethodBoundToNull() cil managed { // Code size 18 (0x12) .maxstack 2 .locals init (class [mscorlib]System.Func`1 V_0) IL_0000: nop IL_0001: ldnull IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0008: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000d: stloc.0 IL_000e: br.s IL_0010 IL_0010: ldloc.0 IL_0011: ret } // end of method NoExtensionMethods::ExtensionMethodBoundToNull } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpA29A { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpA29A.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method assembly hidebysig static class [mscorlib]System.Func`1 AsFunc(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 18 (0x12) .maxstack 8 IL_0000: ldarg.0 IL_0001: box !!T IL_0006: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_000c: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0011: ret } // end of method NoExtensionMethods::AsFunc .method private hidebysig static !!T Return(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 2 (0x2) .maxstack 8 IL_0000: ldarg.0 IL_0001: ret } // end of method NoExtensionMethods::Return .method assembly hidebysig static class [mscorlib]System.Func`2 ExtensionMethodAsStaticFunc() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: ldnull IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0007: newobj instance void class [mscorlib]System.Func`2::.ctor(object, native int) IL_000c: ret } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc .method assembly hidebysig static class [mscorlib]System.Func`1 ExtensionMethodBoundToNull() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: ldnull IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000c: ret } // end of method NoExtensionMethods::ExtensionMethodBoundToNull } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoExtensionMethods { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoExtensionMethods.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method assembly hidebysig static class [mscorlib]System.Func`1 AsFunc(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 18 (0x12) .maxstack 8 IL_0000: ldarg.0 IL_0001: box !!T IL_0006: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_000c: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0011: ret } // end of method NoExtensionMethods::AsFunc .method private hidebysig static !!T Return(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 2 (0x2) .maxstack 8 IL_0000: ldarg.0 IL_0001: ret } // end of method NoExtensionMethods::Return .method assembly hidebysig static class [mscorlib]System.Func`2 ExtensionMethodAsStaticFunc() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: ldnull IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0007: newobj instance void class [mscorlib]System.Func`2::.ctor(object, native int) IL_000c: ret } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc .method assembly hidebysig static class [mscorlib]System.Func`1 ExtensionMethodBoundToNull() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: ldnull IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000c: ret } // end of method NoExtensionMethods::ExtensionMethodBoundToNull } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoExtensionMethods { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoExtensionMethods.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method assembly hidebysig static class [mscorlib]System.Func`1 AsFunc(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 23 (0x17) .maxstack 2 .locals init (class [mscorlib]System.Func`1 V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: box !!T IL_0007: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_000d: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0012: stloc.0 IL_0013: br.s IL_0015 IL_0015: ldloc.0 IL_0016: ret } // end of method NoExtensionMethods::AsFunc .method private hidebysig static !!T Return(!!T 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Code size 7 (0x7) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: br.s IL_0005 IL_0005: ldloc.0 IL_0006: ret } // end of method NoExtensionMethods::Return .method assembly hidebysig static class [mscorlib]System.Func`2 ExtensionMethodAsStaticFunc() cil managed { // Code size 18 (0x12) .maxstack 2 .locals init (class [mscorlib]System.Func`2 V_0) IL_0000: nop IL_0001: ldnull IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0008: newobj instance void class [mscorlib]System.Func`2::.ctor(object, native int) IL_000d: stloc.0 IL_000e: br.s IL_0010 IL_0010: ldloc.0 IL_0011: ret } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc .method assembly hidebysig static class [mscorlib]System.Func`1 ExtensionMethodBoundToNull() cil managed { // Code size 18 (0x12) .maxstack 2 .locals init (class [mscorlib]System.Func`1 V_0) IL_0000: nop IL_0001: ldnull IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) IL_0008: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000d: stloc.0 IL_000e: br.s IL_0010 IL_0010: ldloc.0 IL_0011: ret } // end of method NoExtensionMethods::ExtensionMethodBoundToNull } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.Expected.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoForEachStatement { public static void SimpleNonGenericForeach(IEnumerable enumerable) { IEnumerator enumerator = enumerable.GetEnumerator(); try { while (enumerator.MoveNext()) { #if ROSLYN && OPT Console.WriteLine(enumerator.Current); #else object current = enumerator.Current; Console.WriteLine(current); #endif } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } public static void SimpleForeachOverInts(IEnumerable enumerable) { using (IEnumerator enumerator = enumerable.GetEnumerator()) { while (enumerator.MoveNext()) { #if ROSLYN && OPT Console.WriteLine(enumerator.Current); #else int current = enumerator.Current; Console.WriteLine(current); #endif } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoForEachStatement { public static void SimpleNonGenericForeach(IEnumerable enumerable) { foreach (object item in enumerable) { Console.WriteLine(item); } } public static void SimpleForeachOverInts(IEnumerable enumerable) { foreach (int item in enumerable) { Console.WriteLine(item); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly rerff2f0 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module rerff2f0.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 64 (0x40) .maxstack 2 .locals init (object V_0, class [mscorlib]System.Collections.IEnumerator V_1, bool V_2, class [mscorlib]System.IDisposable V_3) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0008: stloc.1 .try { IL_0009: br.s IL_001b IL_000b: ldloc.1 IL_000c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_0011: stloc.0 IL_0012: nop IL_0013: ldloc.0 IL_0014: call void [mscorlib]System.Console::WriteLine(object) IL_0019: nop IL_001a: nop IL_001b: ldloc.1 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: stloc.2 IL_0022: ldloc.2 IL_0023: brtrue.s IL_000b IL_0025: leave.s IL_003e } // end .try finally { IL_0027: ldloc.1 IL_0028: isinst [mscorlib]System.IDisposable IL_002d: stloc.3 IL_002e: ldloc.3 IL_002f: ldnull IL_0030: ceq IL_0032: stloc.2 IL_0033: ldloc.2 IL_0034: brtrue.s IL_003d IL_0036: ldloc.3 IL_0037: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_003c: nop IL_003d: endfinally } // end handler IL_003e: nop IL_003f: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 57 (0x39) .maxstack 2 .locals init (int32 V_0, class [mscorlib]System.Collections.Generic.IEnumerator`1 V_1, bool V_2) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0008: stloc.1 .try { IL_0009: br.s IL_001b IL_000b: ldloc.1 IL_000c: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_0011: stloc.0 IL_0012: nop IL_0013: ldloc.0 IL_0014: call void [mscorlib]System.Console::WriteLine(int32) IL_0019: nop IL_001a: nop IL_001b: ldloc.1 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: stloc.2 IL_0022: ldloc.2 IL_0023: brtrue.s IL_000b IL_0025: leave.s IL_0037 } // end .try finally { IL_0027: ldloc.1 IL_0028: ldnull IL_0029: ceq IL_002b: stloc.2 IL_002c: ldloc.2 IL_002d: brtrue.s IL_0036 IL_002f: ldloc.1 IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0035: nop IL_0036: endfinally } // end handler IL_0037: nop IL_0038: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpA7E1 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpA7E1.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 56 (0x38) .maxstack 1 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, object V_1, class [mscorlib]System.IDisposable V_2) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0008: stloc.0 .try { IL_0009: br.s IL_001b IL_000b: ldloc.0 IL_000c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_0011: stloc.1 IL_0012: nop IL_0013: ldloc.1 IL_0014: call void [mscorlib]System.Console::WriteLine(object) IL_0019: nop IL_001a: nop IL_001b: ldloc.0 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: brtrue.s IL_000b IL_0023: leave.s IL_0037 } // end .try finally { IL_0025: ldloc.0 IL_0026: isinst [mscorlib]System.IDisposable IL_002b: stloc.2 IL_002c: ldloc.2 IL_002d: brfalse.s IL_0036 IL_002f: ldloc.2 IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0035: nop IL_0036: endfinally } // end handler IL_0037: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 49 (0x31) .maxstack 1 .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0, int32 V_1) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0008: stloc.0 .try { IL_0009: br.s IL_001b IL_000b: ldloc.0 IL_000c: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_0011: stloc.1 IL_0012: nop IL_0013: ldloc.1 IL_0014: call void [mscorlib]System.Console::WriteLine(int32) IL_0019: nop IL_001a: nop IL_001b: ldloc.0 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: brtrue.s IL_000b IL_0023: leave.s IL_0030 } // end .try finally { IL_0025: ldloc.0 IL_0026: brfalse.s IL_002f IL_0028: ldloc.0 IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002e: nop IL_002f: endfinally } // end handler IL_0030: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly prqfqkbt { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module prqfqkbt.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 50 (0x32) .maxstack 1 .locals init (object V_0, class [mscorlib]System.Collections.IEnumerator V_1, class [mscorlib]System.IDisposable V_2) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0006: stloc.1 .try { IL_0007: br.s IL_0016 IL_0009: ldloc.1 IL_000a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_000f: stloc.0 IL_0010: ldloc.0 IL_0011: call void [mscorlib]System.Console::WriteLine(object) IL_0016: ldloc.1 IL_0017: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001c: brtrue.s IL_0009 IL_001e: leave.s IL_0031 } // end .try finally { IL_0020: ldloc.1 IL_0021: isinst [mscorlib]System.IDisposable IL_0026: stloc.2 IL_0027: ldloc.2 IL_0028: brfalse.s IL_0030 IL_002a: ldloc.2 IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0030: endfinally } // end handler IL_0031: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 43 (0x2b) .maxstack 1 .locals init (int32 V_0, class [mscorlib]System.Collections.Generic.IEnumerator`1 V_1) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0006: stloc.1 .try { IL_0007: br.s IL_0016 IL_0009: ldloc.1 IL_000a: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_000f: stloc.0 IL_0010: ldloc.0 IL_0011: call void [mscorlib]System.Console::WriteLine(int32) IL_0016: ldloc.1 IL_0017: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001c: brtrue.s IL_0009 IL_001e: leave.s IL_002a } // end .try finally { IL_0020: ldloc.1 IL_0021: brfalse.s IL_0029 IL_0023: ldloc.1 IL_0024: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0029: endfinally } // end handler IL_002a: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpAA15 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpAA15.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 48 (0x30) .maxstack 1 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, class [mscorlib]System.IDisposable V_1) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0006: stloc.0 .try { IL_0007: br.s IL_0014 IL_0009: ldloc.0 IL_000a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_000f: call void [mscorlib]System.Console::WriteLine(object) IL_0014: ldloc.0 IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001a: brtrue.s IL_0009 IL_001c: leave.s IL_002f } // end .try finally { IL_001e: ldloc.0 IL_001f: isinst [mscorlib]System.IDisposable IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: brfalse.s IL_002e IL_0028: ldloc.1 IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002e: endfinally } // end handler IL_002f: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 41 (0x29) .maxstack 1 .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0006: stloc.0 .try { IL_0007: br.s IL_0014 IL_0009: ldloc.0 IL_000a: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_000f: call void [mscorlib]System.Console::WriteLine(int32) IL_0014: ldloc.0 IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001a: brtrue.s IL_0009 IL_001c: leave.s IL_0028 } // end .try finally { IL_001e: ldloc.0 IL_001f: brfalse.s IL_0027 IL_0021: ldloc.0 IL_0022: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0027: endfinally } // end handler IL_0028: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoForEachStatement { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoForEachStatement.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 48 (0x30) .maxstack 1 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, class [mscorlib]System.IDisposable V_1) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0006: stloc.0 .try { IL_0007: br.s IL_0014 IL_0009: ldloc.0 IL_000a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_000f: call void [mscorlib]System.Console::WriteLine(object) IL_0014: ldloc.0 IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001a: brtrue.s IL_0009 IL_001c: leave.s IL_002f } // end .try finally { IL_001e: ldloc.0 IL_001f: isinst [mscorlib]System.IDisposable IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: brfalse.s IL_002e IL_0028: ldloc.1 IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002e: endfinally } // end handler IL_002f: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 41 (0x29) .maxstack 1 .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0) IL_0000: ldarg.0 IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0006: stloc.0 .try { IL_0007: br.s IL_0014 IL_0009: ldloc.0 IL_000a: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_000f: call void [mscorlib]System.Console::WriteLine(int32) IL_0014: ldloc.0 IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_001a: brtrue.s IL_0009 IL_001c: leave.s IL_0028 } // end .try finally { IL_001e: ldloc.0 IL_001f: brfalse.s IL_0027 IL_0021: ldloc.0 IL_0022: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0027: endfinally } // end handler IL_0028: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoForEachStatement { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoForEachStatement.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement extends [mscorlib]System.Object { .method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed { // Code size 56 (0x38) .maxstack 1 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, object V_1, class [mscorlib]System.IDisposable V_2) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() IL_0008: stloc.0 .try { IL_0009: br.s IL_001b IL_000b: ldloc.0 IL_000c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_0011: stloc.1 IL_0012: nop IL_0013: ldloc.1 IL_0014: call void [mscorlib]System.Console::WriteLine(object) IL_0019: nop IL_001a: nop IL_001b: ldloc.0 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: brtrue.s IL_000b IL_0023: leave.s IL_0037 } // end .try finally { IL_0025: ldloc.0 IL_0026: isinst [mscorlib]System.IDisposable IL_002b: stloc.2 IL_002c: ldloc.2 IL_002d: brfalse.s IL_0036 IL_002f: ldloc.2 IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0035: nop IL_0036: endfinally } // end handler IL_0037: ret } // end of method NoForEachStatement::SimpleNonGenericForeach .method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1 enumerable) cil managed { // Code size 49 (0x31) .maxstack 1 .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0, int32 V_1) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 IL_0003: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() IL_0008: stloc.0 .try { IL_0009: br.s IL_001b IL_000b: ldloc.0 IL_000c: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() IL_0011: stloc.1 IL_0012: nop IL_0013: ldloc.1 IL_0014: call void [mscorlib]System.Console::WriteLine(int32) IL_0019: nop IL_001a: nop IL_001b: ldloc.0 IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0021: brtrue.s IL_000b IL_0023: leave.s IL_0030 } // end .try finally { IL_0025: ldloc.0 IL_0026: brfalse.s IL_002f IL_0028: ldloc.0 IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_002e: nop IL_002f: endfinally } // end handler IL_0030: ret } // end of method NoForEachStatement::SimpleForeachOverInts .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoForEachStatement::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.Expected.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public sealed class Handle { private readonly Func _func; public Handle(Func func) { _func = func; } } public static class NoLocalFunctions { [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct _003C_003Ec__DisplayClass1_0 { public int x; } [CompilerGenerated] private sealed class _003C_003Ec__DisplayClass2_0 { public int x; internal int _003CSimpleCaptureWithRef_003Eg__F_007C0() { return 42 + x; } } private static void UseLocalFunctionReference() { #if OPT new Handle(new Func(_003CUseLocalFunctionReference_003Eg__F_007C0_0)); #else Handle handle = new Handle(new Func(_003CUseLocalFunctionReference_003Eg__F_007C0_0)); #endif } private static void SimpleCapture() { _003C_003Ec__DisplayClass1_0 A_ = default(_003C_003Ec__DisplayClass1_0); A_.x = 1; _003CSimpleCapture_003Eg__F_007C1_0(ref A_); } private static void SimpleCaptureWithRef() { #if OPT _003C_003Ec__DisplayClass2_0 obj = new _003C_003Ec__DisplayClass2_0(); obj.x = 1; new Handle(new Func(obj._003CSimpleCaptureWithRef_003Eg__F_007C0)); #else _003C_003Ec__DisplayClass2_0 _003C_003Ec__DisplayClass2_1 = new _003C_003Ec__DisplayClass2_0(); _003C_003Ec__DisplayClass2_1.x = 1; Handle handle = new Handle(new Func(_003C_003Ec__DisplayClass2_1._003CSimpleCaptureWithRef_003Eg__F_007C0)); #endif } [CompilerGenerated] internal static int _003CUseLocalFunctionReference_003Eg__F_007C0_0() { return 42; } [CompilerGenerated] private static int _003CSimpleCapture_003Eg__F_007C1_0(ref _003C_003Ec__DisplayClass1_0 A_0) { return 42 + A_0.x; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { public sealed class Handle { private readonly Func _func; public Handle(Func func) { _func = func; } } public static class NoLocalFunctions { private static void UseLocalFunctionReference() { int F() => 42; var handle = new Handle(F); } private static void SimpleCapture() { int x = 1; int F() => 42 + x; F(); } private static void SimpleCaptureWithRef() { int x = 1; int F() => 42 + x; var handle = new Handle(F); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpADF1 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpADF1.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle extends [mscorlib]System.Object { .field private initonly class [mscorlib]System.Func`1 _func .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Func`1 func) cil managed { // Code size 16 (0x10) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld class [mscorlib]System.Func`1 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::_func IL_000f: ret } // end of method Handle::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle .class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions extends [mscorlib]System.Object { .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1_0' extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x } // end of class '<>c__DisplayClass1_0' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass2_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method '<>c__DisplayClass2_0'::.ctor .method assembly hidebysig instance int32 'g__F|0'() cil managed { // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_0008: add IL_0009: ret } // end of method '<>c__DisplayClass2_0'::'g__F|0' } // end of class '<>c__DisplayClass2_0' .method private hidebysig static void UseLocalFunctionReference() cil managed { // Code size 21 (0x15) .maxstack 2 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle V_0) IL_0000: nop IL_0001: nop IL_0002: ldnull IL_0003: ldftn int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|0_0'() IL_0009: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000e: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0013: stloc.0 IL_0014: ret } // end of method NoLocalFunctions::UseLocalFunctionReference .method private hidebysig static void SimpleCapture() cil managed { // Code size 19 (0x13) .maxstack 2 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0' V_0) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.1 IL_0004: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0009: nop IL_000a: ldloca.s V_0 IL_000c: call int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'&) IL_0011: pop IL_0012: ret } // end of method NoLocalFunctions::SimpleCapture .method private hidebysig static void SimpleCaptureWithRef() cil managed { // Code size 34 (0x22) .maxstack 2 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0' V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::.ctor() IL_0005: stloc.0 IL_0006: nop IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_000e: nop IL_000f: ldloc.0 IL_0010: ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::'g__F|0'() IL_0016: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_001b: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0020: stloc.1 IL_0021: ret } // end of method NoLocalFunctions::SimpleCaptureWithRef .method assembly hidebysig static int32 'g__F|0_0'() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 3 (0x3) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ret } // end of method NoLocalFunctions::'g__F|0_0' .method assembly hidebysig static int32 'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'& A_0) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: add IL_0009: ret } // end of method NoLocalFunctions::'g__F|1_0' } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpAE03 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpAE03.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle extends [mscorlib]System.Object { .field private initonly class [mscorlib]System.Func`1 _func .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Func`1 func) cil managed { // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld class [mscorlib]System.Func`1 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::_func IL_000d: ret } // end of method Handle::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle .class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions extends [mscorlib]System.Object { .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1_0' extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x } // end of class '<>c__DisplayClass1_0' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass2_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method '<>c__DisplayClass2_0'::.ctor .method assembly hidebysig instance int32 'g__F|0'() cil managed { // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_0008: add IL_0009: ret } // end of method '<>c__DisplayClass2_0'::'g__F|0' } // end of class '<>c__DisplayClass2_0' .method private hidebysig static void UseLocalFunctionReference() cil managed { // Code size 19 (0x13) .maxstack 8 IL_0000: ldnull IL_0001: ldftn int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|0_0'() IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000c: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0011: pop IL_0012: ret } // end of method NoLocalFunctions::UseLocalFunctionReference .method private hidebysig static void SimpleCapture() cil managed { // Code size 17 (0x11) .maxstack 2 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0' V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: ldloca.s V_0 IL_000a: call int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'&) IL_000f: pop IL_0010: ret } // end of method NoLocalFunctions::SimpleCapture .method private hidebysig static void SimpleCaptureWithRef() cil managed { // Code size 30 (0x1e) .maxstack 8 IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::.ctor() IL_0005: dup IL_0006: ldc.i4.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_000c: ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::'g__F|0'() IL_0012: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0017: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_001c: pop IL_001d: ret } // end of method NoLocalFunctions::SimpleCaptureWithRef .method assembly hidebysig static int32 'g__F|0_0'() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 3 (0x3) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ret } // end of method NoLocalFunctions::'g__F|0_0' .method assembly hidebysig static int32 'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'& A_0) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: add IL_0009: ret } // end of method NoLocalFunctions::'g__F|1_0' } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoLocalFunctions { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoLocalFunctions.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle extends [mscorlib]System.Object { .field private initonly class [mscorlib]System.Func`1 _func .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Func`1 func) cil managed { // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld class [mscorlib]System.Func`1 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::_func IL_000d: ret } // end of method Handle::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle .class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions extends [mscorlib]System.Object { .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1_0' extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x } // end of class '<>c__DisplayClass1_0' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass2_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method '<>c__DisplayClass2_0'::.ctor .method assembly hidebysig instance int32 'g__F|0'() cil managed { // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_0008: add IL_0009: ret } // end of method '<>c__DisplayClass2_0'::'g__F|0' } // end of class '<>c__DisplayClass2_0' .method private hidebysig static void UseLocalFunctionReference() cil managed { // Code size 19 (0x13) .maxstack 8 IL_0000: ldnull IL_0001: ldftn int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|0_0'() IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000c: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0011: pop IL_0012: ret } // end of method NoLocalFunctions::UseLocalFunctionReference .method private hidebysig static void SimpleCapture() cil managed { // Code size 17 (0x11) .maxstack 2 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0' V_0) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: ldloca.s V_0 IL_000a: call int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'&) IL_000f: pop IL_0010: ret } // end of method NoLocalFunctions::SimpleCapture .method private hidebysig static void SimpleCaptureWithRef() cil managed { // Code size 30 (0x1e) .maxstack 8 IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::.ctor() IL_0005: dup IL_0006: ldc.i4.1 IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_000c: ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::'g__F|0'() IL_0012: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0017: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_001c: pop IL_001d: ret } // end of method NoLocalFunctions::SimpleCaptureWithRef .method assembly hidebysig static int32 'g__F|0_0'() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 3 (0x3) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ret } // end of method NoLocalFunctions::'g__F|0_0' .method assembly hidebysig static int32 'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'& A_0) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: add IL_0009: ret } // end of method NoLocalFunctions::'g__F|1_0' } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoLocalFunctions.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly NoLocalFunctions { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module NoLocalFunctions.dll .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle extends [mscorlib]System.Object { .field private initonly class [mscorlib]System.Func`1 _func .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Func`1 func) cil managed { // Code size 16 (0x10) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld class [mscorlib]System.Func`1 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::_func IL_000f: ret } // end of method Handle::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle .class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions extends [mscorlib]System.Object { .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1_0' extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x } // end of class '<>c__DisplayClass1_0' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass2_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field public int32 x .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method '<>c__DisplayClass2_0'::.ctor .method assembly hidebysig instance int32 'g__F|0'() cil managed { // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_0008: add IL_0009: ret } // end of method '<>c__DisplayClass2_0'::'g__F|0' } // end of class '<>c__DisplayClass2_0' .method private hidebysig static void UseLocalFunctionReference() cil managed { // Code size 21 (0x15) .maxstack 2 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle V_0) IL_0000: nop IL_0001: nop IL_0002: ldnull IL_0003: ldftn int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|0_0'() IL_0009: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_000e: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0013: stloc.0 IL_0014: ret } // end of method NoLocalFunctions::UseLocalFunctionReference .method private hidebysig static void SimpleCapture() cil managed { // Code size 19 (0x13) .maxstack 2 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0' V_0) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.1 IL_0004: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0009: nop IL_000a: ldloca.s V_0 IL_000c: call int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions::'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'&) IL_0011: pop IL_0012: ret } // end of method NoLocalFunctions::SimpleCapture .method private hidebysig static void SimpleCaptureWithRef() cil managed { // Code size 34 (0x22) .maxstack 2 .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0' V_0, class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle V_1) IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::.ctor() IL_0005: stloc.0 IL_0006: nop IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::x IL_000e: nop IL_000f: ldloc.0 IL_0010: ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass2_0'::'g__F|0'() IL_0016: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_001b: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.Handle::.ctor(class [mscorlib]System.Func`1) IL_0020: stloc.1 IL_0021: ret } // end of method NoLocalFunctions::SimpleCaptureWithRef .method assembly hidebysig static int32 'g__F|0_0'() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 3 (0x3) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ret } // end of method NoLocalFunctions::'g__F|0_0' .method assembly hidebysig static int32 'g__F|1_0'(valuetype ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'& A_0) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 10 (0xa) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ldarg.0 IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions/'<>c__DisplayClass1_0'::x IL_0008: add IL_0009: ret } // end of method NoLocalFunctions::'g__F|1_0' } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoLocalFunctions // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.Expected.cs ================================================ using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoNewOfT where TOnType : new() { public static TOnType CreateTOnType() { #if !ROSLYN #if OPT if (default(TOnType) != null) { return default(TOnType); } return Activator.CreateInstance(); #else return (default(TOnType) == null) ? Activator.CreateInstance() : default(TOnType); #endif #else return Activator.CreateInstance(); #endif } public static T CreateUnconstrainedT() where T : new() { #if !ROSLYN #if OPT if (default(T) != null) { return default(T); } return Activator.CreateInstance(); #else return (default(T) == null) ? Activator.CreateInstance() : default(T); #endif #else return Activator.CreateInstance(); #endif } public static T CreateClassT() where T : class, new() { return Activator.CreateInstance(); } public static T CollectionInitializer() where T : IList, new() { #if ROSLYN T result = Activator.CreateInstance(); result.Add(1); result.Add(2); result.Add(3); result.Add(4); result.Add(5); return result; #else T val = ((default(T) == null) ? Activator.CreateInstance() : default(T)); val.Add(1); val.Add(2); val.Add(3); val.Add(4); val.Add(5); return val; #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.cs ================================================ using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoNewOfT where TOnType : new() { public static TOnType CreateTOnType() { return new TOnType(); } public static T CreateUnconstrainedT() where T : new() { return new T(); } public static T CreateClassT() where T : class, new() { return new T(); } public static T CollectionInitializer() where T : IList, new() { return new T() { 1, 2, 3, 4, 5 }; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpvpdwr1 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpvpdwr1.tmp // MVID: {8447adbb-757d-4626-b7b7-846d745d90d0} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [mscorlib]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 39 (0x27) .maxstack 1 .locals init (!TOnType V_0, !TOnType V_1) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: initobj !TOnType IL_0009: ldloc.1 IL_000a: box !TOnType IL_000f: brfalse.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj !TOnType IL_0019: ldloc.1 IL_001a: br.s IL_0021 IL_001c: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0021: nop IL_0022: stloc.0 IL_0023: br.s IL_0025 IL_0025: ldloc.0 IL_0026: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 39 (0x27) .maxstack 1 .locals init (!!T V_0, !!T V_1) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: initobj !!T IL_0009: ldloc.1 IL_000a: box !!T IL_000f: brfalse.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj !!T IL_0019: ldloc.1 IL_001a: br.s IL_0021 IL_001c: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0021: nop IL_0022: stloc.0 IL_0023: br.s IL_0025 IL_0025: ldloc.0 IL_0026: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [mscorlib]System.Collections.Generic.IList`1) T>() cil managed { // Code size 106 (0x6a) .maxstack 2 .locals init (!!T V_0, !!T V_1, !!T V_2) IL_0000: nop IL_0001: ldloca.s V_2 IL_0003: initobj !!T IL_0009: ldloc.2 IL_000a: box !!T IL_000f: brfalse.s IL_001c IL_0011: ldloca.s V_2 IL_0013: initobj !!T IL_0019: ldloc.2 IL_001a: br.s IL_0021 IL_001c: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0021: nop IL_0022: stloc.0 IL_0023: ldloc.0 IL_0024: box !!T IL_0029: ldc.i4.1 IL_002a: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_002f: nop IL_0030: ldloc.0 IL_0031: box !!T IL_0036: ldc.i4.2 IL_0037: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_003c: nop IL_003d: ldloc.0 IL_003e: box !!T IL_0043: ldc.i4.3 IL_0044: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0049: nop IL_004a: ldloc.0 IL_004b: box !!T IL_0050: ldc.i4.4 IL_0051: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0056: nop IL_0057: ldloc.0 IL_0058: box !!T IL_005d: ldc.i4.5 IL_005e: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0063: nop IL_0064: ldloc.0 IL_0065: stloc.1 IL_0066: br.s IL_0068 IL_0068: ldloc.1 IL_0069: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.net40.roslyn.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpyccvci { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpyccvci.tmp // MVID: {ff6a33b6-7f19-447b-b168-f5c528dc4b6f} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: nop IL_0007: ret } // end of method EmbeddedAttribute::.ctor } // end of class Microsoft.CodeAnalysis.EmbeddedAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RefSafetyRulesAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 02 00 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .field public initonly int32 Version .method public hidebysig specialname rtspecialname instance void .ctor(int32 A_1) cil managed { // Code size 15 (0xf) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: nop IL_0007: ldarg.0 IL_0008: ldarg.1 IL_0009: stfld int32 System.Runtime.CompilerServices.RefSafetyRulesAttribute::Version IL_000e: ret } // end of method RefSafetyRulesAttribute::.ctor } // end of class System.Runtime.CompilerServices.RefSafetyRulesAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 FF 7F 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 01 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .method public hidebysig specialname rtspecialname instance void .ctor(string featureName) cil managed { // Code size 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: nop IL_0007: nop IL_0008: ret } // end of method CompilerFeatureRequiredAttribute::.ctor } // end of class System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute .class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsExternalInit extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method IsExternalInit::.ctor } // end of class System.Runtime.CompilerServices.IsExternalInit .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiredMemberAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 8C 01 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: nop IL_0007: ret } // end of method RequiredMemberAttribute::.ctor } // end of class System.Runtime.CompilerServices.RequiredMemberAttribute .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [mscorlib]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!TOnType V_0) IL_0000: nop IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [mscorlib]System.Collections.Generic.IList`1) T>() cil managed { // Code size 88 (0x58) .maxstack 2 .locals init (!!T V_0, !!T V_1) IL_0000: nop IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldc.i4.1 IL_000a: constrained. !!T IL_0010: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0015: nop IL_0016: ldloca.s V_0 IL_0018: ldc.i4.2 IL_0019: constrained. !!T IL_001f: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0024: nop IL_0025: ldloca.s V_0 IL_0027: ldc.i4.3 IL_0028: constrained. !!T IL_002e: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0033: nop IL_0034: ldloca.s V_0 IL_0036: ldc.i4.4 IL_0037: constrained. !!T IL_003d: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0042: nop IL_0043: ldloca.s V_0 IL_0045: ldc.i4.5 IL_0046: constrained. !!T IL_004c: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0051: nop IL_0052: ldloc.0 IL_0053: stloc.1 IL_0054: br.s IL_0056 IL_0056: ldloc.1 IL_0057: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.opt.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp1ekgox { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp1ekgox.tmp // MVID: {67ce32bd-0144-4136-a198-2fc9f941f624} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [mscorlib]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 32 (0x20) .maxstack 1 .locals init (!TOnType V_0, !TOnType V_1) IL_0000: ldloca.s V_0 IL_0002: initobj !TOnType IL_0008: ldloc.0 IL_0009: box !TOnType IL_000e: brfalse.s IL_001a IL_0010: ldloca.s V_1 IL_0012: initobj !TOnType IL_0018: ldloc.1 IL_0019: ret IL_001a: call !!0 [mscorlib]System.Activator::CreateInstance() IL_001f: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 32 (0x20) .maxstack 1 .locals init (!!T V_0, !!T V_1) IL_0000: ldloca.s V_0 IL_0002: initobj !!T IL_0008: ldloc.0 IL_0009: box !!T IL_000e: brfalse.s IL_001a IL_0010: ldloca.s V_1 IL_0012: initobj !!T IL_0018: ldloc.1 IL_0019: ret IL_001a: call !!0 [mscorlib]System.Activator::CreateInstance() IL_001f: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [mscorlib]System.Collections.Generic.IList`1) T>() cil managed { // Code size 95 (0x5f) .maxstack 2 .locals init (!!T V_0, !!T V_1, !!T V_2) IL_0000: ldloca.s V_1 IL_0002: initobj !!T IL_0008: ldloc.1 IL_0009: box !!T IL_000e: brfalse.s IL_001b IL_0010: ldloca.s V_2 IL_0012: initobj !!T IL_0018: ldloc.2 IL_0019: br.s IL_0020 IL_001b: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0020: stloc.0 IL_0021: ldloc.0 IL_0022: box !!T IL_0027: ldc.i4.1 IL_0028: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_002d: ldloc.0 IL_002e: box !!T IL_0033: ldc.i4.2 IL_0034: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0039: ldloc.0 IL_003a: box !!T IL_003f: ldc.i4.3 IL_0040: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0045: ldloc.0 IL_0046: box !!T IL_004b: ldc.i4.4 IL_004c: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0051: ldloc.0 IL_0052: box !!T IL_0057: ldc.i4.5 IL_0058: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_005d: ldloc.0 IL_005e: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.opt.net40.roslyn.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp4xstme { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp4xstme.tmp // MVID: {a031faef-fcb2-420c-9cd3-6e9b01e7f81e} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method EmbeddedAttribute::.ctor } // end of class Microsoft.CodeAnalysis.EmbeddedAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RefSafetyRulesAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 02 00 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .field public initonly int32 Version .method public hidebysig specialname rtspecialname instance void .ctor(int32 A_1) cil managed { // Code size 14 (0xe) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld int32 System.Runtime.CompilerServices.RefSafetyRulesAttribute::Version IL_000d: ret } // end of method RefSafetyRulesAttribute::.ctor } // end of class System.Runtime.CompilerServices.RefSafetyRulesAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 FF 7F 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 01 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .method public hidebysig specialname rtspecialname instance void .ctor(string featureName) cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method CompilerFeatureRequiredAttribute::.ctor } // end of class System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute .class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsExternalInit extends [mscorlib]System.Object { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method IsExternalInit::.ctor } // end of class System.Runtime.CompilerServices.IsExternalInit .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiredMemberAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 8C 01 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow 4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65 // Multiple.T..Inhe 72 69 74 65 64 00 ) // rited. .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method RequiredMemberAttribute::.ctor } // end of class System.Runtime.CompilerServices.RequiredMemberAttribute .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [mscorlib]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [mscorlib]System.Collections.Generic.IList`1) T>() cil managed { // Code size 78 (0x4e) .maxstack 2 .locals init (!!T V_0) IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance() IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldc.i4.1 IL_0009: constrained. !!T IL_000f: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0014: ldloca.s V_0 IL_0016: ldc.i4.2 IL_0017: constrained. !!T IL_001d: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0022: ldloca.s V_0 IL_0024: ldc.i4.3 IL_0025: constrained. !!T IL_002b: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_0030: ldloca.s V_0 IL_0032: ldc.i4.4 IL_0033: constrained. !!T IL_0039: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_003e: ldloca.s V_0 IL_0040: ldc.i4.5 IL_0041: constrained. !!T IL_0047: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1::Add(!0) IL_004c: ldloc.0 IL_004d: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.opt.roslyn.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 10:0:0:0 } .assembly tmpauzspg { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 19 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V 65 72 73 69 6F 6E 3D 76 31 30 2E 30 00 00 ) // ersion=v10.0.. .permissionset reqmin = {[System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpauzspg.tmp // MVID: {6d3a3b63-d01c-49f7-9515-565421f78266} .custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [System.Runtime]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 6 (0x6) .maxstack 8 IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0005: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [System.Runtime]System.Collections.Generic.IList`1) T>() cil managed { // Code size 78 (0x4e) .maxstack 2 .locals init (!!T V_0) IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldc.i4.1 IL_0009: constrained. !!T IL_000f: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0014: ldloca.s V_0 IL_0016: ldc.i4.2 IL_0017: constrained. !!T IL_001d: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0022: ldloca.s V_0 IL_0024: ldc.i4.3 IL_0025: constrained. !!T IL_002b: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0030: ldloca.s V_0 IL_0032: ldc.i4.4 IL_0033: constrained. !!T IL_0039: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_003e: ldloca.s V_0 IL_0040: ldc.i4.5 IL_0041: constrained. !!T IL_0047: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_004c: ldloc.0 IL_004d: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoNewOfT.roslyn.il ================================================ // .NET IL Disassembler. Version 9.0.4 // Metadata version: v4.0.30319 .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: .ver 10:0:0:0 } .assembly tmpybdlca { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 19 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V 65 72 73 69 6F 6E 3D 76 31 30 2E 30 00 00 ) // ersion=v10.0.. .permissionset reqmin = {[System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpybdlca.tmp // MVID: {b57f13a4-d291-42ea-b595-3766c1f1470c} .custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1<.ctor TOnType> extends [System.Runtime]System.Object { .method public hidebysig static !TOnType CreateTOnType() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!TOnType V_0) IL_0000: nop IL_0001: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateTOnType .method public hidebysig static !!T CreateUnconstrainedT<.ctor T>() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateUnconstrainedT .method public hidebysig static !!T CreateClassT() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init (!!T V_0) IL_0000: nop IL_0001: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method NoNewOfT`1::CreateClassT .method public hidebysig static !!T CollectionInitializer<.ctor (class [System.Runtime]System.Collections.Generic.IList`1) T>() cil managed { // Code size 88 (0x58) .maxstack 2 .locals init (!!T V_0, !!T V_1) IL_0000: nop IL_0001: call !!0 [System.Runtime]System.Activator::CreateInstance() IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldc.i4.1 IL_000a: constrained. !!T IL_0010: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0015: nop IL_0016: ldloca.s V_0 IL_0018: ldc.i4.2 IL_0019: constrained. !!T IL_001f: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0024: nop IL_0025: ldloca.s V_0 IL_0027: ldc.i4.3 IL_0028: constrained. !!T IL_002e: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0033: nop IL_0034: ldloca.s V_0 IL_0036: ldc.i4.4 IL_0037: constrained. !!T IL_003d: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0042: nop IL_0043: ldloca.s V_0 IL_0045: ldc.i4.5 IL_0046: constrained. !!T IL_004c: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0) IL_0051: nop IL_0052: ldloc.0 IL_0053: stloc.1 IL_0054: br.s IL_0056 IL_0056: ldloc.1 IL_0057: ret } // end of method NoNewOfT`1::CollectionInitializer .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoNewOfT`1::.ctor } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoNewOfT`1 // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.Expected.cs ================================================ // Copyright (c) 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 // 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; #if ROSLYN #if !OPT using System.Diagnostics; #endif using System.Runtime.CompilerServices; #endif using System.Threading; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoPropertiesAndEvents { #if ROSLYN [CompilerGenerated] #if !OPT [DebuggerBrowsable(DebuggerBrowsableState.Never)] #endif #endif private EventHandler m_MyEvent; public event EventHandler MyEvent { #if ROSLYN [CompilerGenerated] #endif add { EventHandler eventHandler = this.m_MyEvent; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.m_MyEvent, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } #if ROSLYN [CompilerGenerated] #endif remove { EventHandler eventHandler = this.m_MyEvent; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.m_MyEvent, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.cs ================================================ // Copyright (c) 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 // 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; namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { internal class NoPropertiesAndEvents { public event EventHandler MyEvent; } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp8FA { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp8FA.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { // Code size 48 (0x30) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2, bool V_3) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: ceq IL_0028: ldc.i4.0 IL_0029: ceq IL_002b: stloc.3 IL_002c: ldloc.3 IL_002d: brtrue.s IL_0007 IL_002f: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { // Code size 48 (0x30) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2, bool V_3) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: ceq IL_0028: ldc.i4.0 IL_0029: ceq IL_002b: stloc.3 IL_002c: ldloc.3 IL_002d: brtrue.s IL_0007 IL_002f: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpB115 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpB115.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.opt.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp8FB { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp8FB.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.opt.net40.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmpB126 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmpB126.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.opt.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp8F9 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp8F9.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoPropertiesAndEvents.roslyn.il ================================================ // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly tmp8F8 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .permissionset reqmin = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 0:0:0:0 } .module tmp8F8.tmp .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents extends [mscorlib]System.Object { .field private class [mscorlib]System.EventHandler MyEvent .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .method public hidebysig specialname instance void add_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::add_MyEvent .method public hidebysig specialname instance void remove_MyEvent(class [mscorlib]System.EventHandler 'value') cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 41 (0x29) .maxstack 3 .locals init (class [mscorlib]System.EventHandler V_0, class [mscorlib]System.EventHandler V_1, class [mscorlib]System.EventHandler V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass [mscorlib]System.EventHandler IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class [mscorlib]System.EventHandler ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::MyEvent IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret } // end of method NoPropertiesAndEvents::remove_MyEvent .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method NoPropertiesAndEvents::.ctor .event [mscorlib]System.EventHandler MyEvent { .addon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::add_MyEvent(class [mscorlib]System.EventHandler) .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents::remove_MyEvent(class [mscorlib]System.EventHandler) } // end of event NoPropertiesAndEvents::MyEvent } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoPropertiesAndEvents // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore ================================================ /*.dll /*.exe /*.pdb ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Async.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.VisualBasic.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty { public class Async { private int memberField; private static bool True() { return true; } public async void SimpleVoidMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); } public async void VoidMethodWithoutAwait() { Console.WriteLine("No Await"); } public async void EmptyVoidMethod() { } public async void AwaitYield() { await Task.Yield(); } public async void AwaitDefaultYieldAwaitable() { #if LEGACY_VBC || (OPTIMIZE && !ROSLYN4) YieldAwaitable yieldAwaitable2 = default(YieldAwaitable); YieldAwaitable yieldAwaitable = yieldAwaitable2; await yieldAwaitable; #else await default(YieldAwaitable); #endif } public async void AwaitDefaultHopToThreadPool() { #if LEGACY_VBC || (OPTIMIZE && !ROSLYN4) HopToThreadPoolAwaitable hopToThreadPoolAwaitable2 = default(HopToThreadPoolAwaitable); HopToThreadPoolAwaitable hopToThreadPoolAwaitable = hopToThreadPoolAwaitable2; await hopToThreadPoolAwaitable; #else await default(HopToThreadPoolAwaitable); #endif } public async Task SimpleVoidTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); } public async Task TaskMethodWithoutAwait() { Console.WriteLine("No Await"); } public async Task CapturingThis() { await Task.Delay(memberField); } public async Task CapturingThisWithoutAwait() { Console.WriteLine(memberField); } public async Task SimpleBoolTaskMethod() { Console.WriteLine("Before"); await Task.Delay(TimeSpan.FromSeconds(1.0)); Console.WriteLine("After"); return true; } public async void TwoAwaitsWithDifferentAwaiterTypes() { Console.WriteLine("Before"); if (await SimpleBoolTaskMethod()) { await Task.Delay(TimeSpan.FromSeconds(1.0)); } Console.WriteLine("After"); } public async void AwaitInLoopCondition() { while (await SimpleBoolTaskMethod()) { Console.WriteLine("Body"); } } public async Task Issue2366a() { while (true) { try { await Task.CompletedTask; } catch (Exception projectError) { ProjectData.SetProjectError(projectError); ProjectData.ClearProjectError(); } } } public static async Task GetIntegerSumAsync(IEnumerable items) { await Task.Delay(100); int num = 0; foreach (int item in items) { num = checked(num + item); } return num; } public async Task AsyncCatch(bool b, Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception projectError) { ProjectData.SetProjectError(projectError); Console.WriteLine("No await"); ProjectData.ClearProjectError(); } } public async Task AsyncCatchThrow(bool b, Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } catch (Exception projectError) { ProjectData.SetProjectError(projectError); Console.WriteLine("No await"); throw; } } public async Task AsyncFinally(bool b, Task task1, Task task2) { try { Console.WriteLine("Start try"); await task1; Console.WriteLine("End try"); } finally { Console.WriteLine("No await"); } } public static async Task AlwaysThrow() { throw new Exception(); } public static async Task InfiniteLoop() { while (true) { } } public static async Task InfiniteLoopWithAwait() { while (true) { await Task.Delay(10); } } public async Task AsyncWithLocalVar() { object objectValue = RuntimeHelpers.GetObjectValue(new object()); await UseObj(RuntimeHelpers.GetObjectValue(objectValue)); await UseObj(RuntimeHelpers.GetObjectValue(objectValue)); } public static async Task UseObj(object a) { } } public struct AsyncInStruct { private int i; public async Task Test(AsyncInStruct xx) { checked { xx.i++; i++; await Task.Yield(); return i + xx.i; } } } public struct HopToThreadPoolAwaitable : INotifyCompletion { public bool IsCompleted { get; set; } public HopToThreadPoolAwaitable GetAwaiter() { return this; } public void OnCompleted(Action continuation) { Task.Run(continuation); } void INotifyCompletion.OnCompleted(Action continuation) { //ILSpy generated this explicit interface implementation from .override directive in OnCompleted this.OnCompleted(continuation); } public void GetResult() { } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Async.vb ================================================ Imports System Imports System.Collections.Generic Imports System.Threading.Tasks Imports System.Runtime.CompilerServices Namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty Public Class Async Private memberField As Integer Private Shared Function [True]() As Boolean Return True End Function Public Async Sub SimpleVoidMethod() Console.WriteLine("Before") Await Task.Delay(TimeSpan.FromSeconds(1.0)) Console.WriteLine("After") End Sub Public Async Sub VoidMethodWithoutAwait() Console.WriteLine("No Await") End Sub Public Async Sub EmptyVoidMethod() End Sub Public Async Sub AwaitYield() Await Task.Yield() End Sub Public Async Sub AwaitDefaultYieldAwaitable() Await CType(Nothing, YieldAwaitable) End Sub Public Async Sub AwaitDefaultHopToThreadPool() ' unlike YieldAwaitable which implements ICriticalNotifyCompletion, ' the HopToThreadPoolAwaitable struct only implements ' INotifyCompletion, so this results in different codegen Await CType(Nothing, HopToThreadPoolAwaitable) End Sub Public Async Function SimpleVoidTaskMethod() As Task Console.WriteLine("Before") Await Task.Delay(TimeSpan.FromSeconds(1.0)) Console.WriteLine("After") End Function Public Async Function TaskMethodWithoutAwait() As Task Console.WriteLine("No Await") End Function Public Async Function CapturingThis() As Task Await Task.Delay(memberField) End Function Public Async Function CapturingThisWithoutAwait() As Task Console.WriteLine(memberField) End Function Public Async Function SimpleBoolTaskMethod() As Task(Of Boolean) Console.WriteLine("Before") Await Task.Delay(TimeSpan.FromSeconds(1.0)) Console.WriteLine("After") Return True End Function Public Async Sub TwoAwaitsWithDifferentAwaiterTypes() Console.WriteLine("Before") If Await SimpleBoolTaskMethod() Then Await Task.Delay(TimeSpan.FromSeconds(1.0)) End If Console.WriteLine("After") End Sub Public Async Sub AwaitInLoopCondition() While Await SimpleBoolTaskMethod() Console.WriteLine("Body") End While End Sub Public Async Function Issue2366a() As Task While True Try Await Task.CompletedTask Catch End Try End While End Function Public Shared Async Function GetIntegerSumAsync(ByVal items As IEnumerable(Of Integer)) As Task(Of Integer) Await Task.Delay(100) Dim num = 0 For Each item In items num += item Next Return num End Function Public Async Function AsyncCatch(ByVal b As Boolean, ByVal task1 As Task(Of Integer), ByVal task2 As Task(Of Integer)) As Task Try Console.WriteLine("Start try") Await task1 Console.WriteLine("End try") Catch ex As Exception Console.WriteLine("No await") End Try End Function Public Async Function AsyncCatchThrow(ByVal b As Boolean, ByVal task1 As Task(Of Integer), ByVal task2 As Task(Of Integer)) As Task Try Console.WriteLine("Start try") Await task1 Console.WriteLine("End try") Catch ex As Exception Console.WriteLine("No await") Throw End Try End Function Public Async Function AsyncFinally(ByVal b As Boolean, ByVal task1 As Task(Of Integer), ByVal task2 As Task(Of Integer)) As Task Try Console.WriteLine("Start try") Await task1 Console.WriteLine("End try") Finally Console.WriteLine("No await") End Try End Function Public Shared Async Function AlwaysThrow() As Task Throw New Exception End Function Public Shared Async Function InfiniteLoop() As Task While True End While End Function Public Shared Async Function InfiniteLoopWithAwait() As Task While True Await Task.Delay(10) End While End Function Public Async Function AsyncWithLocalVar() As Task Dim a As Object = New Object() Await UseObj(a) Await UseObj(a) End Function Public Shared Async Function UseObj(ByVal a As Object) As Task End Function End Class Public Structure AsyncInStruct Private i As Integer Public Async Function Test(ByVal xx As AsyncInStruct) As Task(Of Integer) xx.i += 1 i += 1 Await Task.Yield() Return i + xx.i End Function End Structure Public Structure HopToThreadPoolAwaitable Implements INotifyCompletion Public Property IsCompleted As Boolean Public Function GetAwaiter() As HopToThreadPoolAwaitable Return Me End Function Public Sub OnCompleted(ByVal continuation As Action) Implements INotifyCompletion.OnCompleted Task.Run(continuation) End Sub Public Sub GetResult() End Sub End Structure End Namespace ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue1906.cs ================================================ using System; public class Issue1906 { public void M() { Console.WriteLine(Math.Min(Math.Max(long.MinValue, default(long)), long.MaxValue)); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue1906.vb ================================================ Imports System Public Class Issue1906 Public Sub M() Console.WriteLine(Math.Min(Math.Max(Int64.MinValue, New Long), Int64.MaxValue)) End Sub End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue2192.cs ================================================ using System; using System.Linq; using System.Runtime.CompilerServices; public class Issue2192 { public static void M() { string[] source = new string[3] { "abc", "defgh", "ijklm" }; string text = "test"; Console.WriteLine(source.Count([SpecialName] (string w) => w.Length > text.Length)); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue2192.vb ================================================ Imports System Imports System.Linq Public Class Issue2192 Public Shared Sub M() Dim words As String() = {"abc", "defgh", "ijklm"} Dim word As String = "test" Console.WriteLine(words.Count(Function(w) w.Length > word.Length)) End Sub End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs ================================================ using System; using Microsoft.VisualBasic.CompilerServices; [StandardModule] internal sealed class Program { public static void SelectOnString() { switch (Environment.CommandLine) { case "123": Console.WriteLine("a"); break; case "444": Console.WriteLine("b"); break; case "222": Console.WriteLine("c"); break; case "11": Console.WriteLine("d"); break; case "dd": Console.WriteLine("e"); break; case "sss": Console.WriteLine("f"); break; case "aa": Console.WriteLine("g"); break; case null: case "": Console.WriteLine("empty"); break; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb ================================================ Imports System Module Program Sub SelectOnString() Select Case Environment.CommandLine Case "123" Console.WriteLine("a") Case "444" Console.WriteLine("b") Case "222" Console.WriteLine("c") Case "11" Console.WriteLine("d") Case "dd" Console.WriteLine("e") Case "sss" Console.WriteLine("f") Case "aa" Console.WriteLine("g") Case "" Console.WriteLine("empty") End Select End Sub End Module ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBAutomaticEvents.cs ================================================ public class VBAutomaticEvents { public delegate void EventWithParameterEventHandler(int EventNumber); public delegate void EventWithoutParameterEventHandler(); public event EventWithParameterEventHandler EventWithParameter; public event EventWithoutParameterEventHandler EventWithoutParameter; public void RaiseEvents() { EventWithParameter?.Invoke(1); EventWithoutParameter?.Invoke(); } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBAutomaticEvents.vb ================================================ Public Class VBAutomaticEvents Event EventWithParameter(ByVal EventNumber As Integer) Event EventWithoutParameter() Sub RaiseEvents() RaiseEvent EventWithParameter(1) RaiseEvent EventWithoutParameter() End Sub End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs ================================================ using Microsoft.VisualBasic; using Microsoft.VisualBasic.CompilerServices; [StandardModule] internal sealed class VBCompoundAssign { public static double[] Sum3(int[] v) { double[] array = new double[4]; int num = Information.UBound(v); checked { for (int i = 0; i <= num; i += 3) { array[0] += v[i]; array[1] += v[i + 1]; array[2] += v[i + 2]; } return array; } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb ================================================ Imports System Imports Microsoft.VisualBasic Module VBCompoundAssign Function Sum3(v As Int32()) As Double() Dim arr(3) As Double For i = 0 To UBound(v) Step 3 arr(0) += v(i) arr(1) += v(i + 1) arr(2) += v(i + 2) Next Return arr End Function End Module ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBNonGenericForEach.cs ================================================ using System; using System.Collections; using System.Runtime.CompilerServices; public class VBNonGenericForEach { public static void M() { ArrayList arrayList = new ArrayList(); foreach (object item in arrayList) { #if ROSLYN && OPT Console.WriteLine(RuntimeHelpers.GetObjectValue(RuntimeHelpers.GetObjectValue(item))); #else object objectValue = RuntimeHelpers.GetObjectValue(item); Console.WriteLine(RuntimeHelpers.GetObjectValue(objectValue)); #endif } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBNonGenericForEach.vb ================================================ Imports System Imports System.Collections Public Class VBNonGenericForEach Public Shared Sub M() Dim collection = New ArrayList For Each element In collection Console.WriteLine(element) Next End Sub End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.cs ================================================ using System; public class VBPropertiesTest { private int _fullProperty; public int FullProperty { get { return _fullProperty; } set { _fullProperty = value; } } public int AutoProperty { get; set; } #if ROSLYN public int ReadOnlyAutoProperty { get; } public VBPropertiesTest() { ReadOnlyAutoProperty = 32; } #endif public void TestMethod() { FullProperty = 42; _fullProperty = 24; AutoProperty = 4711; Console.WriteLine(AutoProperty); Console.WriteLine(_fullProperty); Console.WriteLine(FullProperty); #if ROSLYN Console.WriteLine(ReadOnlyAutoProperty); #endif } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.vb ================================================ Imports System Public Class VBPropertiesTest Private _fullProperty As Integer Property FullProperty As Integer Get Return _fullProperty End Get Set(value As Integer) _fullProperty = value End Set End Property Property AutoProperty As Integer #If ROSLYN Then ReadOnly Property ReadOnlyAutoProperty As Integer Sub New() Me.ReadOnlyAutoProperty = 32 End Sub #End If Sub TestMethod() Me.FullProperty = 42 Me._fullProperty = 24 Me.AutoProperty = 4711 Console.WriteLine(Me.AutoProperty) Console.WriteLine(Me._fullProperty) Console.WriteLine(Me.FullProperty) #If ROSLYN Then Console.WriteLine(Me.ReadOnlyAutoProperty) #End If End Sub End Class ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.cs ================================================ using System; using System.Collections.Generic; using Microsoft.VisualBasic.CompilerServices; namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty { internal struct StructWithYieldReturn { private int val; public IEnumerable Count() { yield return val; yield return val; } } public class YieldReturnPrettyTest { private int fieldOnThis; public static IEnumerable YieldChars { get { yield return 'a'; yield return 'b'; yield return 'c'; } } internal static void Print(string name, IEnumerator enumerator) { Console.WriteLine(name + ": Test start"); while (enumerator.MoveNext()) { Console.WriteLine(name + ": " + enumerator.Current.ToString()); } } public static IEnumerable SimpleYieldReturn() { yield return "A"; yield return "B"; yield return "C"; } public static IEnumerator SimpleYieldReturnEnumerator() { yield return "A"; yield return "B"; yield return "C"; } public IEnumerable YieldReturnParameters(int p) { yield return p; yield return fieldOnThis; } public IEnumerator YieldReturnParametersEnumerator(int p) { yield return p; yield return fieldOnThis; } public static IEnumerable YieldReturnInLoop() { int num = 0; do { yield return num; num = checked(num + 1); } while (num <= 99); } public static IEnumerable YieldReturnWithTryFinally() { yield return 0; try { yield return 1; } finally { Console.WriteLine("Finally!"); } yield return 2; } public static IEnumerable YieldReturnWithNestedTryFinally(bool breakInMiddle) { Console.WriteLine("Start of method - 1"); yield return "Start of method"; Console.WriteLine("Start of method - 2"); try { Console.WriteLine("Within outer try - 1"); yield return "Within outer try"; Console.WriteLine("Within outer try - 2"); try { Console.WriteLine("Within inner try - 1"); yield return "Within inner try"; Console.WriteLine("Within inner try - 2"); if (breakInMiddle) { Console.WriteLine("Breaking..."); yield break; } Console.WriteLine("End of inner try - 1"); yield return "End of inner try"; Console.WriteLine("End of inner try - 2"); } finally { Console.WriteLine("Inner Finally"); } Console.WriteLine("End of outer try - 1"); yield return "End of outer try"; Console.WriteLine("End of outer try - 2"); } finally { Console.WriteLine("Outer Finally"); } Console.WriteLine("End of method - 1"); yield return "End of method"; Console.WriteLine("End of method - 2"); } public static IEnumerable YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable input) { // outer try-finally block foreach (string item in input) { // nested try-finally block try { yield return item; } finally { Console.WriteLine("Processed " + item); } } yield return "A"; yield return "B"; yield return "C"; yield return "D"; yield return "E"; yield return "F"; // outer try-finally block foreach (string item2 in input) { yield return item2.ToUpper(); } } public static IEnumerable GetEvenNumbers(int n) { int num = checked(n - 1); for (int i = 0; i <= num; i = checked(i + 1)) { if (i % 2 == 0) { yield return i; } } } public static IEnumerable ExceptionHandling() { yield return 'a'; try { Console.WriteLine("1 - try"); } catch (Exception projectError) { ProjectData.SetProjectError(projectError); Console.WriteLine("1 - catch"); ProjectData.ClearProjectError(); } yield return 'b'; try { try { Console.WriteLine("2 - try"); } finally { Console.WriteLine("2 - finally"); } yield return 'c'; } finally { Console.WriteLine("outer finally"); } } public static IEnumerable YieldBreakInCatch() { yield return 0; try { Console.WriteLine("In Try"); } catch (Exception projectError) { ProjectData.SetProjectError(projectError); ProjectData.ClearProjectError(); // yield return is not allowed in catch, but yield break is yield break; } yield return 1; } public static IEnumerable YieldBreakInCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); } catch (Exception projectError) { ProjectData.SetProjectError(projectError); ProjectData.ClearProjectError(); // yield return is not allowed in catch, but yield break is // Note that pre-roslyn, this code triggers a compiler bug: // If the finally block throws an exception, it ends up getting // called a second time. yield break; } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryCatchInTryFinally() { try { yield return 0; try { Console.WriteLine("In Try"); // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } catch (Exception projectError) { ProjectData.SetProjectError(projectError); Console.WriteLine("Catch"); ProjectData.ClearProjectError(); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakInTryFinallyInTryFinally(bool b) { try { yield return 0; try { Console.WriteLine("In Try"); if (b) { // same compiler bug as in YieldBreakInCatchInTryFinally yield break; } } finally { Console.WriteLine("Inner Finally"); } yield return 1; } finally { Console.WriteLine("Finally"); } } public static IEnumerable YieldBreakOnly() { yield break; } public static IEnumerable UnconditionalThrowInTryFinally() { // Here, MoveNext() doesn't call the finally methods at all // (only indirectly via Dispose()) try { yield return 0; throw new NotImplementedException(); } finally { Console.WriteLine("Finally"); } } public static IEnumerable NestedTryFinallyStartingOnSamePosition() { // The first user IL instruction is already in 2 nested try blocks. #if ((ROSLYN2 && !ROSLYN4) && OPT) int num = -1; #endif try { #if ((ROSLYN2 && !ROSLYN4) && OPT) _ = num - 1; #endif try { yield return 0; } finally { Console.WriteLine("Inner Finally"); } } finally { Console.WriteLine("Outer Finally"); } } #if ROSLYN public static IEnumerable LocalInFinally(T a) where T : IDisposable { yield return 1; try { yield return 2; } finally { T val = a; val.Dispose(); val.Dispose(); } yield return 3; } #endif public static IEnumerable GenericYield() where T : new() { T val = new T(); int num = 0; do { yield return val; num = checked(num + 1); } while (num <= 2); } public static IEnumerable MultipleYieldBreakInTryFinally(int i) { try { if (i == 2) { yield break; } while (i < 40) { if (i % 2 == 0) { yield break; } i = checked(i + 1); yield return i; } } finally { Console.WriteLine("finally"); } Console.WriteLine("normal exit"); } internal IEnumerable ForLoopWithYieldReturn(int end, int evil) { // This loop needs to pick the implicit "yield break;" as exit point // in order to produce pretty code; not the "throw" which would // be a less-pretty option. checked { int num = end - 1; for (int i = 0; i <= num; i++) { if (i == evil) { throw new InvalidOperationException("Found evil number"); } yield return i; } } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.vb ================================================ Imports System Imports System.Collections.Generic Namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty Friend Structure StructWithYieldReturn Private val As Integer Public Iterator Function Count() As IEnumerable(Of Integer) Yield val Yield val End Function End Structure Public Class YieldReturnPrettyTest Private fieldOnThis As Integer Public Shared ReadOnly Iterator Property YieldChars As IEnumerable(Of Char) Get Yield "a"c Yield "b"c Yield "c"c End Get End Property Friend Shared Sub Print(Of T)(ByVal name As String, ByVal enumerator As IEnumerator(Of T)) Console.WriteLine(name + ": Test start") While enumerator.MoveNext() Console.WriteLine(name + ": " + enumerator.Current.ToString()) End While End Sub Public Shared Iterator Function SimpleYieldReturn() As IEnumerable(Of String) Yield "A" Yield "B" Yield "C" End Function Public Shared Iterator Function SimpleYieldReturnEnumerator() As IEnumerator(Of String) Yield "A" Yield "B" Yield "C" End Function Public Iterator Function YieldReturnParameters(ByVal p As Integer) As IEnumerable(Of Integer) Yield p Yield fieldOnThis End Function Public Iterator Function YieldReturnParametersEnumerator(ByVal p As Integer) As IEnumerator(Of Integer) Yield p Yield fieldOnThis End Function Public Shared Iterator Function YieldReturnInLoop() As IEnumerable(Of Integer) For i = 0 To 99 Yield i Next End Function Public Shared Iterator Function YieldReturnWithTryFinally() As IEnumerable(Of Integer) Yield 0 Try Yield 1 Finally Console.WriteLine("Finally!") End Try Yield 2 End Function Public Shared Iterator Function YieldReturnWithNestedTryFinally(ByVal breakInMiddle As Boolean) As IEnumerable(Of String) Console.WriteLine("Start of method - 1") Yield "Start of method" Console.WriteLine("Start of method - 2") Try Console.WriteLine("Within outer try - 1") Yield "Within outer try" Console.WriteLine("Within outer try - 2") Try Console.WriteLine("Within inner try - 1") Yield "Within inner try" Console.WriteLine("Within inner try - 2") If breakInMiddle Then Console.WriteLine("Breaking...") Return End If Console.WriteLine("End of inner try - 1") Yield "End of inner try" Console.WriteLine("End of inner try - 2") Finally Console.WriteLine("Inner Finally") End Try Console.WriteLine("End of outer try - 1") Yield "End of outer try" Console.WriteLine("End of outer try - 2") Finally Console.WriteLine("Outer Finally") End Try Console.WriteLine("End of method - 1") Yield "End of method" Console.WriteLine("End of method - 2") End Function Public Shared Iterator Function YieldReturnWithTwoNonNestedFinallyBlocks(ByVal input As IEnumerable(Of String)) As IEnumerable(Of String) ' outer try-finally block For Each line In input ' nested try-finally block Try Yield line Finally Console.WriteLine("Processed " & line) End Try Next Yield "A" Yield "B" Yield "C" Yield "D" Yield "E" Yield "F" ' outer try-finally block For Each item In input Yield item.ToUpper() Next End Function Public Shared Iterator Function GetEvenNumbers(ByVal n As Integer) As IEnumerable(Of Integer) For i = 0 To n - 1 If i Mod 2 = 0 Then Yield i End If Next End Function Public Shared Iterator Function ExceptionHandling() As IEnumerable(Of Char) Yield "a"c Try Console.WriteLine("1 - try") Catch __unusedException1__ As Exception Console.WriteLine("1 - catch") End Try Yield "b"c Try Try Console.WriteLine("2 - try") Finally Console.WriteLine("2 - finally") End Try Yield "c"c Finally Console.WriteLine("outer finally") End Try End Function Public Shared Iterator Function YieldBreakInCatch() As IEnumerable(Of Integer) Yield 0 Try Console.WriteLine("In Try") Catch ' yield return is not allowed in catch, but yield break is Return End Try Yield 1 End Function Public Shared Iterator Function YieldBreakInCatchInTryFinally() As IEnumerable(Of Integer) Try Yield 0 Try Console.WriteLine("In Try") Catch ' yield return is not allowed in catch, but yield break is ' Note that pre-roslyn, this code triggers a compiler bug: ' If the finally block throws an exception, it ends up getting ' called a second time. Return End Try Yield 1 Finally Console.WriteLine("Finally") End Try End Function Public Shared Iterator Function YieldBreakInTryCatchInTryFinally() As IEnumerable(Of Integer) Try Yield 0 Try Console.WriteLine("In Try") ' same compiler bug as in YieldBreakInCatchInTryFinally Return Catch Console.WriteLine("Catch") End Try Yield 1 Finally Console.WriteLine("Finally") End Try End Function Public Shared Iterator Function YieldBreakInTryFinallyInTryFinally(ByVal b As Boolean) As IEnumerable(Of Integer) Try Yield 0 Try Console.WriteLine("In Try") If b Then ' same compiler bug as in YieldBreakInCatchInTryFinally Return End If Finally Console.WriteLine("Inner Finally") End Try Yield 1 Finally Console.WriteLine("Finally") End Try End Function Public Shared Iterator Function YieldBreakOnly() As IEnumerable(Of Integer) Return End Function Public Shared Iterator Function UnconditionalThrowInTryFinally() As IEnumerable(Of Integer) ' Here, MoveNext() doesn't call the finally methods at all ' (only indirectly via Dispose()) Try Yield 0 Throw New NotImplementedException() Finally Console.WriteLine("Finally") End Try End Function Public Shared Iterator Function NestedTryFinallyStartingOnSamePosition() As IEnumerable(Of Integer) ' The first user IL instruction is already in 2 nested try blocks. Try Try Yield 0 Finally Console.WriteLine("Inner Finally") End Try Finally Console.WriteLine("Outer Finally") End Try End Function #If ROSLYN Then Public Shared Iterator Function LocalInFinally(Of T As IDisposable)(ByVal a As T) As IEnumerable(Of Integer) Yield 1 Try Yield 2 Finally Dim val = a val.Dispose() val.Dispose() End Try Yield 3 End Function #End If Public Shared Iterator Function GenericYield(Of T As New)() As IEnumerable(Of T) Dim val As T = New T() For i = 0 To 2 Yield val Next End Function Public Shared Iterator Function MultipleYieldBreakInTryFinally(ByVal i As Integer) As IEnumerable(Of Integer) Try If i = 2 Then Return End If While i < 40 If i Mod 2 = 0 Then Return End If i += 1 Yield i End While Finally Console.WriteLine("finally") End Try Console.WriteLine("normal exit") End Function Friend Iterator Function ForLoopWithYieldReturn(ByVal [end] As Integer, ByVal evil As Integer) As IEnumerable(Of Integer) ' This loop needs to pick the implicit "yield break;" as exit point ' in order to produce pretty code; not the "throw" which would ' be a less-pretty option. For i = 0 To [end] - 1 If i = evil Then Throw New InvalidOperationException("Found evil number") End If Yield i Next End Function End Class End Namespace ================================================ FILE: ICSharpCode.Decompiler.Tests/TestTraceListener.cs ================================================ // Copyright (c) 2016 Daniel Grunwald // // 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.Diagnostics; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler { [SetUpFixture] public class TestTraceListener : DefaultTraceListener { [OneTimeSetUp] public void RunBeforeAnyTests() { Trace.Listeners.Insert(0, this); } [OneTimeTearDown] public void RunAfterAnyTests() { Trace.Listeners.Remove(this); } public override void Fail(string message, string detailMessage) { Assert.Fail(message + " " + detailMessage); } } [SetUpFixture] public class ToolsetSetup { [OneTimeSetUp] public async Task RunBeforeAnyTests() { await Tester.Initialize().ConfigureAwait(false); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TypeSystem/ReflectionHelperTests.cs ================================================ // Copyright (c) 2010-2013 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 // 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; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.TypeSystem { [TestFixture] public unsafe class ReflectionHelperTests { ICompilation compilation = new SimpleCompilation(TypeSystemLoaderTests.Mscorlib); void TestFindType(Type type) { IType t = compilation.FindType(type); Assert.That(t, Is.Not.Null, type.FullName); Assert.That(t.ReflectionName, Is.EqualTo(type.FullName)); } [Test] public void TestGetInnerClass() { TestFindType(typeof(Environment.SpecialFolder)); } [Test] public void TestGetGenericClass1() { TestFindType(typeof(Action<>)); } [Test] public void TestGetGenericClass2() { TestFindType(typeof(Action<,>)); } [Test] public void TestGetInnerClassInGenericClass1() { TestFindType(typeof(Dictionary<,>.ValueCollection)); } [Test] public void TestGetInnerClassInGenericClass2() { TestFindType(typeof(Dictionary<,>.ValueCollection.Enumerator)); } [Test] public void TestFindTypeReflectionNameInnerClass() { Assert.That(compilation.FindType(typeof(Environment.SpecialFolder)).ReflectionName, Is.EqualTo("System.Environment+SpecialFolder")); } [Test] public void TestFindTypeReflectionNameUnboundGenericClass() { Assert.That(compilation.FindType(typeof(Action<>)).ReflectionName, Is.EqualTo("System.Action`1")); Assert.That(compilation.FindType(typeof(Action<,>)).ReflectionName, Is.EqualTo("System.Action`2")); } [Test] public void TestFindTypeReflectionNameBoundGenericClass() { Assert.That(compilation.FindType(typeof(Action)).ReflectionName, Is.EqualTo("System.Action`1[[System.String]]")); Assert.That(compilation.FindType(typeof(Action)).ReflectionName, Is.EqualTo("System.Action`2[[System.Int32],[System.Int16]]")); } [Test] public void TestFindTypeReflectionNameNullableType() { Assert.That(compilation.FindType(typeof(int?)).ReflectionName, Is.EqualTo("System.Nullable`1[[System.Int32]]")); } [Test] public void TestFindTypeReflectionNameInnerClassInUnboundGenericType() { Assert.That(compilation.FindType(typeof(Dictionary<,>.ValueCollection)).ReflectionName, Is.EqualTo("System.Collections.Generic.Dictionary`2+ValueCollection")); } [Test] public void TestFindTypeReflectionNameInnerClassInBoundGenericType() { Assert.That(compilation.FindType(typeof(Dictionary.KeyCollection)).ReflectionName, Is.EqualTo("System.Collections.Generic.Dictionary`2+KeyCollection[[System.String],[System.Int32]]")); } [Test] public void TestFindTypeReflectionNameArrayType() { Assert.That(compilation.FindType(typeof(int[])).ReflectionName, Is.EqualTo(typeof(int[]).FullName)); } [Test] public void TestFindTypeReflectionNameMultidimensionalArrayType() { Assert.That(compilation.FindType(typeof(int[,])).ReflectionName, Is.EqualTo(typeof(int[,]).FullName)); } [Test] public void TestFindTypeReflectionNameJaggedMultidimensionalArrayType() { Assert.That(compilation.FindType(typeof(int[,][,,])).ReflectionName, Is.EqualTo(typeof(int[,][,,]).FullName)); } [Test] public void TestFindTypeReflectionNamePointerType() { Assert.That(compilation.FindType(typeof(int*)).ReflectionName, Is.EqualTo(typeof(int*).FullName)); } [Test] public void TestFindTypeReflectionNameByReferenceType() { Assert.That(compilation.FindType(typeof(int).MakeByRefType()).ReflectionName, Is.EqualTo(typeof(int).MakeByRefType().FullName)); } [Test] public void ParseReflectionName() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("System.Int32", context).ReflectionName, Is.EqualTo("System.Int32")); Assert.That(ReflectionHelper.ParseReflectionName("System.Int32&", context).ReflectionName, Is.EqualTo("System.Int32&")); Assert.That(ReflectionHelper.ParseReflectionName("System.Int32*&", context).ReflectionName, Is.EqualTo("System.Int32*&")); Assert.That(ReflectionHelper.ParseReflectionName(typeof(int).AssemblyQualifiedName, context).ReflectionName, Is.EqualTo("System.Int32")); Assert.That(ReflectionHelper.ParseReflectionName("System.Action`1[[System.String]]", context).ReflectionName, Is.EqualTo("System.Action`1[[System.String]]")); Assert.That(ReflectionHelper.ParseReflectionName("System.Action`1[[System.String, mscorlib]]", context).ReflectionName, Is.EqualTo("System.Action`1[[System.String]]")); Assert.That(ReflectionHelper.ParseReflectionName(typeof(int[,][,,]).AssemblyQualifiedName, context).ReflectionName, Is.EqualTo("System.Int32[,,][,]")); Assert.That(ReflectionHelper.ParseReflectionName("System.Environment+SpecialFolder", context).ReflectionName, Is.EqualTo("System.Environment+SpecialFolder")); } [Test] public void ParseOpenGenericReflectionName() { IType converter = ReflectionHelper.ParseReflectionName("System.Converter`2[[`0],[``0]]", new SimpleTypeResolveContext(compilation.MainModule)); Assert.That(converter.ReflectionName, Is.EqualTo("System.Converter`2[[`0],[``0]]")); IMethod convertAll = compilation.FindType(typeof(List<>)).GetMethods(m => m.Name == "ConvertAll").Single(); IType converter2 = ReflectionHelper.ParseReflectionName("System.Converter`2[[`0],[``0]]", new SimpleTypeResolveContext(convertAll)); Assert.That(converter2.ReflectionName, Is.EqualTo("System.Converter`2[[`0],[``0]]")); } [Test] public void ArrayOfTypeParameter() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("`0[,]", context).ReflectionName, Is.EqualTo("`0[,]")); } [Test] public void ParseNullReflectionName() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName(null, context)); } [Test] public void ParseInvalidReflectionName1() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName(string.Empty, context)); } [Test] public void ParseInvalidReflectionName2() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("`", context).ReflectionName, Is.EqualTo("`")); } [Test] public void ParseInvalidReflectionName3() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("``", context).ReflectionName, Is.EqualTo("``")); } [Test] public void ParseInvalidReflectionName4() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("System.Action`A", context).ReflectionName, Is.EqualTo("System.Action`A")); } [Test] public void ParseInvalidReflectionName5() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Environment+", context)); } [Test] public void ParseInvalidReflectionName5b() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("System.Environment+`", context).ReflectionName, Is.EqualTo("System.Environment+`")); } [Test] public void ParseInvalidReflectionName6() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Int32[", context)); } [Test] public void ParseInvalidReflectionName7() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.That(ReflectionHelper.ParseReflectionName("System.Int32[`]", context).ReflectionName, Is.EqualTo("System.Int32")); } [Test] public void ParseInvalidReflectionName8() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Int32[,", context)); } [Test] public void ParseInvalidReflectionName9() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Int32]", context)); } [Test] public void ParseInvalidReflectionName10() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Int32*a", context)); } [Test] public void ParseInvalidReflectionName11() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[]]", context)); } [Test] public void ParseInvalidReflectionName12() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32]a]", context)); } [Test] public void ParseInvalidReflectionName13() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32],]", context)); } [Test] public void ParseInvalidReflectionName14() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32]", context)); } [Test] public void ParseInvalidReflectionName15() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32", context)); } [Test] public void ParseInvalidReflectionName16() { var context = new SimpleTypeResolveContext(compilation.MainModule); Assert.Throws(() => ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32],[System.String", context)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs ================================================ // Copyright (c) 2010-2018 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 // 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.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.TypeSystem { using AttributeArray = ImmutableArray>; [TestFixture] public class TypeSystemLoaderTests { static PEFile LoadAssembly(string filename) { return new PEFile(filename, new FileStream(filename, FileMode.Open, FileAccess.Read)); } static readonly Lazy mscorlib = new Lazy( delegate { return LoadAssembly(Path.Combine(Helpers.Tester.RefAssembliesToolset.GetPath("legacy"), "mscorlib.dll")); }); static readonly Lazy systemCore = new Lazy( delegate { return LoadAssembly(Path.Combine(Helpers.Tester.RefAssembliesToolset.GetPath("legacy"), "System.Core.dll")); }); static readonly Lazy testAssembly = new Lazy( delegate { return LoadAssembly(typeof(SimplePublicClass).Assembly.Location); }); public static PEFile Mscorlib { get { return mscorlib.Value; } } public static PEFile SystemCore { get { return systemCore.Value; } } public static PEFile TestAssembly { get { return testAssembly.Value; } } [OneTimeSetUp] public void FixtureSetUp() { compilation = new SimpleCompilation(TestAssembly, Mscorlib.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.OnlyPublicAPI)); } protected ICompilation compilation; protected ITypeDefinition GetTypeDefinition(Type type) { return compilation.FindType(type).GetDefinition(); } [Test] public void SimplePublicClassTest() { ITypeDefinition c = GetTypeDefinition(typeof(SimplePublicClass)); Assert.That(c.Name, Is.EqualTo(typeof(SimplePublicClass).Name)); Assert.That(c.FullName, Is.EqualTo(typeof(SimplePublicClass).FullName)); Assert.That(c.Namespace, Is.EqualTo(typeof(SimplePublicClass).Namespace)); Assert.That(c.ReflectionName, Is.EqualTo(typeof(SimplePublicClass).FullName)); Assert.That(c.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!c.IsAbstract); Assert.That(!c.IsSealed); Assert.That(!c.IsStatic); Assert.That(!c.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void SimplePublicClassMethodTest() { ITypeDefinition c = GetTypeDefinition(typeof(SimplePublicClass)); IMethod method = c.Methods.Single(m => m.Name == "Method"); Assert.That(method.FullName, Is.EqualTo(typeof(SimplePublicClass).FullName + ".Method")); Assert.That(method.DeclaringType, Is.SameAs(c)); Assert.That(method.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(method.SymbolKind, Is.EqualTo(SymbolKind.Method)); Assert.That(!method.IsVirtual); Assert.That(!method.IsStatic); Assert.That(method.Parameters.Count, Is.EqualTo(0)); Assert.That(method.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(method.HasBody); Assert.That(method.AccessorOwner, Is.Null); Assert.That(!method.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void SimplePublicClassCtorTest() { ITypeDefinition c = GetTypeDefinition(typeof(SimplePublicClass)); IMethod method = c.Methods.Single(m => m.IsConstructor); Assert.That(method.FullName, Is.EqualTo(typeof(SimplePublicClass).FullName + "..ctor")); Assert.That(method.DeclaringType, Is.SameAs(c)); Assert.That(method.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(method.SymbolKind, Is.EqualTo(SymbolKind.Constructor)); Assert.That(!method.IsVirtual); Assert.That(!method.IsStatic); Assert.That(method.Parameters.Count, Is.EqualTo(0)); Assert.That(method.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(method.HasBody); Assert.That(method.AccessorOwner, Is.Null); Assert.That(!method.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void SimplePublicClassDtorTest() { ITypeDefinition c = GetTypeDefinition(typeof(SimplePublicClass)); IMethod method = c.Methods.Single(m => m.IsDestructor); Assert.That(method.FullName, Is.EqualTo(typeof(SimplePublicClass).FullName + ".Finalize")); Assert.That(method.DeclaringType, Is.SameAs(c)); Assert.That(method.Accessibility, Is.EqualTo(Accessibility.Protected)); Assert.That(method.SymbolKind, Is.EqualTo(SymbolKind.Destructor)); Assert.That(!method.IsVirtual); Assert.That(!method.IsStatic); Assert.That(method.Parameters.Count, Is.EqualTo(0)); Assert.That(method.GetAttributes().Count(), Is.EqualTo(1)); Assert.That(method.HasBody); Assert.That(method.AccessorOwner, Is.Null); Assert.That(!method.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void DynamicType() { ITypeDefinition testClass = GetTypeDefinition(typeof(DynamicTest)); Assert.That(testClass.Fields.Single(f => f.Name == "DynamicField").ReturnType, Is.EqualTo(SpecialType.Dynamic)); Assert.That(testClass.Properties.Single().ReturnType, Is.EqualTo(SpecialType.Dynamic)); Assert.That(testClass.Properties.Single().GetAttributes().Count(), Is.EqualTo(0)); } [Test] public void DynamicTypeInGenerics() { ITypeDefinition testClass = GetTypeDefinition(typeof(DynamicTest)); IMethod m1 = testClass.Methods.Single(me => me.Name == "DynamicGenerics1"); Assert.That(m1.ReturnType.ReflectionName, Is.EqualTo("System.Collections.Generic.List`1[[dynamic]]")); Assert.That(m1.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Object],[dynamic[]],[System.Object]]")); IMethod m2 = testClass.Methods.Single(me => me.Name == "DynamicGenerics2"); Assert.That(m2.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Object],[dynamic],[System.Object]]")); IMethod m3 = testClass.Methods.Single(me => me.Name == "DynamicGenerics3"); Assert.That(m3.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Int32],[dynamic],[System.Object]]")); IMethod m4 = testClass.Methods.Single(me => me.Name == "DynamicGenerics4"); Assert.That(m4.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Int32[]],[dynamic],[System.Object]]")); IMethod m5 = testClass.Methods.Single(me => me.Name == "DynamicGenerics5"); Assert.That(m5.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Int32*[]],[dynamic],[System.Object]]")); IMethod m6 = testClass.Methods.Single(me => me.Name == "DynamicGenerics6"); Assert.That(m6.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Object],[dynamic],[System.Object]]&")); IMethod m7 = testClass.Methods.Single(me => me.Name == "DynamicGenerics7"); Assert.That(m7.Parameters[0].Type.ReflectionName, Is.EqualTo("System.Action`3[[System.Int32[][,]],[dynamic],[System.Object]]")); } [Test] public void DynamicParameterHasNoAttributes() { ITypeDefinition testClass = GetTypeDefinition(typeof(DynamicTest)); IMethod m1 = testClass.Methods.Single(me => me.Name == "DynamicGenerics1"); Assert.That(m1.Parameters[0].GetAttributes().Count(), Is.EqualTo(0)); } [Test] public void AssemblyAttribute() { var attributes = compilation.MainModule.GetAssemblyAttributes().ToList(); var typeTest = attributes.Single(a => a.AttributeType.FullName == typeof(TypeTestAttribute).FullName); Assert.That(typeTest.FixedArguments.Length, Is.EqualTo(3)); // first argument is (int)42 Assert.That((int)typeTest.FixedArguments[0].Value, Is.EqualTo(42)); // second argument is typeof(System.Action<>) var ty = (IType)typeTest.FixedArguments[1].Value; Assert.That(ty is ParameterizedType, Is.False); // rt must not be constructed - it's just an unbound type Assert.That(ty.FullName, Is.EqualTo("System.Action")); Assert.That(ty.TypeParameterCount, Is.EqualTo(1)); // third argument is typeof(IDictionary>) var crt = (ParameterizedType)typeTest.FixedArguments[2].Value; Assert.That(crt.FullName, Is.EqualTo("System.Collections.Generic.IDictionary")); Assert.That(crt.TypeArguments[0].FullName, Is.EqualTo("System.String")); // we know the name for TestAttribute, but not necessarily the namespace, as NUnit is not in the compilation Assert.That(crt.TypeArguments[1].FullName, Is.EqualTo("System.Collections.Generic.IList")); var testAttributeType = ((ParameterizedType)crt.TypeArguments[1]).TypeArguments.Single(); Assert.That(testAttributeType.Name, Is.EqualTo("TestAttribute")); Assert.That(testAttributeType.Kind, Is.EqualTo(TypeKind.Unknown)); // (more accurately, we know the namespace and reflection name if the type was loaded by cecil, // but not if we parsed it from C#) } [Test] public void TypeForwardedTo_Attribute() { var attributes = compilation.MainModule.GetAssemblyAttributes().ToList(); var forwardAttribute = attributes.Single(a => a.AttributeType.FullName == typeof(TypeForwardedToAttribute).FullName); Assert.That(forwardAttribute.FixedArguments.Length, Is.EqualTo(1)); var rt = (IType)forwardAttribute.FixedArguments[0].Value; Assert.That(rt.ReflectionName, Is.EqualTo("System.Func`2")); } [Test] public void TestClassTypeParameters() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); Assert.That(testClass.TypeParameters[0].OwnerType, Is.EqualTo(SymbolKind.TypeDefinition)); Assert.That(testClass.TypeParameters[1].OwnerType, Is.EqualTo(SymbolKind.TypeDefinition)); Assert.That(testClass.TypeParameters[0].DirectBaseTypes.First(), Is.SameAs(testClass.TypeParameters[1])); } [Test] public void TestMethod() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); IMethod m = testClass.Methods.Single(me => me.Name == "TestMethod"); Assert.That(m.TypeParameters[0].Name, Is.EqualTo("K")); Assert.That(m.TypeParameters[1].Name, Is.EqualTo("V")); Assert.That(m.TypeParameters[0].OwnerType, Is.EqualTo(SymbolKind.Method)); Assert.That(m.TypeParameters[1].OwnerType, Is.EqualTo(SymbolKind.Method)); Assert.That(m.TypeParameters[0].DirectBaseTypes.First().ReflectionName, Is.EqualTo("System.IComparable`1[[``1]]")); Assert.That(m.TypeParameters[1].DirectBaseTypes.First(), Is.SameAs(m.TypeParameters[0])); } [Test] public void GetIndex() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); IMethod m = testClass.Methods.Single(me => me.Name == "GetIndex"); Assert.That(m.TypeParameters[0].Name, Is.EqualTo("T")); Assert.That(m.TypeParameters[0].OwnerType, Is.EqualTo(SymbolKind.Method)); Assert.That(m.TypeParameters[0].Owner, Is.SameAs(m)); ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First(); Assert.That(constraint.Name, Is.EqualTo("IEquatable")); Assert.That(constraint.TypeParameterCount, Is.EqualTo(1)); Assert.That(constraint.TypeArguments.Count, Is.EqualTo(1)); Assert.That(constraint.TypeArguments[0], Is.SameAs(m.TypeParameters[0])); Assert.That(m.Parameters[0].Type, Is.SameAs(m.TypeParameters[0])); } [Test] public void GetIndexSpecializedTypeParameter() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex"); var m = methodDef.Specialize(new TypeParameterSubstitution( new[] { compilation.FindType(KnownTypeCode.Int16), compilation.FindType(KnownTypeCode.Int32) }, null )); Assert.That(m.TypeParameters[0].Name, Is.EqualTo("T")); Assert.That(m.TypeParameters[0].OwnerType, Is.EqualTo(SymbolKind.Method)); Assert.That(m.TypeParameters[0].Owner, Is.SameAs(m)); ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First(); Assert.That(constraint.Name, Is.EqualTo("IEquatable")); Assert.That(constraint.TypeParameterCount, Is.EqualTo(1)); Assert.That(constraint.TypeArguments.Count, Is.EqualTo(1)); Assert.That(constraint.TypeArguments[0], Is.SameAs(m.TypeParameters[0])); Assert.That(m.Parameters[0].Type, Is.SameAs(m.TypeParameters[0])); } [Test] public void GetIndexDoubleSpecialization() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); // GenericClass.GetIndex var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex"); // GenericClass.GetIndex var m1 = methodDef.Specialize(new TypeParameterSubstitution( new[] { testClass.TypeParameters[1], testClass.TypeParameters[0] }, new[] { testClass.TypeParameters[0] } )); // GenericClass.GetIndex var m2 = m1.Specialize(new TypeParameterSubstitution( new[] { compilation.FindType(KnownTypeCode.Int32), compilation.FindType(KnownTypeCode.String) }, null )); // GenericClass.GetIndex var m12 = methodDef.Specialize(new TypeParameterSubstitution( new[] { compilation.FindType(KnownTypeCode.String), compilation.FindType(KnownTypeCode.Int32) }, new[] { compilation.FindType(KnownTypeCode.Int32) } )); Assert.That(m2, Is.EqualTo(m12)); } [Test] public void SpecializedMethod_AccessorOwner() { // NRefactory bug #143 - Accessor Owner throws null reference exception in some cases now var method = compilation.FindType(typeof(GenericClass)).GetMethods(m => m.Name == "GetIndex").Single(); Assert.That(method.AccessorOwner, Is.Null); } [Test] public void Specialized_GetIndex_ToMemberReference() { var method = compilation.FindType(typeof(GenericClass)).GetMethods(m => m.Name == "GetIndex").Single(); Assert.That(method.Parameters[0].Type, Is.SameAs(method.TypeParameters[0])); Assert.That(method.TypeParameters[0].Owner, Is.SameAs(method)); Assert.That(method, Is.InstanceOf()); //Assert.That(!method.IsParameterized); // the method itself is not specialized Assert.That(method.TypeArguments, Is.EqualTo(method.TypeParameters)); } [Test] public void Specialized_GetIndex_SpecializeWithIdentityHasNoEffect() { var genericClass = compilation.FindType(typeof(GenericClass)); IType[] methodTypeArguments = { DummyTypeParameter.GetMethodTypeParameter(0) }; var method = genericClass.GetMethods(methodTypeArguments, m => m.Name == "GetIndex").Single(); // GenericClass.GetIndex() Assert.That(method.TypeParameters[0].Owner, Is.SameAs(method)); Assert.That(method.TypeArguments[0], Is.Not.EqualTo(method.TypeParameters[0])); Assert.That(((ITypeParameter)method.TypeArguments[0]).Owner, Is.Null); // Now apply identity substitution: var method2 = method.Specialize(TypeParameterSubstitution.Identity); Assert.That(method2.TypeParameters[0].Owner, Is.SameAs(method2)); Assert.That(method2.TypeArguments[0], Is.Not.EqualTo(method2.TypeParameters[0])); Assert.That(((ITypeParameter)method2.TypeArguments[0]).Owner, Is.Null); Assert.That(method2, Is.EqualTo(method)); } [Test] public void GenericEnum() { var testClass = GetTypeDefinition(typeof(GenericClass<,>.NestedEnum)); Assert.That(testClass.TypeParameterCount, Is.EqualTo(2)); } [Test] public void FieldInGenericClassWithNestedEnumType() { var testClass = GetTypeDefinition(typeof(GenericClass<,>)); var enumClass = GetTypeDefinition(typeof(GenericClass<,>.NestedEnum)); var field = testClass.Fields.Single(f => f.Name == "EnumField"); Assert.That(field.ReturnType, Is.EqualTo(new ParameterizedType(enumClass, testClass.TypeParameters))); } [Test] public void GenericEnumMemberReturnType() { var enumClass = GetTypeDefinition(typeof(GenericClass<,>.NestedEnum)); var field = enumClass.Fields.Single(f => f.Name == "EnumMember"); Assert.That(field.ReturnType, Is.EqualTo(new ParameterizedType(enumClass, enumClass.TypeParameters))); } [Test] public void PropertyWithProtectedSetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.Name == "PropertyWithProtectedSetter"); Assert.That(p.CanGet); Assert.That(p.CanSet); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Protected)); } [Test] public void PropertyWithPrivateSetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.Name == "PropertyWithPrivateSetter"); Assert.That(p.CanGet); Assert.That(p.CanSet); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Private)); Assert.That(p.Getter.HasBody); Assert.That(!p.HasAttribute(KnownAttribute.SpecialName)); Assert.That(!p.Getter.HasAttribute(KnownAttribute.SpecialName)); Assert.That(!p.Setter.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void PropertyWithPrivateGetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.Name == "PropertyWithPrivateGetter"); Assert.That(p.CanGet); Assert.That(p.CanSet); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Private)); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.HasBody); } [Test] public void PropertyWithoutSetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.Name == "PropertyWithoutSetter"); Assert.That(p.CanGet); Assert.That(!p.CanSet); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter, Is.Null); } [Test] public void Indexer() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.IsIndexer); Assert.That(p.Name, Is.EqualTo("Item")); Assert.That(p.Parameters.Select(x => x.Name).ToArray(), Is.EqualTo(new[] { "index" })); } [Test] public void IndexerGetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.IsIndexer); Assert.That(p.CanGet); Assert.That(p.Getter.SymbolKind, Is.EqualTo(SymbolKind.Accessor)); Assert.That(p.Getter.Name, Is.EqualTo("get_Item")); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.Parameters.Select(x => x.Name).ToArray(), Is.EqualTo(new[] { "index" })); Assert.That(p.Getter.ReturnType.ReflectionName, Is.EqualTo("System.String")); Assert.That(p.Getter.AccessorOwner, Is.EqualTo(p)); } [Test] public void IndexerSetter() { var testClass = GetTypeDefinition(typeof(PropertyTest)); IProperty p = testClass.Properties.Single(pr => pr.IsIndexer); Assert.That(p.CanSet); Assert.That(p.Setter.SymbolKind, Is.EqualTo(SymbolKind.Accessor)); Assert.That(p.Setter.Name, Is.EqualTo("set_Item")); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter.Parameters.Select(x => x.Name).ToArray(), Is.EqualTo(new[] { "index", "value" })); Assert.That(p.Setter.ReturnType.Kind, Is.EqualTo(TypeKind.Void)); } [Test] public void GenericPropertyGetter() { var type = compilation.FindType(typeof(GenericClass)); var prop = type.GetProperties(p => p.Name == "Property").Single(); Assert.That(prop.Getter.ReturnType.ReflectionName, Is.EqualTo("System.String")); Assert.That(prop.Getter.IsAccessor); Assert.That(prop.Getter.AccessorOwner, Is.EqualTo(prop)); } [Test] public void EnumTest() { var e = GetTypeDefinition(typeof(MyEnum)); Assert.That(e.Kind, Is.EqualTo(TypeKind.Enum)); Assert.That(e.IsReferenceType, Is.EqualTo(false)); Assert.That(e.EnumUnderlyingType.ReflectionName, Is.EqualTo("System.Int16")); Assert.That(e.DirectBaseTypes.Select(t => t.ReflectionName).ToArray(), Is.EqualTo(new[] { "System.Enum" })); } [Test] public void EnumFieldsTest() { var e = GetTypeDefinition(typeof(MyEnum)); IField valueField = e.Fields.First(); IField[] fields = e.Fields.Skip(1).ToArray(); Assert.That(fields.Length, Is.EqualTo(5)); Assert.That(valueField.Name, Is.EqualTo("value__")); Assert.That(valueField.Type, Is.EqualTo(GetTypeDefinition(typeof(short)))); Assert.That(valueField.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(valueField.GetConstantValue(), Is.EqualTo(null)); Assert.That(!valueField.IsConst); Assert.That(!valueField.IsStatic); foreach (IField f in fields) { Assert.That(f.IsStatic); Assert.That(f.IsConst); Assert.That(f.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(f.Type, Is.SameAs(e)); Assert.That(f.GetConstantValue().GetType(), Is.EqualTo(typeof(short))); } Assert.That(fields[0].Name, Is.EqualTo("First")); Assert.That(fields[0].GetConstantValue(), Is.EqualTo(0)); Assert.That(fields[1].Name, Is.EqualTo("Second")); Assert.That(fields[1].Type, Is.SameAs(e)); Assert.That(fields[1].GetConstantValue(), Is.EqualTo(1)); Assert.That(fields[2].Name, Is.EqualTo("Flag1")); Assert.That(fields[2].GetConstantValue(), Is.EqualTo(0x10)); Assert.That(fields[3].Name, Is.EqualTo("Flag2")); Assert.That(fields[3].GetConstantValue(), Is.EqualTo(0x20)); Assert.That(fields[4].Name, Is.EqualTo("CombinedFlags")); Assert.That(fields[4].GetConstantValue(), Is.EqualTo(0x30)); } [Test] public void GetNestedTypesFromBaseClassTest() { ITypeDefinition d = GetTypeDefinition(typeof(Derived<,>)); IType pBase = d.DirectBaseTypes.Single(); Assert.That(pBase.ReflectionName, Is.EqualTo(typeof(Base<>).FullName + "[[`1]]")); // Base[`1].GetNestedTypes() = { Base`1+Nested`1[`1, unbound] } Assert.That(pBase.GetNestedTypes().Select(n => n.ReflectionName).ToArray(), Is.EqualTo(new[] { typeof(Base<>.Nested<>).FullName + "[[`1],[]]" })); // Derived.GetNestedTypes() = { Base`1+Nested`1[`1, unbound] } Assert.That(d.GetNestedTypes().Select(n => n.ReflectionName).ToArray(), Is.EqualTo(new[] { typeof(Base<>.Nested<>).FullName + "[[`1],[]]" })); // This is 'leaking' the type parameter from B as is usual when retrieving any members from an unbound type. } [Test] public void ParameterizedTypeGetNestedTypesFromBaseClassTest() { // Derived[string,int].GetNestedTypes() = { Base`1+Nested`1[int, unbound] } var d = compilation.FindType(typeof(Derived)); Assert.That(d.GetNestedTypes().Select(n => n.ReflectionName).ToArray(), Is.EqualTo(new[] { typeof(Base<>.Nested<>).FullName + "[[System.Int32],[]]" })); } [Test] public void ConstraintsOnOverrideAreInherited() { ITypeDefinition d = GetTypeDefinition(typeof(Derived<,>)); ITypeParameter tp = d.Methods.Single(m => m.Name == "GenericMethodWithConstraints").TypeParameters.Single(); Assert.That(tp.Name, Is.EqualTo("Y")); Assert.That(!tp.HasValueTypeConstraint); Assert.That(!tp.HasReferenceTypeConstraint); Assert.That(tp.HasDefaultConstructorConstraint); Assert.That(tp.DirectBaseTypes.Select(t => t.ReflectionName).ToArray(), Is.EqualTo(new string[] { "System.Collections.Generic.IComparer`1[[`1]]", "System.Object" })); } [Test] public void DtorInDerivedClass() { ITypeDefinition c = GetTypeDefinition(typeof(Derived<,>)); IMethod method = c.Methods.Single(m => m.IsDestructor); Assert.That(method.FullName, Is.EqualTo(c.FullName + ".Finalize")); Assert.That(method.DeclaringType, Is.SameAs(c)); Assert.That(method.Accessibility, Is.EqualTo(Accessibility.Protected)); Assert.That(method.SymbolKind, Is.EqualTo(SymbolKind.Destructor)); Assert.That(!method.IsVirtual); Assert.That(!method.IsStatic); Assert.That(method.Parameters.Count, Is.EqualTo(0)); Assert.That(method.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(method.HasBody); Assert.That(method.AccessorOwner, Is.Null); } [Test] public void PrivateFinalizeMethodIsNotADtor() { ITypeDefinition c = GetTypeDefinition(typeof(TypeTestAttribute)); IMethod method = c.Methods.Single(m => m.Name == "Finalize"); Assert.That(method.FullName, Is.EqualTo(c.FullName + ".Finalize")); Assert.That(method.DeclaringType, Is.SameAs(c)); Assert.That(method.Accessibility, Is.EqualTo(Accessibility.Private)); Assert.That(method.SymbolKind, Is.EqualTo(SymbolKind.Method)); Assert.That(!method.IsVirtual); Assert.That(!method.IsStatic); Assert.That(method.Parameters.Count, Is.EqualTo(0)); Assert.That(method.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(method.HasBody); Assert.That(method.AccessorOwner, Is.Null); } [Test] public void DefaultConstructorAddedToStruct() { var ctors = compilation.FindType(typeof(MyStructWithCtor)).GetConstructors(); Assert.That(ctors.Count(), Is.EqualTo(2)); Assert.That(!ctors.Any(c => c.IsStatic)); Assert.That(ctors.All(c => c.ReturnType.Kind == TypeKind.Void)); Assert.That(ctors.All(c => c.Accessibility == Accessibility.Public)); } [Test] public void NoDefaultConstructorAddedToStruct() { var ctors = compilation.FindType(typeof(MyStructWithDefaultCtor)).GetConstructors(); Assert.That(ctors.Count(), Is.EqualTo(1)); Assert.That(!ctors.Any(c => c.IsStatic)); Assert.That(ctors.All(c => c.ReturnType.Kind == TypeKind.Void)); Assert.That(ctors.All(c => c.Accessibility == Accessibility.Public)); } [Test] public void NoDefaultConstructorAddedToClass() { var ctors = compilation.FindType(typeof(MyClassWithCtor)).GetConstructors(); Assert.That(ctors.Single().Accessibility, Is.EqualTo(Accessibility.Private)); Assert.That(ctors.Single().Parameters.Count, Is.EqualTo(1)); } [Test] public void DefaultConstructorOnAbstractClassIsProtected() { var ctors = compilation.FindType(typeof(AbstractClass)).GetConstructors(); Assert.That(ctors.Single().Parameters.Count, Is.EqualTo(0)); Assert.That(ctors.Single().Accessibility, Is.EqualTo(Accessibility.Protected)); } [Test] public void SerializableAttribute() { IAttribute attr = GetTypeDefinition(typeof(NonCustomAttributes)).GetAttributes().Single(); Assert.That(attr.AttributeType.FullName, Is.EqualTo("System.SerializableAttribute")); } [Test] public void NonSerializedAttribute() { IField field = GetTypeDefinition(typeof(NonCustomAttributes)).Fields.Single(f => f.Name == "NonSerializedField"); Assert.That(field.GetAttributes().Single().AttributeType.FullName, Is.EqualTo("System.NonSerializedAttribute")); } [Test] public void ExplicitStructLayoutAttribute() { IAttribute attr = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).GetAttributes().Single(); Assert.That(attr.AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.StructLayoutAttribute")); var arg1 = attr.FixedArguments.Single(); Assert.That(arg1.Type.FullName, Is.EqualTo("System.Runtime.InteropServices.LayoutKind")); Assert.That(arg1.Value, Is.EqualTo((int)LayoutKind.Explicit)); var arg2 = attr.NamedArguments[0]; Assert.That(arg2.Name, Is.EqualTo("CharSet")); Assert.That(arg2.Type.FullName, Is.EqualTo("System.Runtime.InteropServices.CharSet")); Assert.That(arg2.Value, Is.EqualTo((int)CharSet.Unicode)); var arg3 = attr.NamedArguments[1]; Assert.That(arg3.Name, Is.EqualTo("Pack")); Assert.That(arg3.Type.FullName, Is.EqualTo("System.Int32")); Assert.That(arg3.Value, Is.EqualTo(8)); } [Test] public void FieldOffsetAttribute() { IField field = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0"); Assert.That(field.GetAttributes().Single().AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.FieldOffsetAttribute")); var arg = field.GetAttributes().Single().FixedArguments.Single(); Assert.That(arg.Type.FullName, Is.EqualTo("System.Int32")); Assert.That(arg.Value, Is.EqualTo(0)); field = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field100"); Assert.That(field.GetAttributes().Single().AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.FieldOffsetAttribute")); arg = field.GetAttributes().Single().FixedArguments.Single(); Assert.That(arg.Type.FullName, Is.EqualTo("System.Int32")); Assert.That(arg.Value, Is.EqualTo(100)); } [Test] public void DllImportAttribute() { IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); IAttribute dllImport = method.GetAttributes().Single(); Assert.That(dllImport.AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.DllImportAttribute")); Assert.That(dllImport.FixedArguments[0].Value, Is.EqualTo("unmanaged.dll")); Assert.That(dllImport.NamedArguments.Single().Value, Is.EqualTo((int)CharSet.Unicode)); } [Test] public void DllImportAttributeWithPreserveSigFalse() { IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DoNotPreserveSig"); IAttribute dllImport = method.GetAttributes().Single(); Assert.That(dllImport.AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.DllImportAttribute")); Assert.That(dllImport.FixedArguments[0].Value, Is.EqualTo("unmanaged.dll")); Assert.That(dllImport.NamedArguments.Single().Value, Is.EqualTo(false)); } [Test] public void PreserveSigAttribute() { IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "PreserveSigAsAttribute"); IAttribute preserveSig = method.GetAttributes().Single(); Assert.That(preserveSig.AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.PreserveSigAttribute")); Assert.That(preserveSig.FixedArguments.Length == 0); Assert.That(preserveSig.NamedArguments.Length == 0); } [Test] public void InOutParametersOnRefMethod() { IParameter p = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single(); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.Ref)); var attr = p.GetAttributes().ToList(); Assert.That(attr.Count, Is.EqualTo(2)); Assert.That(attr[0].AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.InAttribute")); Assert.That(attr[1].AttributeType.FullName, Is.EqualTo("System.Runtime.InteropServices.OutAttribute")); } [Test] public void MarshalAsAttributeOnMethod() { IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); IAttribute marshalAs = method.GetReturnTypeAttributes().Single(); Assert.That(marshalAs.FixedArguments.Single().Value, Is.EqualTo((int)UnmanagedType.Bool)); } [Test] public void MethodWithOutParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.Out)); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.Type.Kind == TypeKind.ByReference); } [Test] public void MethodWithRefParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithRefParameter").Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.Ref)); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.Type.Kind == TypeKind.ByReference); } [Test] public void MethodWithInParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithInParameter").Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.In)); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.Type.Kind == TypeKind.ByReference); } [Test] public void MethodWithParamsArray() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(p.IsParams); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.Type.Kind == TypeKind.Array); } [Test] public void MethodWithOptionalParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.GetConstantValue(), Is.EqualTo(4)); } [Test] public void MethodWithExplicitOptionalParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithExplicitOptionalParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(!p.HasConstantValueInSignature); // explicit optional parameter appears in type system if it's read from C#, but not when read from IL //Assert.AreEqual(1, p.GetAttributes().Count()); } [Test] public void MethodWithEnumOptionalParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithEnumOptionalParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.GetConstantValue(), Is.EqualTo((int)StringComparison.OrdinalIgnoreCase)); } [Test] public void MethodWithOptionalNullableParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(p.GetConstantValue(), Is.Null); } [Test] public void MethodWithOptionalLongParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalLongParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetConstantValue(), Is.EqualTo(1L)); Assert.That(p.GetConstantValue().GetType(), Is.EqualTo(typeof(long))); } [Test] public void MethodWithOptionalNullableLongParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableLongParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetConstantValue(), Is.EqualTo(1L)); Assert.That(p.GetConstantValue().GetType(), Is.EqualTo(typeof(long))); } [Test] public void MethodWithOptionalDecimalParameter() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalDecimalParameter").Parameters.Single(); Assert.That(p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.HasConstantValueInSignature); Assert.That(p.GetConstantValue(), Is.EqualTo(1M)); Assert.That(p.GetConstantValue().GetType(), Is.EqualTo(typeof(decimal))); } [Test] public void VarArgsMethod() { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "VarArgsMethod").Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.Type.Kind, Is.EqualTo(TypeKind.ArgList)); Assert.That(p.Name, Is.EqualTo("")); } [Test] public void VarArgsCtor() { IParameter p = GetTypeDefinition(typeof(VarArgsCtor)).Methods.Single(m => m.IsConstructor).Parameters.Single(); Assert.That(!p.IsOptional); Assert.That(p.ReferenceKind, Is.EqualTo(ReferenceKind.None)); Assert.That(!p.IsParams); Assert.That(p.Type.Kind, Is.EqualTo(TypeKind.ArgList)); Assert.That(p.Name, Is.EqualTo("")); } [Test] public void GenericDelegate_Variance() { ITypeDefinition type = GetTypeDefinition(typeof(GenericDelegate<,>)); Assert.That(type.TypeParameters[0].Variance, Is.EqualTo(VarianceModifier.Contravariant)); Assert.That(type.TypeParameters[1].Variance, Is.EqualTo(VarianceModifier.Covariant)); Assert.That(type.TypeParameters[0].DirectBaseTypes.FirstOrDefault(), Is.SameAs(type.TypeParameters[1])); } [Test] public void GenericDelegate_ReferenceTypeConstraints() { ITypeDefinition type = GetTypeDefinition(typeof(GenericDelegate<,>)); Assert.That(!type.TypeParameters[0].HasReferenceTypeConstraint); Assert.That(type.TypeParameters[1].HasReferenceTypeConstraint); Assert.That(type.TypeParameters[0].IsReferenceType, Is.Null); Assert.That(type.TypeParameters[1].IsReferenceType, Is.EqualTo(true)); } [Test] public void GenericDelegate_GetInvokeMethod() { IType type = compilation.FindType(typeof(GenericDelegate)); IMethod m = type.GetDelegateInvokeMethod(); Assert.That(m.Name, Is.EqualTo("Invoke")); Assert.That(m.ReturnType.FullName, Is.EqualTo("System.Object")); Assert.That(m.Parameters[0].Type.FullName, Is.EqualTo("System.String")); } [Test] public void ComInterfaceTest() { ITypeDefinition type = GetTypeDefinition(typeof(IAssemblyEnum)); // [ComImport] Assert.That(type.GetAttributes().Count(a => a.AttributeType.FullName == typeof(ComImportAttribute).FullName), Is.EqualTo(1)); IMethod m = type.Methods.Single(); Assert.That(m.Name, Is.EqualTo("GetNextAssembly")); Assert.That(m.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(m.IsAbstract); Assert.That(!m.IsVirtual); Assert.That(!m.IsSealed); } [Test] public void InnerClassInGenericClassIsReferencedUsingParameterizedType() { ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>)); IField field1 = type.Fields.Single(f => f.Name == "Field1"); IField field2 = type.Fields.Single(f => f.Name == "Field2"); IField field3 = type.Fields.Single(f => f.Name == "Field3"); // types must be self-parameterized Assert.That(field1.Type.ReflectionName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1+Inner[[`0]]")); Assert.That(field2.Type.ReflectionName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1+Inner[[`0]]")); Assert.That(field3.Type.ReflectionName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1+Inner[[ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1+Inner[[`0]]]]")); } [Test] public void FlagsOnInterfaceMembersAreCorrect() { ITypeDefinition type = GetTypeDefinition(typeof(IInterfaceWithProperty)); var p = type.Properties.Single(); Assert.That(p.IsIndexer, Is.EqualTo(false)); Assert.That(p.IsAbstract, Is.EqualTo(true)); Assert.That(p.IsOverridable, Is.EqualTo(true)); Assert.That(p.IsOverride, Is.EqualTo(false)); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.IsAbstract, Is.EqualTo(true)); Assert.That(p.Getter.IsOverridable, Is.EqualTo(true)); Assert.That(p.Getter.IsOverride, Is.EqualTo(false)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.HasBody, Is.EqualTo(false)); Assert.That(p.Setter.IsAbstract, Is.EqualTo(true)); Assert.That(p.Setter.IsOverridable, Is.EqualTo(true)); Assert.That(p.Setter.IsOverride, Is.EqualTo(false)); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter.HasBody, Is.EqualTo(false)); type = GetTypeDefinition(typeof(IInterfaceWithIndexers)); p = type.Properties.Single(x => x.Parameters.Count == 2); Assert.That(p.IsIndexer, Is.EqualTo(true)); Assert.That(p.IsAbstract, Is.EqualTo(true)); Assert.That(p.IsOverridable, Is.EqualTo(true)); Assert.That(p.IsOverride, Is.EqualTo(false)); Assert.That(p.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Getter.IsAbstract, Is.EqualTo(true)); Assert.That(p.Getter.IsOverridable, Is.EqualTo(true)); Assert.That(p.Getter.IsOverride, Is.EqualTo(false)); Assert.That(p.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(p.Setter.IsAbstract, Is.EqualTo(true)); Assert.That(p.Setter.IsOverridable, Is.EqualTo(true)); Assert.That(p.Setter.IsOverride, Is.EqualTo(false)); Assert.That(p.Setter.Accessibility, Is.EqualTo(Accessibility.Public)); type = GetTypeDefinition(typeof(IHasEvent)); var e = type.Events.Single(); Assert.That(e.IsAbstract, Is.EqualTo(true)); Assert.That(e.IsOverridable, Is.EqualTo(true)); Assert.That(e.IsOverride, Is.EqualTo(false)); Assert.That(e.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(e.AddAccessor.IsAbstract, Is.EqualTo(true)); Assert.That(e.AddAccessor.IsOverridable, Is.EqualTo(true)); Assert.That(e.AddAccessor.IsOverride, Is.EqualTo(false)); Assert.That(e.AddAccessor.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(e.RemoveAccessor.IsAbstract, Is.EqualTo(true)); Assert.That(e.RemoveAccessor.IsOverridable, Is.EqualTo(true)); Assert.That(e.RemoveAccessor.IsOverride, Is.EqualTo(false)); Assert.That(e.RemoveAccessor.Accessibility, Is.EqualTo(Accessibility.Public)); type = GetTypeDefinition(typeof(IDisposable)); var m = type.Methods.Single(); Assert.That(m.IsAbstract, Is.EqualTo(true)); Assert.That(m.IsOverridable, Is.EqualTo(true)); Assert.That(m.IsOverride, Is.EqualTo(false)); Assert.That(m.Accessibility, Is.EqualTo(Accessibility.Public)); } [Test] public void InnerClassInGenericClass_TypeParameterOwner() { ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner)); Assert.That(type.TypeParameters[0], Is.SameAs(type.DeclaringTypeDefinition.TypeParameters[0])); Assert.That(type.TypeParameters[0].Owner, Is.SameAs(type.DeclaringTypeDefinition)); } [Test] public void InnerClassInGenericClass_ReferencesTheOuterClass_Field() { ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner)); IField f = type.Fields.Single(); Assert.That(f.Type.ReflectionName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1[[`0]]")); } [Test] public void InnerClassInGenericClass_ReferencesTheOuterClass_Parameter() { ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner)); IParameter p = type.Methods.Single(m => m.IsConstructor).Parameters.Single(); Assert.That(p.Type.ReflectionName, Is.EqualTo("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1[[`0]]")); } CustomAttributeTypedArgument GetParamsAttributeArgument(int index) { ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)); var arr = (AttributeArray)type.GetAttributes().Single().FixedArguments.Single().Value; Assert.That(arr.Length, Is.EqualTo(5)); return arr[index]; } [Test] public void ParamsAttribute_Integer() { var arg = GetParamsAttributeArgument(0); Assert.That(arg.Type.FullName, Is.EqualTo("System.Int32")); Assert.That(arg.Value, Is.EqualTo(1)); } [Test] public void ParamsAttribute_Enum() { var arg = GetParamsAttributeArgument(1); Assert.That(arg.Type.FullName, Is.EqualTo("System.StringComparison")); Assert.That(arg.Value, Is.EqualTo((int)StringComparison.CurrentCulture)); } [Test] public void ParamsAttribute_NullReference() { var arg = GetParamsAttributeArgument(2); //Assert.AreEqual("System.Object", arg.Type.FullName); Assert.That(arg.Value, Is.Null); } [Test] public void ParamsAttribute_Double() { var arg = GetParamsAttributeArgument(3); Assert.That(arg.Type.FullName, Is.EqualTo("System.Double")); Assert.That(arg.Value, Is.EqualTo(4.0)); } [Test] public void ParamsAttribute_String() { var arg = GetParamsAttributeArgument(4); Assert.That(arg.Type.FullName, Is.EqualTo("System.String")); Assert.That(arg.Value, Is.EqualTo("Test")); } [Test] public void ParamsAttribute_Property() { ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)); IProperty prop = type.Properties.Single(p => p.Name == "Property"); var attr = prop.GetAttributes().Single(); Assert.That(attr.AttributeType, Is.EqualTo(type)); var elements = (AttributeArray)attr.FixedArguments.Single().Value; Assert.That(elements.Length, Is.EqualTo(0)); var namedArg = attr.NamedArguments.Single(); Assert.That(namedArg.Name, Is.EqualTo(prop.Name)); var arrayElements = (AttributeArray)namedArg.Value; Assert.That(arrayElements.Length, Is.EqualTo(2)); } [Test] public void ParamsAttribute_Getter_ReturnType() { ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)); IProperty prop = type.Properties.Single(p => p.Name == "Property"); Assert.That(prop.Getter.GetAttributes().Count(), Is.EqualTo(0)); Assert.That(prop.Getter.GetReturnTypeAttributes().Count(), Is.EqualTo(1)); } [Test] public void DoubleAttribute_ImplicitNumericConversion() { ITypeDefinition type = GetTypeDefinition(typeof(DoubleAttribute)); var arg = type.GetAttributes().Single().FixedArguments.Single(); Assert.That(arg.Type.ReflectionName, Is.EqualTo("System.Double")); Assert.That(arg.Value, Is.EqualTo(1.0)); } /* TS no longer provides implicitly implemented interface members. [Test] public void ImplicitImplementationOfUnifiedMethods() { ITypeDefinition type = GetTypeDefinition(typeof(ImplementationOfUnifiedMethods)); IMethod test = type.Methods.Single(m => m.Name == "Test"); Assert.AreEqual(2, test.ImplementedInterfaceMembers.Count); Assert.AreEqual("Int32", ((IMethod)test.ImplementedInterfaceMembers[0]).Parameters.Single().Type.Name); Assert.AreEqual("Int32", ((IMethod)test.ImplementedInterfaceMembers[1]).Parameters.Single().Type.Name); Assert.AreEqual("T", ((IMethod)test.ImplementedInterfaceMembers[0].MemberDefinition).Parameters.Single().Type.Name); Assert.AreEqual("S", ((IMethod)test.ImplementedInterfaceMembers[1].MemberDefinition).Parameters.Single().Type.Name); } */ [Test] public void StaticityOfEventAccessors() { // https://github.com/icsharpcode/NRefactory/issues/20 ITypeDefinition type = GetTypeDefinition(typeof(ClassWithStaticAndNonStaticMembers)); var evt1 = type.Events.Single(e => e.Name == "Event1"); Assert.That(evt1.IsStatic); Assert.That(evt1.AddAccessor.IsStatic); Assert.That(evt1.RemoveAccessor.IsStatic); var evt2 = type.Events.Single(e => e.Name == "Event2"); Assert.That(!evt2.IsStatic); Assert.That(!evt2.AddAccessor.IsStatic); Assert.That(!evt2.RemoveAccessor.IsStatic); var evt3 = type.Events.Single(e => e.Name == "Event3"); Assert.That(evt3.IsStatic); Assert.That(evt3.AddAccessor.IsStatic); Assert.That(evt3.RemoveAccessor.IsStatic); var evt4 = type.Events.Single(e => e.Name == "Event4"); Assert.That(!evt4.IsStatic); Assert.That(!evt4.AddAccessor.IsStatic); Assert.That(!evt4.RemoveAccessor.IsStatic); } [Test] public void StaticityOfPropertyAccessors() { // https://github.com/icsharpcode/NRefactory/issues/20 ITypeDefinition type = GetTypeDefinition(typeof(ClassWithStaticAndNonStaticMembers)); var prop1 = type.Properties.Single(e => e.Name == "Prop1"); Assert.That(prop1.IsStatic); Assert.That(prop1.Getter.IsStatic); Assert.That(prop1.Setter.IsStatic); var prop2 = type.Properties.Single(e => e.Name == "Prop2"); Assert.That(!prop2.IsStatic); Assert.That(!prop2.Getter.IsStatic); Assert.That(!prop2.Setter.IsStatic); var prop3 = type.Properties.Single(e => e.Name == "Prop3"); Assert.That(prop3.IsStatic); Assert.That(prop3.Getter.IsStatic); Assert.That(prop3.Setter.IsStatic); var prop4 = type.Properties.Single(e => e.Name == "Prop4"); Assert.That(!prop4.IsStatic); Assert.That(!prop4.Getter.IsStatic); Assert.That(!prop4.Setter.IsStatic); } [Test] public void PropertyAccessorsHaveBody() { ITypeDefinition type = GetTypeDefinition(typeof(ClassWithStaticAndNonStaticMembers)); foreach (var prop in type.Properties) { Assert.That(prop.Getter.HasBody, prop.Getter.Name); Assert.That(prop.Setter.HasBody, prop.Setter.Name); } } [Test] public void EventAccessorNames() { ITypeDefinition type = GetTypeDefinition(typeof(ClassWithStaticAndNonStaticMembers)); var customEvent = type.Events.Single(e => e.Name == "Event1"); Assert.That(customEvent.AddAccessor.Name, Is.EqualTo("add_Event1")); Assert.That(customEvent.RemoveAccessor.Name, Is.EqualTo("remove_Event1")); var normalEvent = type.Events.Single(e => e.Name == "Event3"); Assert.That(normalEvent.AddAccessor.Name, Is.EqualTo("add_Event3")); Assert.That(normalEvent.RemoveAccessor.Name, Is.EqualTo("remove_Event3")); } [Test] public void EventAccessorHaveBody() { ITypeDefinition type = GetTypeDefinition(typeof(ClassWithStaticAndNonStaticMembers)); foreach (var ev in type.Events) { Assert.That(ev.AddAccessor.HasBody, ev.AddAccessor.Name); Assert.That(ev.RemoveAccessor.HasBody, ev.RemoveAccessor.Name); } } [Test] public void InterfacePropertyAccessorsShouldNotBeOverrides() { ITypeDefinition type = GetTypeDefinition(typeof(IInterfaceWithProperty)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(prop.Getter.IsOverride, Is.False); Assert.That(prop.Getter.IsOverridable, Is.True); Assert.That(prop.Setter.IsOverride, Is.False); Assert.That(prop.Setter.IsOverridable, Is.True); } [Test] public void InterfaceShouldDeriveFromObject() { ITypeDefinition type = GetTypeDefinition(typeof(IInterfaceWithProperty)); Assert.That(type.DirectBaseTypes.Count() == 1, "Should have exactly one direct base type"); Assert.That(type.DirectBaseTypes.First().IsKnownType(KnownTypeCode.Object), "Base type should be object"); } [Test] public void InterfaceShouldDeriveFromObject2() { ITypeDefinition type = GetTypeDefinition(typeof(IShadowTestDerived)); Assert.That(type.DirectBaseTypes.Count() == 2, "Should have exactly two direct base types"); Assert.That(type.DirectBaseTypes.Skip(1).First() == GetTypeDefinition(typeof(IShadowTestBase)), "Base type should be IShadowTestBase"); Assert.That(type.DirectBaseTypes.First().IsKnownType(KnownTypeCode.Object), "Base type should be object"); } [Test] public void CheckInterfaceDirectBaseTypes() { ITypeDefinition type = GetTypeDefinition(typeof(IDerived)); Assert.That(type.DirectBaseTypes.Count() == 3, "Should have exactly three direct base types"); Assert.That(type.DirectBaseTypes.First().IsKnownType(KnownTypeCode.Object), "Base type should be object"); Assert.That(type.DirectBaseTypes.Skip(1).First() == GetTypeDefinition(typeof(IBase1)), "Base type should be IBase1"); Assert.That(type.DirectBaseTypes.Skip(2).First() == GetTypeDefinition(typeof(IBase2)), "Base type should be IBase2"); } [Test] public void VirtualPropertyAccessorsShouldNotBeOverrides() { ITypeDefinition type = GetTypeDefinition(typeof(ClassWithVirtualProperty)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(prop.Getter.IsOverride, Is.False); Assert.That(prop.Getter.IsOverridable, Is.True); Assert.That(prop.Setter.IsOverride, Is.False); Assert.That(prop.Setter.IsOverridable, Is.True); } [Test] public void ClassThatOverridesGetterOnly() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatOverridesGetterOnly)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(prop.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(prop.Getter.Accessibility, Is.EqualTo(Accessibility.Public)); } [Test] public void ClassThatOverridesSetterOnly() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatOverridesSetterOnly)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(prop.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(prop.Setter.Accessibility, Is.EqualTo(Accessibility.Protected)); } /* TS no longer provides implicit interface impls [Test] public void PropertyAccessorsShouldBeReportedAsImplementingInterfaceAccessors() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsProperty)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(prop.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.Prop" })); Assert.That(prop.Getter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.get_Prop" })); Assert.That(prop.Setter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.set_Prop" })); } */ [Test] public void PropertyThatImplementsInterfaceIsNotVirtual() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsProperty)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(!prop.IsVirtual); Assert.That(!prop.IsOverridable); Assert.That(!prop.IsSealed); } [Test] public void Property_SealedOverride() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatOverridesAndSealsVirtualProperty)); var prop = type.Properties.Single(p => p.Name == "Prop"); Assert.That(!prop.IsVirtual); Assert.That(prop.IsOverride); Assert.That(prop.IsSealed); Assert.That(!prop.IsOverridable); } /* The TS no longer provides implicit interface impls. [Test] public void IndexerAccessorsShouldBeReportedAsImplementingTheCorrectInterfaceAccessors() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsIndexers)); var ix1 = type.Properties.Single(p => p.Parameters.Count == 1 && p.Parameters[0].Type.GetDefinition().KnownTypeCode == KnownTypeCode.Int32); var ix2 = type.Properties.Single(p => p.Parameters.Count == 1 && p.Parameters[0].Type.GetDefinition().KnownTypeCode == KnownTypeCode.String); var ix3 = type.Properties.Single(p => p.Parameters.Count == 2); Assert.That(ix1.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EquivalentTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.Item", "ICSharpCode.Decompiler.Tests.TypeSystem.IGenericInterfaceWithIndexer`1.Item" })); Assert.That(ix1.ImplementedInterfaceMembers.All(p => ((IProperty)p).Parameters.Select(x => x.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32 }))); Assert.That(ix1.Getter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EquivalentTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.get_Item", "ICSharpCode.Decompiler.Tests.TypeSystem.IGenericInterfaceWithIndexer`1.get_Item" })); Assert.That(ix1.Getter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32 }))); Assert.That(ix1.Setter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EquivalentTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.set_Item", "ICSharpCode.Decompiler.Tests.TypeSystem.IGenericInterfaceWithIndexer`1.set_Item" })); Assert.That(ix1.Setter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32, KnownTypeCode.Int32 }))); Assert.That(ix2.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.Item" })); Assert.That(ix2.ImplementedInterfaceMembers.All(p => ((IProperty)p).Parameters.Select(x => x.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.String }))); Assert.That(ix2.Getter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.get_Item" })); Assert.That(ix2.Getter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.String }))); Assert.That(ix2.Setter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.set_Item" })); Assert.That(ix2.Setter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.String, KnownTypeCode.Int32 }))); Assert.That(ix3.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.Item" })); Assert.That(ix3.ImplementedInterfaceMembers.All(p => ((IProperty)p).Parameters.Select(x => x.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32, KnownTypeCode.Int32 }))); Assert.That(ix3.Getter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.get_Item" })); Assert.That(ix3.Getter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32, KnownTypeCode.Int32 }))); Assert.That(ix3.Setter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithIndexers.set_Item" })); Assert.That(ix3.Setter.ImplementedInterfaceMembers.All(m => ((IMethod)m).Parameters.Select(p => p.Type.GetDefinition().KnownTypeCode).SequenceEqual(new[] { KnownTypeCode.Int32, KnownTypeCode.Int32, KnownTypeCode.Int32 }))); } */ [Test] public void ExplicitIndexerImplementationReturnsTheCorrectMembers() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsIndexersExplicitly)); Assert.That(type.Properties.All(p => p.SymbolKind == SymbolKind.Indexer)); Assert.That(type.Properties.All(p => p.ExplicitlyImplementedInterfaceMembers.Count() == 1)); Assert.That(type.Properties.All(p => p.Getter.ExplicitlyImplementedInterfaceMembers.Count() == 1)); Assert.That(type.Properties.All(p => p.Setter.ExplicitlyImplementedInterfaceMembers.Count() == 1)); } [Test] public void ExplicitDisposableImplementation() { ITypeDefinition disposable = GetTypeDefinition(typeof(ExplicitDisposableImplementation)); IMethod method = disposable.Methods.Single(m => !m.IsConstructor); Assert.That(method.IsExplicitInterfaceImplementation); Assert.That(method.ExplicitlyImplementedInterfaceMembers.Single().FullName, Is.EqualTo("System.IDisposable.Dispose")); } [Test] public void ExplicitImplementationOfUnifiedMethods() { IType type = compilation.FindType(typeof(ExplicitGenericInterfaceImplementationWithUnifiableMethods)); Assert.That(type.GetMethods(m => m.IsExplicitInterfaceImplementation).Count(), Is.EqualTo(2)); foreach (IMethod method in type.GetMethods(m => m.IsExplicitInterfaceImplementation)) { Assert.That(method.ExplicitlyImplementedInterfaceMembers.Count(), Is.EqualTo(1), method.ToString()); Assert.That(method.Parameters.Single().Type.ReflectionName, Is.EqualTo("System.Int32")); IMethod interfaceMethod = (IMethod)method.ExplicitlyImplementedInterfaceMembers.Single(); Assert.That(interfaceMethod.Parameters.Single().Type.ReflectionName, Is.EqualTo("System.Int32")); var genericParamType = ((IMethod)method.MemberDefinition).Parameters.Single().Type; var interfaceGenericParamType = ((IMethod)interfaceMethod.MemberDefinition).Parameters.Single().Type; Assert.That(genericParamType.Kind, Is.EqualTo(TypeKind.TypeParameter)); Assert.That(interfaceGenericParamType.Kind, Is.EqualTo(TypeKind.TypeParameter)); Assert.That(interfaceGenericParamType.ReflectionName, Is.EqualTo(genericParamType.ReflectionName)); } } [Test] public void ExplicitGenericInterfaceImplementation() { ITypeDefinition impl = GetTypeDefinition(typeof(ExplicitGenericInterfaceImplementation)); IType genericInterfaceOfString = compilation.FindType(typeof(IGenericInterface)); IMethod implMethod1 = impl.Methods.Single(m => !m.IsConstructor && m.Parameters[1].ReferenceKind == ReferenceKind.None); IMethod implMethod2 = impl.Methods.Single(m => !m.IsConstructor && m.Parameters[1].ReferenceKind == ReferenceKind.Ref); Assert.That(implMethod1.IsExplicitInterfaceImplementation); Assert.That(implMethod2.IsExplicitInterfaceImplementation); IMethod interfaceMethod1 = (IMethod)implMethod1.ExplicitlyImplementedInterfaceMembers.Single(); Assert.That(interfaceMethod1.DeclaringType, Is.EqualTo(genericInterfaceOfString)); Assert.That(interfaceMethod1.Parameters[1].ReferenceKind == ReferenceKind.None); IMethod interfaceMethod2 = (IMethod)implMethod2.ExplicitlyImplementedInterfaceMembers.Single(); Assert.That(interfaceMethod2.DeclaringType, Is.EqualTo(genericInterfaceOfString)); Assert.That(interfaceMethod2.Parameters[1].ReferenceKind == ReferenceKind.Ref); } [Test] public void ExplicitlyImplementedPropertiesShouldBeReportedAsBeingImplemented() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsPropertyExplicitly)); var prop = type.Properties.Single(); Assert.That(prop.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.Prop" })); Assert.That(prop.Getter.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.get_Prop" })); Assert.That(prop.Setter.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IInterfaceWithProperty.set_Prop" })); } [Test] public void ExplicitlyImplementedPropertiesShouldHaveExplicitlyImplementedAccessors() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsPropertyExplicitly)); var prop = type.Properties.Single(); Assert.That(prop.IsExplicitInterfaceImplementation); Assert.That(prop.Getter.IsExplicitInterfaceImplementation); Assert.That(prop.Setter.IsExplicitInterfaceImplementation); } /* The TS no longer provides implicit interface impls. [Test] public void EventAccessorsShouldBeReportedAsImplementingInterfaceAccessors() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsEvent)); var evt = type.Events.Single(p => p.Name == "Event"); Assert.That(evt.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.Event" })); Assert.That(evt.AddAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.add_Event" })); Assert.That(evt.RemoveAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.remove_Event" })); } [Test] public void EventAccessorsShouldBeReportedAsImplementingInterfaceAccessorsWhenCustomAccessorMethodsAreUsed() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsEventWithCustomAccessors)); var evt = type.Events.Single(p => p.Name == "Event"); Assert.That(evt.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.Event" })); Assert.That(evt.AddAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.add_Event" })); Assert.That(evt.RemoveAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.remove_Event" })); } */ [Test] public void ExplicitlyImplementedEventsShouldBeReportedAsBeingImplemented() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsEventExplicitly)); var evt = type.Events.Single(); Assert.That(evt.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.Event" })); Assert.That(evt.AddAccessor.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.add_Event" })); Assert.That(evt.RemoveAccessor.ExplicitlyImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.Decompiler.Tests.TypeSystem.IHasEvent.remove_Event" })); } [Test] public void MembersDeclaredInDerivedInterfacesDoNotImplementBaseMembers() { ITypeDefinition type = GetTypeDefinition(typeof(IShadowTestDerived)); var method = type.Methods.Single(m => m.Name == "Method"); var indexer = type.Properties.Single(p => p.IsIndexer); var prop = type.Properties.Single(p => p.Name == "Prop"); var evt = type.Events.Single(e => e.Name == "Evt"); Assert.That(method.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(indexer.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(indexer.Getter.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(indexer.Setter.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(prop.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(prop.Getter.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(prop.Setter.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(evt.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(evt.AddAccessor.ExplicitlyImplementedInterfaceMembers, Is.Empty); Assert.That(evt.RemoveAccessor.ExplicitlyImplementedInterfaceMembers, Is.Empty); } [Test] public void StaticClassTest() { ITypeDefinition type = GetTypeDefinition(typeof(StaticClass)); Assert.That(type.IsAbstract); Assert.That(type.IsSealed); Assert.That(type.IsStatic); } [Test] public void ExtensionMethodTest() { ITypeDefinition type = GetTypeDefinition(typeof(StaticClass)); var method = type.Methods.Single(m => m.Name == "Extension"); Assert.That(method.IsStatic); Assert.That(method.IsExtensionMethod); Assert.That(method.ReducedFrom, Is.Null); Assert.That(type.HasExtensions); } [Test] public void NoDefaultConstructorOnStaticClassTest() { ITypeDefinition type = GetTypeDefinition(typeof(StaticClass)); Assert.That(type.GetConstructors().Count(), Is.EqualTo(0)); } [Test] public void IndexerNonDefaultName() { ITypeDefinition type = GetTypeDefinition(typeof(IndexerNonDefaultName)); var indexer = type.GetProperties(p => p.IsIndexer).Single(); Assert.That(indexer.Name, Is.EqualTo("Foo")); } [Test] public void TestNullableDefaultParameter() { ITypeDefinition type = GetTypeDefinition(typeof(ClassWithMethodThatHasNullableDefaultParameter)); var method = type.GetMethods().Single(m => m.Name == "Foo"); Assert.That(method.Parameters.Single().GetConstantValue(), Is.EqualTo(42)); } [Test] public void AccessibilityTests() { ITypeDefinition type = GetTypeDefinition(typeof(AccessibilityTest)); Assert.That(type.Methods.Single(m => m.Name == "Public").Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(type.Methods.Single(m => m.Name == "Internal").Accessibility, Is.EqualTo(Accessibility.Internal)); Assert.That(type.Methods.Single(m => m.Name == "ProtectedInternal").Accessibility, Is.EqualTo(Accessibility.ProtectedOrInternal)); Assert.That(type.Methods.Single(m => m.Name == "InternalProtected").Accessibility, Is.EqualTo(Accessibility.ProtectedOrInternal)); Assert.That(type.Methods.Single(m => m.Name == "Protected").Accessibility, Is.EqualTo(Accessibility.Protected)); Assert.That(type.Methods.Single(m => m.Name == "Private").Accessibility, Is.EqualTo(Accessibility.Private)); Assert.That(type.Methods.Single(m => m.Name == "None").Accessibility, Is.EqualTo(Accessibility.Private)); } private void AssertConstantField(ITypeDefinition type, string name, T expected) { var f = type.GetFields().Single(x => x.Name == name); Assert.That(f.IsConst); Assert.That(f.GetConstantValue(), Is.EqualTo(expected)); Assert.That(f.GetAttributes().Count(), Is.EqualTo(0)); } [Test] public unsafe void ConstantFieldsCreatedWithNew() { ITypeDefinition type = GetTypeDefinition(typeof(ConstantFieldTest)); AssertConstantField(type, "CNewb", new byte()); AssertConstantField(type, "CNewsb", new sbyte()); AssertConstantField(type, "CNewc", new char()); AssertConstantField(type, "CNews", new short()); AssertConstantField(type, "CNewus", new ushort()); AssertConstantField(type, "CNewi", new int()); AssertConstantField(type, "CNewui", new uint()); AssertConstantField(type, "CNewl", new long()); AssertConstantField(type, "CNewul", new ulong()); AssertConstantField(type, "CNewd", new double()); AssertConstantField(type, "CNewf", new float()); AssertConstantField(type, "CNewm", new decimal()); } [Test] public void ConstantFieldsSizeOf() { ITypeDefinition type = GetTypeDefinition(typeof(ConstantFieldTest)); AssertConstantField(type, "SOsb", sizeof(sbyte)); AssertConstantField(type, "SOb", sizeof(byte)); AssertConstantField(type, "SOs", sizeof(short)); AssertConstantField(type, "SOus", sizeof(ushort)); AssertConstantField(type, "SOi", sizeof(int)); AssertConstantField(type, "SOui", sizeof(uint)); AssertConstantField(type, "SOl", sizeof(long)); AssertConstantField(type, "SOul", sizeof(ulong)); AssertConstantField(type, "SOc", sizeof(char)); AssertConstantField(type, "SOf", sizeof(float)); AssertConstantField(type, "SOd", sizeof(double)); AssertConstantField(type, "SObl", sizeof(bool)); AssertConstantField(type, "SOe", sizeof(MyEnum)); } [Test] public void ConstantEnumFromThisAssembly() { ITypeDefinition type = GetTypeDefinition(typeof(ConstantFieldTest)); IField field = type.Fields.Single(f => f.Name == "EnumFromThisAssembly"); Assert.That(field.IsConst); Assert.That(field.GetConstantValue(), Is.EqualTo((short)MyEnum.Second)); } [Test] public void ConstantEnumFromAnotherAssembly() { ITypeDefinition type = GetTypeDefinition(typeof(ConstantFieldTest)); IField field = type.Fields.Single(f => f.Name == "EnumFromAnotherAssembly"); Assert.That(field.IsConst); Assert.That(field.GetConstantValue(), Is.EqualTo((int)StringComparison.OrdinalIgnoreCase)); } [Test] public void DefaultOfEnum() { ITypeDefinition type = GetTypeDefinition(typeof(ConstantFieldTest)); IField field = type.Fields.Single(f => f.Name == "DefaultOfEnum"); Assert.That(field.IsConst); Assert.That(field.GetConstantValue(), Is.EqualTo((short)default(MyEnum))); } [Test] public void ExplicitImplementation() { var type = GetTypeDefinition(typeof(ExplicitImplementationTests)); var itype = GetTypeDefinition(typeof(IExplicitImplementationTests)); var methods = type.GetMethods(m => m.Name == "M" || m.Name.EndsWith(".M")).ToList(); var imethod = itype.GetMethods(m => m.Name == "M").Single(); Assert.That(methods.Select(m => m.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(imethod, Is.EqualTo(methods.SelectMany(m => m.ExplicitlyImplementedInterfaceMembers).Single())); var properties = type.GetProperties(p => p.Name == "P" || p.Name.EndsWith(".P")).ToList(); var iproperty = itype.GetProperties(m => m.Name == "P").Single(); Assert.That(properties.Select(p => p.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iproperty, Is.EqualTo(properties.SelectMany(p => p.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(properties.Select(p => p.Getter.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iproperty.Getter, Is.EqualTo(properties.SelectMany(p => p.Getter.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(properties.Select(p => p.Setter.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iproperty.Setter, Is.EqualTo(properties.SelectMany(p => p.Setter.ExplicitlyImplementedInterfaceMembers).Single())); var indexers = type.GetProperties(p => p.Name == "Item" || p.Name.EndsWith(".Item")).ToList(); var iindexer = itype.GetProperties(m => m.Name == "Item").Single(); Assert.That(indexers.Select(p => p.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iindexer, Is.EqualTo(indexers.SelectMany(p => p.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(indexers.Select(p => p.Getter.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iindexer.Getter, Is.EqualTo(indexers.SelectMany(p => p.Getter.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(indexers.Select(p => p.Setter.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(iindexer.Setter, Is.EqualTo(indexers.SelectMany(p => p.Setter.ExplicitlyImplementedInterfaceMembers).Single())); var events = type.GetEvents(e => e.Name == "E" || e.Name.EndsWith(".E")).ToList(); var ievent = itype.GetEvents(m => m.Name == "E").Single(); Assert.That(events.Select(e => e.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(ievent, Is.EqualTo(events.SelectMany(e => e.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(events.Select(e => e.AddAccessor.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(ievent.AddAccessor, Is.EqualTo(events.SelectMany(e => e.AddAccessor.ExplicitlyImplementedInterfaceMembers).Single())); Assert.That(events.Select(e => e.RemoveAccessor.ExplicitlyImplementedInterfaceMembers.Count()).ToList(), Is.EquivalentTo(new[] { 0, 1 })); Assert.That(ievent.RemoveAccessor, Is.EqualTo(events.SelectMany(e => e.RemoveAccessor.ExplicitlyImplementedInterfaceMembers).Single())); } [Test] public void MarshalTests() { ITypeDefinition c = compilation.FindType(typeof(IMarshalAsTests)).GetDefinition(); Assert.That(c.GetMethods(m => m.Name == "GetCollectionByQuery2").Count(), Is.EqualTo(1)); } [Test] public void AttributesUsingNestedMembers() { var type = GetTypeDefinition(typeof(ClassWithAttributesUsingNestedMembers)); var inner = type.GetNestedTypes().Single(t => t.Name == "Inner"); var myAttribute = type.GetNestedTypes().Single(t => t.Name == "MyAttribute"); var typeTypeTestAttr = type.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute"); Assert.That(typeTypeTestAttr.FixedArguments[0].Value, Is.EqualTo(42)); Assert.That(typeTypeTestAttr.FixedArguments[1].Value, Is.EqualTo(inner)); var typeMyAttr = type.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute"); Assert.That(typeMyAttr.AttributeType, Is.EqualTo(myAttribute)); var prop = type.GetProperties().Single(p => p.Name == "P"); var propTypeTestAttr = prop.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute"); Assert.That(propTypeTestAttr.FixedArguments[0].Value, Is.EqualTo(42)); Assert.That(propTypeTestAttr.FixedArguments[1].Value, Is.EqualTo(inner)); var propMyAttr = prop.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute"); Assert.That(propMyAttr.AttributeType, Is.EqualTo(myAttribute)); var attributedInner = (ITypeDefinition)type.GetNestedTypes().Single(t => t.Name == "AttributedInner"); var innerTypeTestAttr = attributedInner.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute"); Assert.That(innerTypeTestAttr.FixedArguments[0].Value, Is.EqualTo(42)); Assert.That(innerTypeTestAttr.FixedArguments[1].Value, Is.EqualTo(inner)); var innerMyAttr = attributedInner.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute"); Assert.That(innerMyAttr.AttributeType, Is.EqualTo(myAttribute)); var attributedInner2 = (ITypeDefinition)type.GetNestedTypes().Single(t => t.Name == "AttributedInner2"); var inner2 = attributedInner2.GetNestedTypes().Single(t => t.Name == "Inner"); var myAttribute2 = attributedInner2.GetNestedTypes().Single(t => t.Name == "MyAttribute"); var inner2TypeTestAttr = attributedInner2.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute"); Assert.That(inner2TypeTestAttr.FixedArguments[0].Value, Is.EqualTo(43)); Assert.That(inner2TypeTestAttr.FixedArguments[1].Value, Is.EqualTo(inner2)); var inner2MyAttr = attributedInner2.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute"); Assert.That(inner2MyAttr.AttributeType, Is.EqualTo(myAttribute2)); } [Test] public void ClassWithAttributeOnTypeParameter() { var tp = GetTypeDefinition(typeof(ClassWithAttributeOnTypeParameter<>)).TypeParameters.Single(); var attr = tp.GetAttributes().Single(); Assert.That(attr.AttributeType.Name, Is.EqualTo("DoubleAttribute")); } [Test] public void InheritanceTest() { ITypeDefinition c = compilation.FindType(typeof(SystemException)).GetDefinition(); ITypeDefinition c2 = compilation.FindType(typeof(Exception)).GetDefinition(); Assert.That(c, Is.Not.Null, "c is null"); Assert.That(c2, Is.Not.Null, "c2 is null"); //Assert.AreEqual(3, c.BaseTypes.Count); // Inherited interfaces are not reported by Cecil // which matches the behaviour of our C#/VB parsers Assert.That(c.DirectBaseTypes.First().FullName, Is.EqualTo("System.Exception")); Assert.That(c.DirectBaseTypes.First(), Is.SameAs(c2)); string[] superTypes = c.GetAllBaseTypes().Select(t => t.ReflectionName).ToArray(); Assert.That(superTypes, Is.EqualTo(new string[] { "System.Object", "System.Runtime.Serialization.ISerializable", "System.Runtime.InteropServices._Exception", "System.Exception", "System.SystemException" })); } [Test] public void GenericPropertyTest() { ITypeDefinition c = compilation.FindType(typeof(Comparer<>)).GetDefinition(); IProperty def = c.Properties.Single(p => p.Name == "Default"); ParameterizedType pt = (ParameterizedType)def.ReturnType; Assert.That(pt.FullName, Is.EqualTo("System.Collections.Generic.Comparer")); Assert.That(pt.TypeArguments[0], Is.EqualTo(c.TypeParameters[0])); } [Test] public void PointerTypeTest() { ITypeDefinition c = compilation.FindType(typeof(IntPtr)).GetDefinition(); IMethod toPointer = c.Methods.Single(p => p.Name == "ToPointer"); Assert.That(toPointer.ReturnType.ReflectionName, Is.EqualTo("System.Void*")); Assert.That(toPointer.ReturnType is PointerType); Assert.That(((PointerType)toPointer.ReturnType).ElementType.FullName, Is.EqualTo("System.Void")); } [Test] public void DateTimeDefaultConstructor() { ITypeDefinition c = compilation.FindType(typeof(DateTime)).GetDefinition(); Assert.That(c.Methods.Count(m => m.IsConstructor && !m.IsStatic && m.Parameters.Count == 0), Is.EqualTo(1)); Assert.That(c.GetConstructors().Count(m => m.Parameters.Count == 0), Is.EqualTo(1)); } [Test] public void NoEncodingInfoDefaultConstructor() { ITypeDefinition c = compilation.FindType(typeof(EncodingInfo)).GetDefinition(); // EncodingInfo only has an internal constructor Assert.That(!c.Methods.Any(m => m.IsConstructor)); // and no implicit ctor should be added: Assert.That(c.GetConstructors().Count(), Is.EqualTo(0)); } [Test] public void StaticModifierTest() { ITypeDefinition c = compilation.FindType(typeof(Environment)).GetDefinition(); Assert.That(c, Is.Not.Null, "System.Environment not found"); Assert.That(c.IsAbstract, "class should be abstract"); Assert.That(c.IsSealed, "class should be sealed"); Assert.That(c.IsStatic, "class should be static"); } [Test] public void InnerClassReferenceTest() { ITypeDefinition c = compilation.FindType(typeof(Environment)).GetDefinition(); Assert.That(c, Is.Not.Null, "System.Environment not found"); IType rt = c.Methods.First(m => m.Name == "GetFolderPath").Parameters[0].Type; Assert.That(rt, Is.SameAs(c.NestedTypes.Single(ic => ic.Name == "SpecialFolder"))); } [Test] public void NestedTypesTest() { ITypeDefinition c = compilation.FindType(typeof(Environment.SpecialFolder)).GetDefinition(); Assert.That(c, Is.Not.Null, "c is null"); Assert.That(c.FullName, Is.EqualTo("System.Environment.SpecialFolder")); Assert.That(c.ReflectionName, Is.EqualTo("System.Environment+SpecialFolder")); } [Test] public void VoidHasNoMembers() { ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); Assert.That(c, Is.Not.Null, "System.Void not found"); Assert.That(c.Kind, Is.EqualTo(TypeKind.Void)); Assert.That(c.GetMethods().Count(), Is.EqualTo(0)); Assert.That(c.GetProperties().Count(), Is.EqualTo(0)); Assert.That(c.GetEvents().Count(), Is.EqualTo(0)); Assert.That(c.GetFields().Count(), Is.EqualTo(0)); } [Test] public void Void_SerializableAttribute() { ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.SerializableAttribute"); Assert.That(attr.Constructor.Parameters.Count, Is.EqualTo(0)); Assert.That(attr.FixedArguments.Length, Is.EqualTo(0)); Assert.That(attr.NamedArguments.Length, Is.EqualTo(0)); } [Test] public void Void_StructLayoutAttribute() { ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.StructLayoutAttribute"); Assert.That(attr.Constructor.Parameters.Count, Is.EqualTo(1)); Assert.That(attr.FixedArguments.Length, Is.EqualTo(1)); Assert.That(attr.FixedArguments[0].Value, Is.EqualTo(0)); Assert.That(attr.NamedArguments.Length, Is.EqualTo(1)); Assert.That(attr.NamedArguments[0].Name, Is.EqualTo("Size")); Assert.That(attr.NamedArguments[0].Value, Is.EqualTo(1)); } [Test] public void Void_ComVisibleAttribute() { ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.ComVisibleAttribute"); Assert.That(attr.Constructor.Parameters.Count, Is.EqualTo(1)); Assert.That(attr.FixedArguments.Length, Is.EqualTo(1)); Assert.That(attr.FixedArguments[0].Value, Is.EqualTo(true)); Assert.That(attr.NamedArguments.Length, Is.EqualTo(0)); } [Test] public void NestedClassInGenericClassTest() { ITypeDefinition dictionary = compilation.FindType(typeof(Dictionary<,>)).GetDefinition(); Assert.That(dictionary, Is.Not.Null); ITypeDefinition valueCollection = compilation.FindType(typeof(Dictionary<,>.ValueCollection)).GetDefinition(); Assert.That(valueCollection, Is.Not.Null); var dictionaryRT = new ParameterizedType(dictionary, new[] { compilation.FindType(typeof(string)).GetDefinition(), compilation.FindType(typeof(int)).GetDefinition() }); IProperty valueProperty = dictionaryRT.GetProperties(p => p.Name == "Values").Single(); IType parameterizedValueCollection = valueProperty.ReturnType; Assert.That(parameterizedValueCollection.ReflectionName, Is.EqualTo("System.Collections.Generic.Dictionary`2+ValueCollection[[System.String],[System.Int32]]")); Assert.That(parameterizedValueCollection.GetDefinition(), Is.SameAs(valueCollection)); } [Test] public void ValueCollectionCountModifiers() { ITypeDefinition valueCollection = compilation.FindType(typeof(Dictionary<,>.ValueCollection)).GetDefinition(); Assert.That(valueCollection.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(valueCollection.IsSealed); Assert.That(!valueCollection.IsAbstract); Assert.That(!valueCollection.IsStatic); IProperty count = valueCollection.Properties.Single(p => p.Name == "Count"); Assert.That(count.Accessibility, Is.EqualTo(Accessibility.Public)); // It's sealed on the IL level; but in C# it's just a normal non-virtual method that happens to implement an interface Assert.That(!count.IsSealed); Assert.That(!count.IsVirtual); Assert.That(!count.IsAbstract); } [Test] public void MathAcosModifiers() { ITypeDefinition math = compilation.FindType(typeof(Math)).GetDefinition(); Assert.That(math.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(math.IsSealed); Assert.That(math.IsAbstract); Assert.That(math.IsStatic); IMethod acos = math.Methods.Single(p => p.Name == "Acos"); Assert.That(acos.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(acos.IsStatic); Assert.That(!acos.IsAbstract); Assert.That(!acos.IsSealed); Assert.That(!acos.IsVirtual); Assert.That(!acos.IsOverride); } [Test] public void EncodingModifiers() { ITypeDefinition encoding = compilation.FindType(typeof(Encoding)).GetDefinition(); Assert.That(encoding.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!encoding.IsSealed); Assert.That(encoding.IsAbstract); IMethod getDecoder = encoding.Methods.Single(p => p.Name == "GetDecoder"); Assert.That(getDecoder.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!getDecoder.IsStatic); Assert.That(!getDecoder.IsAbstract); Assert.That(!getDecoder.IsSealed); Assert.That(getDecoder.IsVirtual); Assert.That(!getDecoder.IsOverride); IMethod getMaxByteCount = encoding.Methods.Single(p => p.Name == "GetMaxByteCount"); Assert.That(getMaxByteCount.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!getMaxByteCount.IsStatic); Assert.That(getMaxByteCount.IsAbstract); Assert.That(!getMaxByteCount.IsSealed); Assert.That(!getMaxByteCount.IsVirtual); Assert.That(!getMaxByteCount.IsOverride); IProperty encoderFallback = encoding.Properties.Single(p => p.Name == "EncoderFallback"); Assert.That(encoderFallback.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!encoderFallback.IsStatic); Assert.That(!encoderFallback.IsAbstract); Assert.That(!encoderFallback.IsSealed); Assert.That(!encoderFallback.IsVirtual); Assert.That(!encoderFallback.IsOverride); } [Test] public void UnicodeEncodingModifiers() { ITypeDefinition encoding = compilation.FindType(typeof(UnicodeEncoding)).GetDefinition(); Assert.That(encoding.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!encoding.IsSealed); Assert.That(!encoding.IsAbstract); IMethod getDecoder = encoding.Methods.Single(p => p.Name == "GetDecoder"); Assert.That(getDecoder.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!getDecoder.IsStatic); Assert.That(!getDecoder.IsAbstract); Assert.That(!getDecoder.IsSealed); Assert.That(!getDecoder.IsVirtual); Assert.That(getDecoder.IsOverride); } [Test] public void UTF32EncodingModifiers() { ITypeDefinition encoding = compilation.FindType(typeof(UTF32Encoding)).GetDefinition(); Assert.That(encoding.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(encoding.IsSealed); Assert.That(!encoding.IsAbstract); IMethod getDecoder = encoding.Methods.Single(p => p.Name == "GetDecoder"); Assert.That(getDecoder.Accessibility, Is.EqualTo(Accessibility.Public)); Assert.That(!getDecoder.IsStatic); Assert.That(!getDecoder.IsAbstract); Assert.That(!getDecoder.IsSealed); Assert.That(!getDecoder.IsVirtual); Assert.That(getDecoder.IsOverride); } [Test] public void FindRedirectedType() { var compilationWithSystemCore = new SimpleCompilation(SystemCore, Mscorlib); var type = ReflectionHelper.ParseReflectionName("System.Func`2, System.Core", new SimpleTypeResolveContext(compilationWithSystemCore)); ITypeDefinition c = type.GetDefinition(); Assert.That(c, Is.Not.Null, "System.Func<,> not found"); Assert.That(c.ParentModule.AssemblyName, Is.EqualTo("mscorlib")); } public void DelegateIsClass() { var @delegate = compilation.FindType(KnownTypeCode.Delegate).GetDefinition(); Assert.That(@delegate, Is.EqualTo(TypeKind.Class)); Assert.That(!@delegate.IsSealed); } public void MulticastDelegateIsClass() { var multicastDelegate = compilation.FindType(KnownTypeCode.MulticastDelegate).GetDefinition(); Assert.That(multicastDelegate, Is.EqualTo(TypeKind.Class)); Assert.That(!multicastDelegate.IsSealed); } [Test] public void HasSpecialName() { var nonCustomAttributes = compilation.FindType(typeof(NonCustomAttributes)).GetDefinition(); var method = nonCustomAttributes.GetMethods(m => m.Name == "SpecialNameMethod").Single(); var property = nonCustomAttributes.GetProperties(p => p.Name == "SpecialNameProperty").Single(); var @event = nonCustomAttributes.GetEvents(e => e.Name == "SpecialNameEvent").Single(); var field = nonCustomAttributes.GetFields(f => f.Name == "SpecialNameField").Single(); var @class = nonCustomAttributes.GetNestedTypes(t => t.Name == "SpecialNameClass").Single().GetDefinition(); var @struct = nonCustomAttributes.GetNestedTypes(t => t.Name == "SpecialNameStruct").Single().GetDefinition(); Assert.That(method.HasAttribute(KnownAttribute.SpecialName)); Assert.That(property.HasAttribute(KnownAttribute.SpecialName)); Assert.That(@event.HasAttribute(KnownAttribute.SpecialName)); Assert.That(field.HasAttribute(KnownAttribute.SpecialName)); Assert.That(@class.HasAttribute(KnownAttribute.SpecialName)); Assert.That(@struct.HasAttribute(KnownAttribute.SpecialName)); } [Test] public void ExtensionEverything() { var extensionEverything = GetTypeDefinition(typeof(ExtensionEverything)); Assert.That(extensionEverything.IsStatic, Is.True, "ExtensionEverything should be static"); Assert.That(extensionEverything.HasExtensions, Is.True, "ExtensionEverything should have extensions"); var info = extensionEverything.ExtensionInfo; Assert.That(info, Is.Not.Null, "ExtensionEverything should have ExtensionInfo"); foreach (var method in extensionEverything.Methods) { Assert.That(method.IsStatic, Is.True, "Method should be static: " + method.Name); ExtensionMemberInfo? infoOfImpl = info.InfoOfImplementationMember(method); Assert.That(infoOfImpl, Is.Not.Null, "Method should have implementation info: " + method.Name); ExtensionMemberInfo? infoOfExtension = info.InfoOfExtensionMember(infoOfImpl.Value.ExtensionMember); Assert.That(infoOfExtension, Is.EqualTo(infoOfImpl), "Info of extension member should be equal to info of implementation member: " + method.Name); } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs ================================================ // Copyright (c) 2010-2018 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 // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: ICSharpCode.Decompiler.Tests.TypeSystem.TypeTestAttribute( 42, typeof(System.Action<>), typeof(IDictionary>))] [assembly: TypeForwardedTo(typeof(Func<,>))] namespace ICSharpCode.Decompiler.Tests.TypeSystem { public delegate S GenericDelegate(T input) where T : S where S : class; public class SimplePublicClass { public void Method() { } public SimplePublicClass() { } [Double(1)] ~SimplePublicClass() { } } public class TypeTestAttribute : Attribute { public TypeTestAttribute(int a1, Type a2, Type a3) { } #pragma warning disable CS0465 private void Finalize() { } #pragma warning restore CS0465 } [Params(1, StringComparison.CurrentCulture, null, 4.0, "Test")] public class ParamsAttribute : Attribute { public ParamsAttribute(params object[] x) { } [Params(Property = new string[] { "a", "b" })] public string[] Property { [return: Params("Attribute on return type of getter")] get { return null; } set { } } } [Double(1)] public class DoubleAttribute : Attribute { public DoubleAttribute(double val) { } } public unsafe class DynamicTest { public dynamic DynamicField; public dynamic SimpleProperty { get; set; } public List DynamicGenerics1(Action param) { return null; } public void DynamicGenerics2(Action param) { } public void DynamicGenerics3(Action param) { } public void DynamicGenerics4(Action param) { } public void DynamicGenerics5(Action param) { } public void DynamicGenerics6(ref Action param) { } public void DynamicGenerics7(Action param) { } } public class GenericClass where A : B { public void TestMethod(string param) where V : K where K : IComparable { } public void GetIndex(T element) where T : IEquatable { } public NestedEnum EnumField; public A Property { get; set; } public enum NestedEnum { EnumMember } } public class PropertyTest { public int PropertyWithProtectedSetter { get; protected set; } public object PropertyWithPrivateSetter { get; private set; } public object PropertyWithoutSetter { get { return null; } } public object PropertyWithPrivateGetter { private get; set; } public string this[int index] { get { return "Test"; } set { } } } public enum MyEnum : short { First, Second, Flag1 = 0x10, Flag2 = 0x20, CombinedFlags = Flag1 | Flag2 } public class Base { public class Nested { } ~Base() { } public virtual void GenericMethodWithConstraints(T a) where X : IComparer, new() { } } public class Derived : Base { ~Derived() { } public override void GenericMethodWithConstraints(B a) { } } public struct MyStructWithCtor { public MyStructWithCtor(int a) { } } public struct MyStructWithDefaultCtor { public MyStructWithDefaultCtor() { } } public class MyClassWithCtor { private MyClassWithCtor(int a) { } } [Serializable] public class NonCustomAttributes { [SpecialName] public class SpecialNameClass { } [SpecialName] public struct SpecialNameStruct { } [NonSerialized] public readonly int NonSerializedField; [SpecialName] public readonly int SpecialNameField; [SpecialName] public event EventHandler SpecialNameEvent; [SpecialName] public int SpecialNameProperty { get; set; } [DllImport("unmanaged.dll", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DllMethod([In, Out] ref int p); [DllImport("unmanaged.dll", PreserveSig = false)] public static extern bool DoNotPreserveSig(); [PreserveSig] public static void PreserveSigAsAttribute() { } [SpecialName] public static void SpecialNameMethod() { } } [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8)] public struct ExplicitFieldLayoutStruct { [FieldOffset(0)] public int Field0; [FieldOffset(100)] public int Field100; } public class ParameterTests { public void MethodWithOutParameter(out int x) { x = 0; } public void MethodWithParamsArray(params object[] x) { } public void MethodWithOptionalParameter(int x = 4) { } public void MethodWithExplicitOptionalParameter([Optional] int x) { } public void MethodWithRefParameter(ref int x) { } public void MethodWithInParameter(in int x) { } public void MethodWithEnumOptionalParameter(StringComparison x = StringComparison.OrdinalIgnoreCase) { } public void MethodWithOptionalNullableParameter(int? x = null) { } public void MethodWithOptionalLongParameter(long x = 1) { } public void MethodWithOptionalNullableLongParameter(long? x = 1) { } public void MethodWithOptionalDecimalParameter(decimal x = 1) { } public void VarArgsMethod(__arglist) { } } public class VarArgsCtor { public VarArgsCtor(__arglist) { } } [ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IAssemblyEnum { [PreserveSig()] int GetNextAssembly(uint dwFlags); } public class OuterGeneric { public class Inner { public OuterGeneric referenceToOuter; public Inner(OuterGeneric referenceToOuter) { } } public OuterGeneric.Inner Field1; public Inner Field2; public OuterGeneric.Inner>.Inner Field3; } public class ExplicitDisposableImplementation : IDisposable { void IDisposable.Dispose() { } } public interface IGenericInterface { void Test(T a, S b) where S : T; void Test(T a, ref S b); } public class ExplicitGenericInterfaceImplementation : IGenericInterface { void IGenericInterface.Test(string a, T b) { } void IGenericInterface.Test(string a, ref T b) { } } public interface IGenericInterfaceWithUnifiableMethods { void Test(T a); void Test(S a); } public class ImplementationOfUnifiedMethods : IGenericInterfaceWithUnifiableMethods { public void Test(int a) { } } public class ExplicitGenericInterfaceImplementationWithUnifiableMethods : IGenericInterfaceWithUnifiableMethods { void IGenericInterfaceWithUnifiableMethods.Test(T a) { } void IGenericInterfaceWithUnifiableMethods.Test(S a) { } } public partial class PartialClass { partial void PartialMethodWithImplementation(int a); partial void PartialMethodWithImplementation(System.Int32 a) { } partial void PartialMethodWithImplementation(string a); partial void PartialMethodWithImplementation(System.String a) { } partial void PartialMethodWithoutImplementation(); } public class ClassWithStaticAndNonStaticMembers { public static event System.EventHandler Event1 { add { } remove { } } public event System.EventHandler Event2 { add { } remove { } } #pragma warning disable 67 public static event System.EventHandler Event3; public event System.EventHandler Event4; public static int Prop1 { get { return 0; } set { } } public int Prop2 { get { return 0; } set { } } public static int Prop3 { get; set; } public int Prop4 { get; set; } } public interface IInterfaceWithProperty { int Prop { get; set; } } public interface IBase1 { int Prop { get; set; } } public interface IBase2 { int Prop { get; set; } } public interface IDerived : IBase1, IBase2 { new int Prop { get; set; } } public class ClassWithVirtualProperty { public virtual int Prop { get; protected set; } } public class ClassThatOverridesAndSealsVirtualProperty : ClassWithVirtualProperty { public sealed override int Prop { get; protected set; } } public class ClassThatOverridesGetterOnly : ClassWithVirtualProperty { public override int Prop { get { return 1; } } } public class ClassThatOverridesSetterOnly : ClassThatOverridesGetterOnly { public override int Prop { protected set { } } } public class ClassThatImplementsProperty : IInterfaceWithProperty { public int Prop { get; set; } } public class ClassThatImplementsPropertyExplicitly : IInterfaceWithProperty { int IInterfaceWithProperty.Prop { get; set; } } public interface IInterfaceWithIndexers { int this[int x] { get; set; } int this[string x] { get; set; } int this[int x, int y] { get; set; } } public interface IGenericInterfaceWithIndexer { int this[T x] { get; set; } } public interface IInterfaceWithRenamedIndexer { [IndexerName("NewName")] int this[int x] { get; set; } } public class ClassThatImplementsIndexers : IInterfaceWithIndexers, IGenericInterfaceWithIndexer { public int this[int x] { get { return 0; } set { } } public int this[string x] { get { return 0; } set { } } public int this[int x, int y] { get { return 0; } set { } } } public class ClassThatImplementsIndexersExplicitly : IInterfaceWithIndexers, IGenericInterfaceWithIndexer, IInterfaceWithRenamedIndexer { int IInterfaceWithIndexers.this[int x] { get { return 0; } set { } } int IGenericInterfaceWithIndexer.this[int x] { get { return 0; } set { } } int IInterfaceWithIndexers.this[string x] { get { return 0; } set { } } int IInterfaceWithIndexers.this[int x, int y] { get { return 0; } set { } } int IInterfaceWithRenamedIndexer.this[int x] { get { return 0; } set { } } } public interface IHasEvent { event EventHandler Event; } public class ClassThatImplementsEvent : IHasEvent { public event EventHandler Event; } public class ClassThatImplementsEventWithCustomAccessors : IHasEvent { public event EventHandler Event { add { } remove { } } } public class ClassThatImplementsEventExplicitly : IHasEvent { event EventHandler IHasEvent.Event { add { } remove { } } } public interface IShadowTestBase { void Method(); int this[int i] { get; set; } int Prop { get; set; } event EventHandler Evt; } public interface IShadowTestDerived : IShadowTestBase { new void Method(); new int this[int i] { get; set; } new int Prop { get; set; } new event EventHandler Evt; } public static class StaticClass { public static void Extension(this object inst) { } } public abstract class AbstractClass { } public class IndexerNonDefaultName { [IndexerName("Foo")] public int this[int index] { get { return 0; } } } public class ClassWithMethodThatHasNullableDefaultParameter { public void Foo(int? bar = 42) { } } public class AccessibilityTest { public void Public() { } internal void Internal() { } protected internal void ProtectedInternal() { } internal protected void InternalProtected() { } protected void Protected() { } private void Private() { } void None() { } } public class ConstantFieldTest { public const byte Cb = 42; public const sbyte Csb = 42; public const char Cc = '\x42'; public const short Cs = 42; public const ushort Cus = 42; public const int Ci = 42; public const uint Cui = 42; public const long Cl = 42; public const ulong Cul = 42; public const double Cd = 42; public const float Cf = 42; public const decimal Cm = 42; public const string S = "hello, world"; public const string NullString = null; public const MyEnum EnumFromThisAssembly = MyEnum.Second; public const StringComparison EnumFromAnotherAssembly = StringComparison.OrdinalIgnoreCase; public const MyEnum DefaultOfEnum = default(MyEnum); public const int SOsb = sizeof(sbyte); public const int SOb = sizeof(byte); public const int SOs = sizeof(short); public const int SOus = sizeof(ushort); public const int SOi = sizeof(int); public const int SOui = sizeof(uint); public const int SOl = sizeof(long); public const int SOul = sizeof(ulong); public const int SOc = sizeof(char); public const int SOf = sizeof(float); public const int SOd = sizeof(double); public const int SObl = sizeof(bool); public const int SOe = sizeof(MyEnum); public const byte CNewb = new byte(); public const sbyte CNewsb = new sbyte(); public const char CNewc = new char(); public const short CNews = new short(); public const ushort CNewus = new ushort(); public const int CNewi = new int(); public const uint CNewui = new uint(); public const long CNewl = new long(); public const ulong CNewul = new ulong(); public const double CNewd = new double(); public const float CNewf = new float(); public const decimal CNewm = new decimal(); } public interface IExplicitImplementationTests { void M(int a); int P { get; set; } event Action E; int this[int x] { get; set; } } public class ExplicitImplementationTests : IExplicitImplementationTests { public void M(int a) { } public int P { get; set; } public event Action E; public int this[int x] { get { return 0; } set { } } void IExplicitImplementationTests.M(int a) { } int IExplicitImplementationTests.P { get; set; } event Action IExplicitImplementationTests.E { add { } remove { } } int IExplicitImplementationTests.this[int x] { get { return 0; } set { } } } [TypeTest(C, typeof(Inner), typeof(int)), My] public class ClassWithAttributesUsingNestedMembers { sealed class MyAttribute : Attribute { } const int C = 42; class Inner { } [TypeTest(C, typeof(Inner), typeof(int)), My] public int P { get; set; } [TypeTest(C, typeof(Inner), typeof(int)), My] class AttributedInner { } [TypeTest(C, typeof(Inner), typeof(int)), My] class AttributedInner2 { sealed class MyAttribute : Attribute { } const int C = 43; class Inner { } } } public struct GenericStructWithIDisposableConstraintAndImplicitConversion where T : IDisposable { public static implicit operator GenericStructWithIDisposableConstraintAndImplicitConversion(T s) { return default(GenericStructWithIDisposableConstraintAndImplicitConversion); } } public class ClassImplementingIDisposable : IDisposable { public void Dispose() { } } public class ClassWithAttributeOnTypeParameter<[Double(2)] T> { } [Guid("790C6E0B-9194-4cc9-9426-A48A63185696"), InterfaceType(ComInterfaceType.InterfaceIsDual)] [ComImport] public interface IMarshalAsTests { [DispId(48)] void AliasComponent([MarshalAs(UnmanagedType.BStr)][In] string bstrSrcApplicationIDOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrCLSIDOrProgID, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestApplicationIDOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrNewProgId, [MarshalAs(UnmanagedType.BStr)][In] string bstrNewClsid); [DispId(33)] [return: MarshalAs(UnmanagedType.VariantBool)] bool AreApplicationInstancesPaused([MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationInstanceID); [DispId(19)] void BackupREGDB([MarshalAs(UnmanagedType.BStr)][In] string bstrBackupFilePath); [DispId(2)] [return: MarshalAs(UnmanagedType.Interface)] object Connect([MarshalAs(UnmanagedType.BStr)][In] string connectStr); [DispId(45)] void CopyApplications([MarshalAs(UnmanagedType.BStr)][In] string bstrSourcePartitionIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationID, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestinationPartitionIDOrName); [DispId(46)] void CopyComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrSourceApplicationIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarCLSIDOrProgID, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestinationApplicationIDOrName); [DispId(36)] void CreateServiceForApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrServiceName, [MarshalAs(UnmanagedType.BStr)][In] string bstrStartType, [MarshalAs(UnmanagedType.BStr)][In] string bstrErrorControl, [MarshalAs(UnmanagedType.BStr)][In] string bstrDependencies, [MarshalAs(UnmanagedType.BStr)][In] string bstrRunAs, [MarshalAs(UnmanagedType.BStr)][In] string bstrPassword, [MarshalAs(UnmanagedType.VariantBool)][In] bool bDesktopOk); [DispId(40)] void CurrentPartition([MarshalAs(UnmanagedType.BStr)][In] string bstrPartitionIDOrName); [DispId(41)] [return: MarshalAs(UnmanagedType.BStr)] string CurrentPartitionID(); [DispId(42)] [return: MarshalAs(UnmanagedType.BStr)] string CurrentPartitionName(); [DispId(37)] void DeleteServiceForApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName); [DispId(34)] [return: MarshalAs(UnmanagedType.BStr)] string DumpApplicationInstance([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationInstanceID, [MarshalAs(UnmanagedType.BStr)][In] string bstrDirectory, [MarshalAs(UnmanagedType.I4)][In] int lMaxImages); [DispId(9)] void ExportApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationFile, [In] int lOptions); [DispId(54)] void ExportPartition([MarshalAs(UnmanagedType.BStr)][In] string bstrPartitionIDOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrPartitionFileName, [MarshalAs(UnmanagedType.I4)][In] int lOptions); [DispId(44)] void FlushPartitionCache(); [DispId(28)] [return: MarshalAs(UnmanagedType.BStr)] string GetApplicationInstanceIDFromProcessID([MarshalAs(UnmanagedType.I4)][In] int lProcessID); [DispId(1)] [return: MarshalAs(UnmanagedType.Interface)] object GetCollection([MarshalAs(UnmanagedType.BStr)][In] string bstrCollName); [DispId(5)] [return: MarshalAs(UnmanagedType.Interface)] object GetCollectionByQuery([MarshalAs(UnmanagedType.BStr)][In] string collName, [MarshalAs(UnmanagedType.SafeArray)][In] ref object[] aQuery); [DispId(27)] [return: MarshalAs(UnmanagedType.Interface)] object GetCollectionByQuery2([MarshalAs(UnmanagedType.BStr)][In] string bstrCollectionName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarQueryStrings); [DispId(57)] [return: MarshalAs(UnmanagedType.I4)] int GetComponentVersionCount([MarshalAs(UnmanagedType.BStr)][In] string bstrCLSIDOrProgID); [DispId(26)] void GetEventClassesForIID([In] string bstrIID, [MarshalAs(UnmanagedType.SafeArray)][In][Out] ref object[] varCLSIDS, [MarshalAs(UnmanagedType.SafeArray)][In][Out] ref object[] varProgIDs, [MarshalAs(UnmanagedType.SafeArray)][In][Out] ref object[] varDescriptions); [DispId(17)] void GetMultipleComponentsInfo([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [In] object varFileNames, [MarshalAs(UnmanagedType.SafeArray)] out object[] varCLSIDS, [MarshalAs(UnmanagedType.SafeArray)] out object[] varClassNames, [MarshalAs(UnmanagedType.SafeArray)] out object[] varFileFlags, [MarshalAs(UnmanagedType.SafeArray)] out object[] varComponentFlags); [DispId(38)] [return: MarshalAs(UnmanagedType.BStr)] string GetPartitionID([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName); [DispId(39)] [return: MarshalAs(UnmanagedType.BStr)] string GetPartitionName([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName); [DispId(43)] [return: MarshalAs(UnmanagedType.BStr)] string GlobalPartitionID(); [DispId(6)] void ImportComponent([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrCLSIDOrProgId); [DispId(52)] void ImportComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarCLSIDOrProgID, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarComponentType); [DispId(50)] void ImportUnconfiguredComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarCLSIDOrProgID, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarComponentType); [DispId(10)] void InstallApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationFile, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestinationDirectory, [In] int lOptions, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserId, [MarshalAs(UnmanagedType.BStr)][In] string bstrPassword, [MarshalAs(UnmanagedType.BStr)][In] string bstrRSN); [DispId(7)] void InstallComponent([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrDLL, [MarshalAs(UnmanagedType.BStr)][In] string bstrTLB, [MarshalAs(UnmanagedType.BStr)][In] string bstrPSDLL); [DispId(25)] void InstallEventClass([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.BStr)][In] string bstrDLL, [MarshalAs(UnmanagedType.BStr)][In] string bstrTLB, [MarshalAs(UnmanagedType.BStr)][In] string bstrPSDLL); [DispId(16)] void InstallMultipleComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)][In] ref object[] fileNames, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)][In] ref object[] CLSIDS); [DispId(24)] void InstallMultipleEventClasses([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)][In] ref object[] fileNames, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)][In] ref object[] CLSIDS); [DispId(55)] void InstallPartition([MarshalAs(UnmanagedType.BStr)][In] string bstrFileName, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestDirectory, [MarshalAs(UnmanagedType.I4)][In] int lOptions, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserID, [MarshalAs(UnmanagedType.BStr)][In] string bstrPassword, [MarshalAs(UnmanagedType.BStr)][In] string bstrRSN); [DispId(53)] [return: MarshalAs(UnmanagedType.VariantBool)] bool Is64BitCatalogServer(); [DispId(35)] [return: MarshalAs(UnmanagedType.VariantBool)] bool IsApplicationInstanceDumpSupported(); [DispId(49)] [return: MarshalAs(UnmanagedType.Interface)] object IsSafeToDelete([MarshalAs(UnmanagedType.BStr)][In] string bstrDllName); [DispId(3)] int MajorVersion(); [DispId(4)] int MinorVersion(); [DispId(47)] void MoveComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrSourceApplicationIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarCLSIDOrProgID, [MarshalAs(UnmanagedType.BStr)][In] string bstrDestinationApplicationIDOrName); [DispId(30)] void PauseApplicationInstances([MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationInstanceID); [DispId(51)] void PromoteUnconfiguredComponents([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationIDOrName, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarCLSIDOrProgID, [MarshalAs(UnmanagedType.LPStruct)][In] object pVarComponentType); [DispId(21)] void QueryApplicationFile([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationFile, [MarshalAs(UnmanagedType.BStr)] out string bstrApplicationName, [MarshalAs(UnmanagedType.BStr)] out string bstrApplicationDescription, [MarshalAs(UnmanagedType.VariantBool)] out bool bHasUsers, [MarshalAs(UnmanagedType.VariantBool)] out bool bIsProxy, [MarshalAs(UnmanagedType.SafeArray)] out object[] varFileNames); [DispId(56)] [return: MarshalAs(UnmanagedType.IDispatch)] object QueryApplicationFile2([MarshalAs(UnmanagedType.BStr)][In] string bstrApplicationFile); [DispId(32)] void RecycleApplicationInstances([MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationInstanceID, [MarshalAs(UnmanagedType.I4)][In] int lReasonCode); [DispId(18)] void RefreshComponents(); [DispId(12)] void RefreshRouter(); [DispId(14)] void Reserved1(); [DispId(15)] void Reserved2(); [DispId(20)] void RestoreREGDB([MarshalAs(UnmanagedType.BStr)][In] string bstrBackupFilePath); [DispId(31)] void ResumeApplicationInstances([MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationInstanceID); [DispId(23)] int ServiceCheck([In] int lService); [DispId(8)] void ShutdownApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName); [DispId(29)] void ShutdownApplicationInstances([MarshalAs(UnmanagedType.LPStruct)][In] object pVarApplicationInstanceID); [DispId(22)] void StartApplication([MarshalAs(UnmanagedType.BStr)][In] string bstrApplIdOrName); [DispId(13)] void StartRouter(); [DispId(11)] void StopRouter(); } public static class ExtensionEverything { extension(int input) { public void Method() { } public void Method(char c) { } public string AsString => input.ToString(); public string Test { get => "Test"; set { } } public static void StaticMethod() { } public static void StaticMethod(double x) { } public static string StaticProperty => "StaticProperty"; public static void GenericMethod(T value) { } } extension(int number) { public int Squared => number * number; } extension(string input) { public void Method() { } public void Method(char c) { } public string AsString => input.ToString(); public string Test { get => "Test"; set { } } public static void StaticMethodOnString() { } public static void StaticMethodOnString(double x) { } public static string StaticPropertyOnString => "StaticProperty"; public static void GenericMethodOnString(T value) { } } extension(T input) { public void Method() { } public void Method(char c) { } public string AsString => input.ToString(); public string Test { get => "Test"; set { } } public static void StaticMethodOnGeneric() { } public static void StaticMethodOnGeneric(double x) { } public static string StaticPropertyOnGeneric => "StaticProperty"; public static void GenericMethodOnGeneric(U value) { } } extension(T input) { public void StaticMethodOnGenericTwoParams(T2 x) { } } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/UglyTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class UglyTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/Ugly"; [Test] public void AllFilesHaveTests() { var testNames = GetType().GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase) || file.Extension.Equals(".cs", StringComparison.OrdinalIgnoreCase)) { var testName = file.Name.Split('.')[0]; Assert.That(testNames, Has.Member(testName)); } } } static readonly CompilerOptions[] noRoslynOptions = { CompilerOptions.None, CompilerOptions.Optimize }; static readonly CompilerOptions[] roslynOnlyOptions = { CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] defaultOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; [Test] public async Task NoArrayInitializers([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) { ArrayInitializers = false }); } [Test] public async Task NoDecimalConstants([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) { DecimalConstants = false }); } [Test] public async Task NoExtensionMethods([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp9_0) { ExtensionMethods = false }); } [Test] public async Task NoForEachStatement([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) { ForEachStatement = false, UseEnhancedUsing = false, }); } [Test] public async Task NoLocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1)); } [Test] public async Task NoPropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) { AutomaticEvents = false, AutomaticProperties = false, }); } [Test] public async Task AggressiveScalarReplacementOfAggregates([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp3) { AggressiveScalarReplacementOfAggregates = true }); } [Test] public async Task NoNewOfT([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1)); } async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) { await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); } async Task Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) { var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il"; var csFile = Path.Combine(TestCasePath, testName + ".cs"); var expectedFile = Path.Combine(TestCasePath, testName + ".Expected.cs"); if (!File.Exists(ilFile)) { // re-create .il file if necessary Helpers.CompilerResults output = null; try { output = await Tester.CompileCSharp(csFile, cscOptions).ConfigureAwait(false); await Tester.Disassemble(output.PathToAssembly, ilFile, asmOptions).ConfigureAwait(false); } finally { if (output != null) output.DeleteTempFiles(); } } var executable = await Tester.AssembleIL(ilFile, asmOptions).ConfigureAwait(false); var decompiled = await Tester.DecompileCSharp(executable, decompilerSettings).ConfigureAwait(false); CodeAssert.FilesAreEqual(expectedFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).ToArray()); Tester.RepeatOnIOError(() => File.Delete(decompiled)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Util/BitSetTests.cs ================================================ // Copyright (c) 2017 Daniel Grunwald // // 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 ICSharpCode.Decompiler.Util; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Util { [TestFixture] public class BitSetTests { [Test] public void SetRange() { var bitset = new BitSet(302); bitset.Set(2, 300); Assert.That(!bitset[0]); Assert.That(!bitset[1]); for (int i = 2; i < 300; ++i) { Assert.That(bitset[i]); } Assert.That(!bitset[301]); } [Test] public void ClearRange() { var bitset = new BitSet(300); bitset.Set(0, 300); bitset.Clear(1, 299); Assert.That(bitset[0]); for (int i = 1; i < 299; ++i) { Assert.That(!bitset[i]); } Assert.That(bitset[299]); } [Test] public void AllInRange() { var bitset = new BitSet(300); bitset.Set(1, 299); Assert.That(bitset.All(1, 299)); Assert.That(bitset.All(10, 290)); Assert.That(bitset.All(100, 200)); Assert.That(!bitset.All(0, 200)); Assert.That(!bitset.All(0, 1)); Assert.That(!bitset.All(1, 300)); bitset[200] = false; Assert.That(!bitset.All(1, 299)); } [Test] public void NextBitSet() { var bitset = new BitSet(300); bitset.Set(0); bitset.Set(2); bitset.Set(3); bitset.Set(130); bitset.Set(135); bitset.Set(150); bitset.Set(190); Assert.That(bitset.SetBits(0, 300), Is.EqualTo(new[] { 0, 2, 3, 130, 135, 150, 190 })); Assert.That(bitset.SetBits(1, 5), Is.EqualTo(new[] { 2, 3 })); Assert.That(bitset.SetBits(5, 132), Is.EqualTo(new[] { 130 })); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Util/FileUtilityTests.cs ================================================ // Copyright (c) 2020 Daniel Grunwald // // 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 ICSharpCode.Decompiler.Util; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Util { [TestFixture] public class FileUtilityTests { #region NormalizePath [Test] public void NormalizePath() { Assert.That(FileUtility.NormalizePath(@"c:\temp\project\..\test.txt"), Is.EqualTo(@"c:\temp\test.txt")); Assert.That(FileUtility.NormalizePath(@"c:\temp\project\.\..\test.txt"), Is.EqualTo(@"c:\temp\test.txt")); Assert.That(FileUtility.NormalizePath(@"c:\temp\\test.txt"), Is.EqualTo(@"c:\temp\test.txt")); // normalize double backslash Assert.That(FileUtility.NormalizePath(@"c:\temp\."), Is.EqualTo(@"c:\temp")); Assert.That(FileUtility.NormalizePath(@"c:\temp\subdir\.."), Is.EqualTo(@"c:\temp")); } [Test] public void NormalizePath_DriveRoot() { Assert.That(FileUtility.NormalizePath(@"C:\"), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:/"), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:"), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:/."), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:/.."), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:/./"), Is.EqualTo(@"C:\")); Assert.That(FileUtility.NormalizePath(@"C:/..\"), Is.EqualTo(@"C:\")); } [Test] public void NormalizePath_UNC() { Assert.That(FileUtility.NormalizePath(@"\\server\share"), Is.EqualTo(@"\\server\share")); Assert.That(FileUtility.NormalizePath(@"\\server\share\"), Is.EqualTo(@"\\server\share")); Assert.That(FileUtility.NormalizePath(@"//server/share/"), Is.EqualTo(@"\\server\share")); Assert.That(FileUtility.NormalizePath(@"//server/share/dir/..\otherdir"), Is.EqualTo(@"\\server\share\otherdir")); } [Test] public void NormalizePath_Web() { Assert.That(FileUtility.NormalizePath(@"http://danielgrunwald.de/path/"), Is.EqualTo(@"http://danielgrunwald.de/path/")); Assert.That(FileUtility.NormalizePath(@"browser://http://danielgrunwald.de/wrongpath/../path/"), Is.EqualTo(@"browser://http://danielgrunwald.de/path/")); } [Test] public void NormalizePath_Relative() { Assert.That(FileUtility.NormalizePath(@"..\a\..\b"), Is.EqualTo(@"../b")); Assert.That(FileUtility.NormalizePath(@"."), Is.EqualTo(@".")); Assert.That(FileUtility.NormalizePath(@"a\.."), Is.EqualTo(@".")); } [Test] public void NormalizePath_UnixStyle() { Assert.That(FileUtility.NormalizePath("/"), Is.EqualTo("/")); Assert.That(FileUtility.NormalizePath("/a/b"), Is.EqualTo("/a/b")); Assert.That(FileUtility.NormalizePath("/c/../a/./b"), Is.EqualTo("/a/b")); Assert.That(FileUtility.NormalizePath("/c/../../a/./b"), Is.EqualTo("/a/b")); } #endregion [Test] public void TestIsBaseDirectory() { Assert.That(FileUtility.IsBaseDirectory(@"C:\a", @"C:\A\b\hello")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a", @"C:\a")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a\", @"C:\a\")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a\", @"C:\a")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a", @"C:\a\")); Assert.That(FileUtility.IsBaseDirectory(@"C:\A", @"C:\a")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a", @"C:\A")); Assert.That(FileUtility.IsBaseDirectory(@"C:\a\x\fWufhweoe", @"C:\a\x\fwuFHweoe\a\b\hello")); Assert.That(FileUtility.IsBaseDirectory(@"C:\b\..\A", @"C:\a")); Assert.That(FileUtility.IsBaseDirectory(@"C:\HELLO\..\B\..\a", @"C:\b\..\a")); Assert.That(FileUtility.IsBaseDirectory(@"C:\.\B\..\.\.\a", @"C:\.\.\.\.\.\.\.\a")); Assert.That(!FileUtility.IsBaseDirectory(@"C:\b", @"C:\a\b\hello")); Assert.That(!FileUtility.IsBaseDirectory(@"C:\a\b\hello", @"C:\b")); Assert.That(!FileUtility.IsBaseDirectory(@"C:\a\x\fwufhweoe", @"C:\a\x\fwuFHweoex\a\b\hello")); Assert.That(FileUtility.IsBaseDirectory(@"C:\", @"C:\")); Assert.That(FileUtility.IsBaseDirectory(@"C:\", @"C:\a\b\hello")); Assert.That(!FileUtility.IsBaseDirectory(@"C:\", @"D:\a\b\hello")); } [Test] public void TestIsBaseDirectoryRelative() { Assert.That(FileUtility.IsBaseDirectory(@".", @"a\b")); Assert.That(FileUtility.IsBaseDirectory(@".", @"a")); Assert.That(!FileUtility.IsBaseDirectory(@".", @"c:\")); Assert.That(!FileUtility.IsBaseDirectory(@".", @"/")); } [Test] public void TestIsBaseDirectoryUnixStyle() { Assert.That(FileUtility.IsBaseDirectory(@"/", @"/")); Assert.That(FileUtility.IsBaseDirectory(@"/", @"/a")); Assert.That(FileUtility.IsBaseDirectory(@"/", @"/a/subdir")); } [Test] public void TestIsBaseDirectoryUNC() { Assert.That(FileUtility.IsBaseDirectory(@"\\server\share", @"\\server\share\dir\subdir")); Assert.That(FileUtility.IsBaseDirectory(@"\\server\share", @"\\server\share\dir\subdir")); Assert.That(!FileUtility.IsBaseDirectory(@"\\server2\share", @"\\server\share\dir\subdir")); } [Test] public void TestGetRelativePath() { Assert.That(FileUtility.GetRelativePath(@"C:\hello\.\..\a", @"C:\.\a\blub"), Is.EqualTo(@"blub")); Assert.That(FileUtility.GetRelativePath(@"C:\.\.\.\.\hello", @"C:\.\blub\.\..\.\a\.\blub"), Is.EqualTo(@"..\a\blub")); Assert.That(FileUtility.GetRelativePath(@"C:\.\.\.\.\hello\", @"C:\.\blub\.\..\.\a\.\blub"), Is.EqualTo(@"..\a\blub")); Assert.That(FileUtility.GetRelativePath(@"C:\hello", @"C:\.\hello"), Is.EqualTo(@".")); Assert.That(FileUtility.GetRelativePath(@"C:\", @"C:\"), Is.EqualTo(@".")); Assert.That(FileUtility.GetRelativePath(@"C:\", @"C:\blub"), Is.EqualTo(@"blub")); Assert.That(FileUtility.GetRelativePath(@"C:\", @"D:\"), Is.EqualTo(@"D:\")); Assert.That(FileUtility.GetRelativePath(@"C:\abc", @"D:\def"), Is.EqualTo(@"D:\def")); // casing troubles Assert.That(FileUtility.GetRelativePath(@"C:\hello\.\..\A", @"C:\.\a\blub"), Is.EqualTo(@"blub")); Assert.That(FileUtility.GetRelativePath(@"C:\.\.\.\.\HELlo", @"C:\.\blub\.\..\.\a\.\blub"), Is.EqualTo(@"..\a\blub")); Assert.That(FileUtility.GetRelativePath(@"C:\.\.\.\.\heLLo\A\..", @"C:\.\blub\.\..\.\a\.\blub"), Is.EqualTo(@"..\a\blub")); } [Test] public void RelativeGetRelativePath() { // Relative path Assert.That(FileUtility.GetRelativePath(@".", @"a"), Is.EqualTo(@"a")); Assert.That(FileUtility.GetRelativePath(@"a", @"."), Is.EqualTo(@"..")); Assert.That(FileUtility.GetRelativePath(@"a", @"b"), Is.EqualTo(@"..\b")); Assert.That(FileUtility.GetRelativePath(@"a", @".."), Is.EqualTo(@"..\..")); // Getting a path from an absolute path to a relative path isn't really possible; // so we just keep the existing relative path (don't introduce incorrect '..\'). Assert.That(FileUtility.GetRelativePath(@"C:\abc", @"def"), Is.EqualTo(@"def")); } [Test] public void GetRelativePath_Unix() { Assert.That(FileUtility.GetRelativePath("/", "/a"), Is.EqualTo(@"a")); Assert.That(FileUtility.GetRelativePath("/", "/a/b"), Is.EqualTo(@"a\b")); Assert.That(FileUtility.GetRelativePath("/a", "/a/b"), Is.EqualTo(@"b")); } [Test] public void TestIsEqualFile() { Assert.That(FileUtility.IsEqualFileName(@"C:\.\Hello World.Exe", @"C:\HELLO WOrld.exe")); Assert.That(FileUtility.IsEqualFileName(@"C:\bla\..\a\my.file.is.this", @"C:\gg\..\.\.\.\.\a\..\a\MY.FILE.IS.THIS")); Assert.That(!FileUtility.IsEqualFileName(@"C:\.\Hello World.Exe", @"C:\HELLO_WOrld.exe")); Assert.That(!FileUtility.IsEqualFileName(@"C:\a\my.file.is.this", @"C:\gg\..\.\.\.\.\a\..\b\MY.FILE.IS.THIS")); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Util/IntervalTests.cs ================================================ // Copyright (c) 2014 Daniel Grunwald // // 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 ICSharpCode.Decompiler.Util; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Util { public class IntervalTests { [Test] public void DefaultIsEmpty() { Assert.That(default(Interval).IsEmpty); Assert.That(!default(Interval).Contains(-1)); Assert.That(!default(Interval).Contains(0)); Assert.That(!default(Interval).Contains(1)); } [Test] public void EmptyAt1() { Interval i = new Interval(1, 1); Assert.That(default(Interval).IsEmpty); Assert.That(!default(Interval).Contains(-1)); Assert.That(!default(Interval).Contains(0)); Assert.That(!default(Interval).Contains(1)); Assert.That(!default(Interval).Contains(2)); } [Test] public void OneToThree() { Interval i = new Interval(1, 3); Assert.That(!i.IsEmpty); Assert.That(!i.Contains(0)); Assert.That(i.Contains(1)); Assert.That(i.Contains(2)); Assert.That(!i.Contains(3)); } [Test] public void FullInterval() { Interval full = new Interval(int.MinValue, int.MinValue); Assert.That(!full.IsEmpty); Assert.That(full.Contains(int.MinValue)); Assert.That(full.Contains(0)); Assert.That(full.Contains(int.MaxValue)); } [Test] public void NonNegativeIntegers() { Interval i = new Interval(0, int.MinValue); Assert.That(!i.IsEmpty); Assert.That(i.Contains(0)); Assert.That(i.Contains(1000)); Assert.That(i.Contains(int.MaxValue)); Assert.That(!i.Contains(-1)); Assert.That(!i.Contains(-1000)); Assert.That(!i.Contains(int.MinValue)); } [Test] public void Intersection() { Interval empty = new Interval(0, 0); Interval emptyAtOne = new Interval(0, 0); Interval zero = new Interval(0, 1); Interval full = new Interval(int.MinValue, int.MinValue); Interval nonneg = new Interval(0, int.MinValue); Interval nonpos = new Interval(int.MinValue, 1); Interval maxval = new Interval(int.MaxValue, int.MinValue); Assert.That(full.Intersect(nonneg), Is.EqualTo(nonneg)); Assert.That(nonneg.Intersect(full), Is.EqualTo(nonneg)); Assert.That(nonneg.Intersect(zero), Is.EqualTo(zero)); Assert.That(nonneg.Intersect(nonpos), Is.EqualTo(zero)); Assert.That(nonneg.Intersect(maxval), Is.EqualTo(maxval)); Assert.That(nonpos.Intersect(maxval), Is.EqualTo(empty)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/Util/LongSetTests.cs ================================================ // Copyright (c) 2014 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 // 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.Collections.Immutable; using System.Linq; using ICSharpCode.Decompiler.Util; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Util { [TestFixture] public class LongSetTests { [Test] public void UpperBound() { var longSet = new LongSet(new[] { new LongInterval(1, 5), new LongInterval(6, 7) }.ToImmutableArray()); Assert.That(longSet.upper_bound(0), Is.EqualTo(0)); for (int i = 1; i <= 5; i++) Assert.That(longSet.upper_bound(i), Is.EqualTo(1)); for (int i = 6; i <= 10; i++) Assert.That(longSet.upper_bound(i), Is.EqualTo(2)); } [Test] public void UniverseContainsAll() { Assert.That(LongSet.Universe.Contains(long.MinValue)); Assert.That(LongSet.Universe.Contains(1)); Assert.That(LongSet.Universe.Contains(long.MaxValue)); Assert.That(!LongSet.Universe.IsEmpty); } [Test] public void IntersectUniverse() { Assert.That(LongSet.Universe.IntersectWith(LongSet.Universe), Is.EqualTo(LongSet.Universe)); Assert.That(LongSet.Universe.IntersectWith(LongSet.Empty), Is.EqualTo(LongSet.Empty)); Assert.That(LongSet.Universe.IntersectWith(new LongSet(long.MaxValue)), Is.EqualTo(new LongSet(long.MaxValue))); var longSet = new LongSet(new[] { new LongInterval(1, 5), new LongInterval(6, 7) }.ToImmutableArray()); Assert.That(longSet.IntersectWith(LongSet.Universe), Is.EqualTo(longSet)); } [Test] public void UnionUniverse() { Assert.That(LongSet.Universe.UnionWith(LongSet.Universe), Is.EqualTo(LongSet.Universe)); Assert.That(LongSet.Universe.UnionWith(LongSet.Empty), Is.EqualTo(LongSet.Universe)); Assert.That(LongSet.Universe.UnionWith(new LongSet(long.MaxValue)), Is.EqualTo(LongSet.Universe)); var longSet = new LongSet(new[] { new LongInterval(1, 5), new LongInterval(6, 7) }.ToImmutableArray()); Assert.That(longSet.UnionWith(LongSet.Universe), Is.EqualTo(LongSet.Universe)); } [Test] public void ExceptWithUniverse() { Assert.That(LongSet.Universe.ExceptWith(LongSet.Empty), Is.EqualTo(LongSet.Universe)); Assert.That(LongSet.Universe.ExceptWith(LongSet.Universe), Is.EqualTo(LongSet.Empty)); Assert.That(LongSet.Empty.ExceptWith(LongSet.Universe), Is.EqualTo(LongSet.Empty)); Assert.That(LongSet.Empty.ExceptWith(LongSet.Empty), Is.EqualTo(LongSet.Empty)); } [Test] public void UnionWith() { Assert.That(new LongSet(0).UnionWith(new LongSet(1)), Is.EqualTo(new LongSet(new LongInterval(0, 2)))); Assert.That(new LongSet(0).Invert().UnionWith(new LongSet(0)), Is.EqualTo(LongSet.Universe)); } [Test] public void AddTo() { Assert.That(new LongSet(0).AddOffset(1), Is.EqualTo(new LongSet(1))); Assert.That(new LongSet(long.MaxValue).AddOffset(1), Is.EqualTo(new LongSet(long.MinValue))); TestAddTo(new LongSet(new LongInterval(-10, 10)), 5); TestAddTo(new LongSet(new LongInterval(-10, 10)), long.MaxValue); Assert.That(new LongSet(0).Invert().AddOffset(10), Is.EqualTo(new LongSet(10).Invert())); Assert.That(new LongSet(30).Invert().AddOffset(-10), Is.EqualTo(new LongSet(20).Invert())); } void TestAddTo(LongSet input, long constant) { Assert.That( input.AddOffset(constant).Values.ToList(), Is.EqualTo(input.Values.Select(e => unchecked(e + constant)).OrderBy(e => e).ToList())); } [Test] public void Values() { Assert.That(!LongSet.Empty.Values.Any()); Assert.That(LongSet.Universe.Values.Any()); Assert.That(new LongSet(LongInterval.Inclusive(1, 3)).Values.ToArray(), Is.EqualTo(new[] { 1, 2, 3 })); } [Test] public void ValueCount() { Assert.That(LongSet.Empty.Count(), Is.EqualTo(0)); Assert.That(new LongSet(3).Invert().Count(), Is.EqualTo(ulong.MaxValue)); Assert.That(LongSet.Universe.Count(), Is.EqualTo(ulong.MaxValue)); Assert.That(new LongSet(LongInterval.Inclusive(-1, long.MaxValue)).Count(), Is.EqualTo(long.MaxValue + 2ul)); } } } ================================================ FILE: ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs ================================================ // Copyright (c) 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 // 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.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ICSharpCode.Decompiler.Tests.Helpers; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture, Parallelizable(ParallelScope.All)] public class VBPrettyTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/VBPretty"; [Test] public void AllFilesHaveTests() { var testNames = typeof(VBPrettyTestRunner).GetMethods() .Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any()) .Select(m => m.Name) .ToArray(); foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { if (file.Extension.Equals(".vb", StringComparison.OrdinalIgnoreCase)) { var testName = file.Name.Split('.')[0]; Assert.That(testNames, Has.Member(testName)); Assert.That(File.Exists(Path.Combine(TestCasePath, testName + ".cs"))); } } } static readonly CompilerOptions[] defaultOptions = { CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; static readonly CompilerOptions[] roslynOnlyOptions = { CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40, CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40, CompilerOptions.UseRoslyn1_3_2, CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2, CompilerOptions.UseRoslyn2_10_0, CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0, CompilerOptions.UseRoslynLatest, CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest, }; [Test] public async Task Async([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] // TODO: legacy VB compound assign public async Task VBCompoundAssign([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task Select([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task Issue1906([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task Issue2192([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task VBPropertiesTest([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task VBAutomaticEvents([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task VBNonGenericForEach([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } [Test] public async Task YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions options) { await Run(options: options | CompilerOptions.Library); } async Task Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var csFile = Path.Combine(TestCasePath, testName + ".cs"); var exeFile = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(options) + ".exe"); if (options.HasFlag(CompilerOptions.Library)) { exeFile = Path.ChangeExtension(exeFile, ".dll"); } var executable = await Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile).ConfigureAwait(false); var decompiled = await Tester.DecompileCSharp(executable.PathToAssembly, settings ?? new DecompilerSettings { FileScopedNamespaces = false }).ConfigureAwait(false); CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(options).ToArray()); Tester.RepeatOnIOError(() => File.Delete(decompiled)); } } } ================================================ FILE: ICSharpCode.ILSpyCmd/AsContainer/Dockerfile ================================================ FROM mcr.microsoft.com/dotnet/sdk:8.0 RUN useradd -m -s /bin/bash ilspy USER ilspy WORKDIR /home/ilspy RUN dotnet tool install -g ilspycmd --version 9.1.0.7988 RUN echo 'export PATH="$PATH:/home/ilspy/.dotnet/tools/"' >> /home/ilspy/.bashrc ENTRYPOINT [ "/bin/bash" ] ================================================ FILE: ICSharpCode.ILSpyCmd/AsContainer/DockerfileForAutomation ================================================ FROM mcr.microsoft.com/dotnet/sdk:8.0 RUN useradd -m -s /bin/bash ilspy USER ilspy WORKDIR /home/ilspy RUN dotnet tool install -g ilspycmd --version 9.1.0.7988 RUN echo 'export PATH="$PATH:/home/ilspy/.dotnet/tools/"' >> /home/ilspy/.bashrc ENTRYPOINT [ "/bin/bash", "-l", "-c" ] ================================================ FILE: ICSharpCode.ILSpyCmd/AsContainer/README.md ================================================ # ILSpyCmd in Docker Inspired by https://trustedsec.com/blog/hunting-deserialization-vulnerabilities-with-claude (and thus https://github.com/berdav/ilspycmd-docker) ## Building the Image There are two dockerfiles available - one for interactive use of ilspycmd (exploration), and the other for driving it from the outside (automation) ### Interactive `docker build -t ilspycmd:91interactive . -f Dockerfile` `docker run --rm -it -v ./:/docker ilspycmd:91interactive` ### Automation `docker build -t ilspycmd:91forautomation . -f DockerfileForAutomation` `docker run --rm -v ./:/infolder -v ./out:/outfolder ilspycmd:91forautomation "/home/ilspy/.dotnet/tools/ilspycmd -p -o /outfolder /infolder/sample.dll"` ================================================ FILE: ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs ================================================ using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using NuGet.Common; using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Versioning; namespace ICSharpCode.ILSpyCmd { internal record PackageCheckResult(NuGetVersion RunningVersion, NuGetVersion LatestVersion, bool UpdateRecommendation); // Idea from https://github.com/ErikEJ/EFCorePowerTools/blob/master/src/GUI/efcpt/Services/PackageService.cs internal static class DotNetToolUpdateChecker { static NuGetVersion CurrentPackageVersion() { return new NuGetVersion(Assembly.GetEntryAssembly()!.GetCustomAttribute()! .InformationalVersion); } public static async Task CheckForPackageUpdateAsync(string packageId) { try { using var cache = new SourceCacheContext(); var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); var resource = await repository.GetResourceAsync().ConfigureAwait(false); var versions = await resource.GetAllVersionsAsync( packageId, cache, new NullLogger(), CancellationToken.None).ConfigureAwait(false); var latestVersion = versions.Where(v => v.Release == "").MaxBy(v => v); var runningVersion = CurrentPackageVersion(); int comparisonResult = latestVersion.CompareTo(runningVersion, VersionComparison.Version); return new PackageCheckResult(runningVersion, latestVersion, comparisonResult > 0); } #pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. catch (Exception) { } #pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. return null; } } } ================================================ FILE: ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj ================================================ Exe net10.0 true true true true true en-US False False False ilspycmd ilspycmd Command-line decompiler using the ILSpy decompilation engine README.md 8.0.0.0-noversion Copyright 2011-$([System.DateTime]::Now.Year) AlphaSierraPapa https://github.com/icsharpcode/ILSpy/ MIT ILSpyCmdNuGetPackageIcon.png https://github.com/icsharpcode/ILSpy/ ic#code true ILSpy Team true true false NU1605 ================================================ FILE: ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.IO.Compression; using System.IO.MemoryMappedFiles; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer; using ICSharpCode.ILSpyX.PdbProvider; using McMaster.Extensions.CommandLineUtils; using Microsoft.Extensions.Hosting; namespace ICSharpCode.ILSpyCmd { [Command(Name = "ilspycmd", Description = "dotnet tool for decompiling .NET assemblies and generating portable PDBs", ExtendedHelpText = @" Remarks: -o is valid with every option and required when using -p. Examples: Decompile assembly to console out. ilspycmd sample.dll Decompile assembly to destination directory (single C# file). ilspycmd -o c:\decompiled sample.dll Decompile assembly to destination directory, create a project file, one source file per type. ilspycmd -p -o c:\decompiled sample.dll Decompile assembly to destination directory, create a project file, one source file per type, into nicely nested directories. ilspycmd --nested-directories -p -o c:\decompiled sample.dll Generate a HTML diagrammer containing all type info into a folder next to the input assembly ilspycmd sample.dll --generate-diagrammer Generate a HTML diagrammer containing filtered type info into a custom output folder (including types in the LightJson namespace while excluding types in nested LightJson.Serialization namespace) ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+ ")] [HelpOption("-h|--help")] [ProjectOptionRequiresOutputDirectoryValidation] [VersionOptionFromMember("-v|--version", Description = "Show version of ICSharpCode.Decompiler used.", MemberName = nameof(DecompilerVersion))] class ILSpyCmdProgram { // https://natemcmaster.github.io/CommandLineUtils/docs/advanced/generic-host.html // https://github.com/natemcmaster/CommandLineUtils/blob/main/docs/samples/dependency-injection/generic-host/Program.cs public static Task Main(string[] args) => new HostBuilder().RunCommandLineApplicationAsync(args); [FilesExist] [Required] [Argument(0, "Assembly file name(s)", "The list of assemblies that is being decompiled. This argument is mandatory.")] public string[] InputAssemblyNames { get; } [Option("-o|--outputdir ", "The output directory, if omitted decompiler output is written to standard out.", CommandOptionType.SingleValue)] public string OutputDirectory { get; } [Option("-p|--project", "Decompile assembly as compilable project. This requires the output directory option.", CommandOptionType.NoValue)] public bool CreateCompilableProjectFlag { get; } [Option("-t|--type ", "The fully qualified name of the type to decompile.", CommandOptionType.SingleValue)] public string TypeName { get; } [Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)] public bool ShowILCodeFlag { get; } [Option("--il-sequence-points", "Show IL with sequence points. Implies -il.", CommandOptionType.NoValue)] public bool ShowILSequencePointsFlag { get; } [Option("-genpdb|--generate-pdb", "Generate PDB.", CommandOptionType.NoValue)] public bool CreateDebugInfoFlag { get; } [FileExistsOrNull] [Option("-usepdb|--use-varnames-from-pdb", "Use variable names from PDB.", CommandOptionType.SingleOrNoValue)] public (bool IsSet, string Value) InputPDBFile { get; } [Option("-l|--list ", "Lists all entities of the specified type(s). Valid types: c(lass), i(nterface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)] public string[] EntityTypes { get; } = Array.Empty(); public string DecompilerVersion => "ilspycmd: " + typeof(ILSpyCmdProgram).Assembly.GetName().Version.ToString() + Environment.NewLine + "ICSharpCode.Decompiler: " + typeof(FullTypeName).Assembly.GetName().Version.ToString(); [Option("-lv|--languageversion ", "C# Language version: CSharp1, CSharp2, CSharp3, " + "CSharp4, CSharp5, CSharp6, CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, " + "CSharp10_0, CSharp11_0, CSharp12_0, CSharp13_0, Preview or Latest", CommandOptionType.SingleValue)] public LanguageVersion LanguageVersion { get; } = LanguageVersion.Latest; [FileExists] [Option("--ilspy-settingsfile ", "Path to an ILSpy settings file.", CommandOptionType.SingleValue)] public string ILSpySettingsFile { get; } [Option("-ds|--decompiler-setting =", "Set a decompiler setting. Use multiple times to set multiple settings.", CommandOptionType.MultipleValue)] public string[] DecompilerSettingOverrides { get; set; } = Array.Empty(); [DirectoryExists] [Option("-r|--referencepath ", "Path to a directory containing dependencies of the assembly that is being decompiled.", CommandOptionType.MultipleValue)] public string[] ReferencePaths { get; } [Option("--no-dead-code", "Remove dead code.", CommandOptionType.NoValue)] public bool RemoveDeadCode { get; } [Option("--no-dead-stores", "Remove dead stores.", CommandOptionType.NoValue)] public bool RemoveDeadStores { get; } [Option("-d|--dump-package", "Dump package assemblies into a folder. This requires the output directory option.", CommandOptionType.NoValue)] public bool DumpPackageFlag { get; } [Option("--nested-directories", "Use nested directories for namespaces.", CommandOptionType.NoValue)] public bool NestedDirectories { get; } [Option("--disable-updatecheck", "If using ilspycmd in a tight loop or fully automated scenario, you might want to disable the automatic update check.", CommandOptionType.NoValue)] public bool DisableUpdateCheck { get; } #region MermaidDiagrammer options // reused or quoted commands private const string generateDiagrammerCmd = "--generate-diagrammer", exclude = generateDiagrammerCmd + "-exclude", include = generateDiagrammerCmd + "-include"; [Option(generateDiagrammerCmd, "Generates an interactive HTML diagrammer app from selected types in the target assembly" + " - to the --outputdir or in a 'diagrammer' folder next to to the assembly by default.", CommandOptionType.NoValue)] public bool GenerateDiagrammer { get; } [Option(include, "An optional regular expression matching Type.FullName used to whitelist types to include in the generated diagrammer.", CommandOptionType.SingleValue)] public string Include { get; set; } [Option(exclude, "An optional regular expression matching Type.FullName used to blacklist types to exclude from the generated diagrammer.", CommandOptionType.SingleValue)] public string Exclude { get; set; } [Option(generateDiagrammerCmd + "-report-excluded", "Outputs a report of types excluded from the generated diagrammer" + $" - whether by default because compiler-generated, explicitly by '{exclude}' or implicitly by '{include}'." + " You may find this useful to develop and debug your regular expressions.", CommandOptionType.NoValue)] public bool ReportExcludedTypes { get; set; } [Option(generateDiagrammerCmd + "-docs", "The path or file:// URI of the XML file containing the target assembly's documentation comments." + " You only need to set this if a) you want your diagrams annotated with them and b) the file name differs from that of the assmbly." + " To enable XML documentation output for your assmbly, see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output", CommandOptionType.SingleValue)] public string XmlDocs { get; set; } /// [Option(generateDiagrammerCmd + "-strip-namespaces", "Optional space-separated namespace names that are removed for brevity from XML documentation comments." + " Note that the order matters: e.g. replace 'System.Collections' before 'System' to remove both of them completely.", CommandOptionType.MultipleValue)] public string[] StrippedNamespaces { get; set; } [Option(generateDiagrammerCmd + "-json-only", "Whether to generate a model.json file instead of baking it into the HTML template." + " This is useful for the HTML/JS/CSS development loop.", CommandOptionType.NoValue, ShowInHelpText = false)] // developer option, output is really only useful in combination with the corresponding task in html/gulpfile.js public bool JsonOnly { get; set; } #endregion private readonly IHostEnvironment _env; public ILSpyCmdProgram(IHostEnvironment env) { _env = env; } private async Task OnExecuteAsync(CommandLineApplication app) { Task updateCheckTask = null; if (!DisableUpdateCheck) { updateCheckTask = DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); } TextWriter output = System.Console.Out; string outputDirectory = ResolveOutputDirectory(OutputDirectory); if (outputDirectory != null) { Directory.CreateDirectory(outputDirectory); } try { if (CreateCompilableProjectFlag) { if (InputAssemblyNames.Length == 1) { string projectFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(InputAssemblyNames[0]) + ".csproj"); DecompileAsProject(InputAssemblyNames[0], projectFileName); return 0; } var projects = new List(); foreach (var file in InputAssemblyNames) { string projectFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(file), Path.GetFileNameWithoutExtension(file) + ".csproj"); Directory.CreateDirectory(Path.GetDirectoryName(projectFileName)); ProjectId projectId = DecompileAsProject(file, projectFileName); projects.Add(new ProjectItem(projectFileName, projectId.PlatformName, projectId.Guid, projectId.TypeGuid)); } SolutionCreator.WriteSolutionFile(Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(outputDirectory) + ".sln"), projects); return 0; } else if (GenerateDiagrammer) { foreach (var file in InputAssemblyNames) { var command = new GenerateHtmlDiagrammer { Assembly = file, OutputFolder = OutputDirectory, Include = Include, Exclude = Exclude, ReportExcludedTypes = ReportExcludedTypes, JsonOnly = JsonOnly, XmlDocs = XmlDocs, StrippedNamespaces = StrippedNamespaces }; command.Run(); } return 0; } else { foreach (var file in InputAssemblyNames) { int result = PerformPerFileAction(file); if (result != 0) return result; } return 0; } } catch (Exception ex) { app.Error.WriteLine(ex.ToString()); return ProgramExitCodes.EX_SOFTWARE; } finally { output.Close(); if (null != updateCheckTask) { var checkResult = await updateCheckTask; if (null != checkResult && checkResult.UpdateRecommendation) { Console.WriteLine("You are not using the latest version of the tool, please update."); Console.WriteLine($"Latest version is '{checkResult.LatestVersion}' (yours is '{checkResult.RunningVersion}')"); } } } int PerformPerFileAction(string fileName) { if (EntityTypes.Any()) { var values = EntityTypes.SelectMany(v => v.Split(',', ';')).ToArray(); HashSet kinds = TypesParser.ParseSelection(values); if (outputDirectory != null) { string outputName = Path.GetFileNameWithoutExtension(fileName); output = File.CreateText(Path.Combine(outputDirectory, outputName) + ".list.txt"); } return ListContent(fileName, output, kinds); } else if (ShowILCodeFlag || ShowILSequencePointsFlag) { if (outputDirectory != null) { string outputName = Path.GetFileNameWithoutExtension(fileName); output = File.CreateText(Path.Combine(outputDirectory, outputName) + ".il"); } return ShowIL(fileName, output); } else if (CreateDebugInfoFlag) { string pdbFileName = null; if (outputDirectory != null) { string outputName = Path.GetFileNameWithoutExtension(fileName); pdbFileName = Path.Combine(outputDirectory, outputName) + ".pdb"; } else { pdbFileName = Path.ChangeExtension(fileName, ".pdb"); } return GeneratePdbForAssembly(fileName, pdbFileName, app); } else if (DumpPackageFlag) { return DumpPackageAssemblies(fileName, outputDirectory, app); } else { if (outputDirectory != null) { string outputName = Path.GetFileNameWithoutExtension(fileName); output = File.CreateText(Path.Combine(outputDirectory, (string.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs")); } return Decompile(fileName, output, TypeName); } } } private static string ResolveOutputDirectory(string outputDirectory) { // path is not set if (string.IsNullOrWhiteSpace(outputDirectory)) return null; // resolve relative path, backreferences ('.' and '..') and other // platform-specific path elements, like '~'. return Path.GetFullPath(outputDirectory); } DecompilerSettings GetSettings(PEFile module) { DecompilerSettings decompilerSettings = null; if (ILSpySettingsFile != null) { try { ILSpyX.Settings.ILSpySettings.SettingsFilePathProvider = new ILSpyX.Settings.DefaultSettingsFilePathProvider(ILSpySettingsFile); var settingsService = new ILSpyX.Settings.SettingsServiceBase(ILSpyX.Settings.ILSpySettings.Load()); decompilerSettings = settingsService.GetSettings(); } catch (Exception ex) { Console.Error.WriteLine($"Error loading ILSpy settings file '{ILSpySettingsFile}': {ex.Message}"); } } if (decompilerSettings == null) { decompilerSettings = new DecompilerSettings(LanguageVersion) { ThrowOnAssemblyResolveErrors = false, RemoveDeadCode = RemoveDeadCode, RemoveDeadStores = RemoveDeadStores, UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module), UseNestedDirectoriesForNamespaces = NestedDirectories, }; } if (DecompilerSettingOverrides is { Length: > 0 }) { foreach (var entry in DecompilerSettingOverrides) { int equals = entry.IndexOf('='); if (equals <= 0) { Console.Error.WriteLine($"Decompiler setting '{entry}' is invalid; use '='"); continue; } string name = entry[..equals].Trim(); string value = entry[(equals + 1)..].Trim(); if (!ILSpyX.Settings.DecompilerSettings.IsKnownOption(name, out var property)) { Console.Error.WriteLine($"Decompiler setting '{name}' is unknown."); continue; } object typedValue; try { typedValue = Convert.ChangeType(value, property.PropertyType); } catch (Exception) { Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'."); continue; } if (typedValue == null && property.PropertyType.IsValueType) { Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'."); continue; } property.SetValue(decompilerSettings, typedValue); } } return decompilerSettings; } CSharpDecompiler GetDecompiler(string assemblyFileName) { var module = new PEFile(assemblyFileName); var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId()); foreach (var path in (ReferencePaths ?? Array.Empty())) { resolver.AddSearchDirectory(path); } return new CSharpDecompiler(assemblyFileName, resolver, GetSettings(module)) { DebugInfoProvider = TryLoadPDB(module) }; } int ListContent(string assemblyFileName, TextWriter output, ISet kinds) { CSharpDecompiler decompiler = GetDecompiler(assemblyFileName); foreach (var type in decompiler.TypeSystem.MainModule.TypeDefinitions) { if (!kinds.Contains(type.Kind)) continue; output.WriteLine($"{type.Kind} {type.FullName}"); } return 0; } int ShowIL(string assemblyFileName, TextWriter output) { var module = new PEFile(assemblyFileName); output.WriteLine($"// IL code: {module.Name}"); var disassembler = new ReflectionDisassembler(new PlainTextOutput(output), CancellationToken.None) { DebugInfo = TryLoadPDB(module), ShowSequencePoints = ShowILSequencePointsFlag, }; disassembler.WriteModuleContents(module); return 0; } ProjectId DecompileAsProject(string assemblyFileName, string projectFileName) { var module = new PEFile(assemblyFileName); var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId()); foreach (var path in (ReferencePaths ?? Array.Empty())) { resolver.AddSearchDirectory(path); } var decompiler = new WholeProjectDecompiler(GetSettings(module), resolver, null, resolver, TryLoadPDB(module)); using (var projectFileWriter = new StreamWriter(File.OpenWrite(projectFileName))) return decompiler.DecompileProject(module, Path.GetDirectoryName(projectFileName), projectFileWriter); } int Decompile(string assemblyFileName, TextWriter output, string typeName = null) { CSharpDecompiler decompiler = GetDecompiler(assemblyFileName); if (typeName == null) { output.Write(decompiler.DecompileWholeModuleAsString()); } else { var name = new FullTypeName(typeName); output.Write(decompiler.DecompileTypeAsString(name)); } return 0; } int GeneratePdbForAssembly(string assemblyFileName, string pdbFileName, CommandLineApplication app) { var module = new PEFile(assemblyFileName, new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read), PEStreamOptions.PrefetchEntireImage, metadataOptions: MetadataReaderOptions.None); if (!PortablePdbWriter.HasCodeViewDebugDirectoryEntry(module)) { app.Error.WriteLine($"Cannot create PDB file for {assemblyFileName}, because it does not contain a PE Debug Directory Entry of type 'CodeView'."); return ProgramExitCodes.EX_DATAERR; } using (FileStream stream = new FileStream(pdbFileName, FileMode.Create, FileAccess.Write)) { var decompiler = GetDecompiler(assemblyFileName); PortablePdbWriter.WritePdb(module, decompiler, GetSettings(module), stream); } return 0; } int DumpPackageAssemblies(string packageFileName, string outputDirectory, CommandLineApplication app) { using (var memoryMappedPackage = MemoryMappedFile.CreateFromFile(packageFileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read)) { using (var packageView = memoryMappedPackage.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read)) { if (!SingleFileBundle.IsBundle(packageView, out long bundleHeaderOffset)) { app.Error.WriteLine($"Cannot dump assembiles for {packageFileName}, because it is not a single file bundle."); return ProgramExitCodes.EX_DATAERR; } var manifest = SingleFileBundle.ReadManifest(packageView, bundleHeaderOffset); foreach (var entry in manifest.Entries) { Stream contents; if (entry.RelativePath.Replace('\\', '/').Contains("../", StringComparison.Ordinal) || Path.IsPathRooted(entry.RelativePath)) { app.Error.WriteLine($"Skipping single-file entry '{entry.RelativePath}' because it might refer to a location outside of the bundle output directory."); continue; } if (entry.CompressedSize == 0) { contents = new UnmanagedMemoryStream(packageView.SafeMemoryMappedViewHandle, entry.Offset, entry.Size); } else { Stream compressedStream = new UnmanagedMemoryStream(packageView.SafeMemoryMappedViewHandle, entry.Offset, entry.CompressedSize); Stream decompressedStream = new MemoryStream((int)entry.Size); using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) { deflateStream.CopyTo(decompressedStream); } if (decompressedStream.Length != entry.Size) { app.Error.WriteLine($"Corrupted single-file entry '{entry.RelativePath}'. Declared decompressed size '{entry.Size}' is not the same as actual decompressed size '{decompressedStream.Length}'."); return ProgramExitCodes.EX_DATAERR; } decompressedStream.Seek(0, SeekOrigin.Begin); contents = decompressedStream; } string target = Path.Combine(outputDirectory, entry.RelativePath); Directory.CreateDirectory(Path.GetDirectoryName(target)); using (var fileStream = File.Create(target)) { contents.CopyTo(fileStream); } } } } return 0; } IDebugInfoProvider TryLoadPDB(PEFile module) { if (InputPDBFile.IsSet) { if (InputPDBFile.Value == null) return DebugInfoUtils.LoadSymbols(module); return DebugInfoUtils.FromFile(module, InputPDBFile.Value); } return null; } } } ================================================ FILE: ICSharpCode.ILSpyCmd/ProgramExitCodes.cs ================================================ // ReSharper disable InconsistentNaming namespace ICSharpCode.ILSpyCmd { public class ProgramExitCodes { // https://www.freebsd.org/cgi/man.cgi?query=sysexits public const int EX_USAGE = 64; public const int EX_DATAERR = 65; public const int EX_NOINPUT = 66; public const int EX_SOFTWARE = 70; } } ================================================ FILE: ICSharpCode.ILSpyCmd/Properties/AssemblyInfo.cs ================================================ #region Using directives using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; #endregion // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] [assembly: AssemblyVersion(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision)] [assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithCommitHash)] [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] ================================================ FILE: ICSharpCode.ILSpyCmd/Properties/launchSettings.json ================================================ { "profiles": { "no args": { "commandName": "Project", "commandLineArgs": "" }, "print help": { "commandName": "Project", "commandLineArgs": "--help" }, "generate diagrammer": { "commandName": "Project", // containing all types // full diagrammer (~6.3 Mb!) //"commandLineArgs": "ICSharpCode.Decompiler.dll --generate-diagrammer" // including types in LightJson namespace while excluding types in nested LightJson.Serialization namespace, matched by what returns System.Type.FullName //"commandLineArgs": "ICSharpCode.Decompiler.dll --generate-diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+" // including types in Decompiler.TypeSystem namespace while excluding types in nested Decompiler.TypeSystem.Implementation namespace "commandLineArgs": "ICSharpCode.Decompiler.dll --generate-diagrammer --generate-diagrammer-include Decompiler\\.TypeSystem\\..+ --generate-diagrammer-exclude Decompiler\\.TypeSystem\\.Implementation\\..+" }, "generate diagrammer model.json": { "commandName": "Project", "commandLineArgs": "ICSharpCode.Decompiler.dll --generate-diagrammer --generate-diagrammer-json-only" } } } ================================================ FILE: ICSharpCode.ILSpyCmd/README.md ================================================ # ilspycmd .NET Tool To install: ``` dotnet tool install --global ilspycmd ``` Help output (`ilspycmd --help`): ``` ilspycmd: 9.0.0.7847 ICSharpCode.Decompiler: 9.0.0.7847 dotnet tool for decompiling .NET assemblies and generating portable PDBs Usage: ilspycmd [options] Arguments: Assembly file name(s) The list of assemblies that is being decompiled. This argument is mandatory. Options: -v|--version Show version of ICSharpCode.Decompiler used. -h|--help Show help information. -o|--outputdir The output directory, if omitted decompiler output is written to standard out. -p|--project Decompile assembly as compilable project. This requires the output directory option. -t|--type The fully qualified name of the type to decompile. -il|--ilcode Show IL code. --il-sequence-points Show IL with sequence points. Implies -il. -genpdb|--generate-pdb Generate PDB. -usepdb|--use-varnames-from-pdb Use variable names from PDB. -l|--list Lists all entities of the specified type(s). Valid types: c(lass), i(nterface), s(truct), d(elegate), e(num) -lv|--languageversion C# Language version: CSharp1, CSharp2, CSharp3, CSharp4, CSharp5, CSharp6, CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, CSharp10_0, Preview or Latest Allowed values are: CSharp1, CSharp2, CSharp3, CSharp4, CSharp5, CSharp6, CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, CSharp10_0, CSharp11_0, Preview, CSharp12_0, Latest. Default value is: Latest. -r|--referencepath Path to a directory containing dependencies of the assembly that is being decompiled. --no-dead-code Remove dead code. --no-dead-stores Remove dead stores. -d|--dump-package Dump package assemblies into a folder. This requires the output directory option. --nested-directories Use nested directories for namespaces. --disable-updatecheck If using ilspycmd in a tight loop or fully automated scenario, you might want to disable the automatic update check. --generate-diagrammer Generates an interactive HTML diagrammer app from selected types in the target assembly - to the --outputdir or in a 'diagrammer' folder next to to the assembly by default. --generate-diagrammer-include An optional regular expression matching Type.FullName used to whitelist types to include in the generated diagrammer. --generate-diagrammer-exclude An optional regular expression matching Type.FullName used to blacklist types to exclude from the generated diagrammer. --generate-diagrammer-report-excluded Outputs a report of types excluded from the generated diagrammer - whether by default because compiler-generated, explicitly by '--generate-diagrammer-exclude' or implicitly by '--generate-diagrammer-include'. You may find this useful to develop and debug your regular expressions. --generate-diagrammer-docs The path or file:// URI of the XML file containing the target assembly's documentation comments. You only need to set this if a) you want your diagrams annotated with them and b) the file name differs from that of the assmbly. To enable XML documentation output for your assmbly, see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output --generate-diagrammer-strip-namespaces Optional space-separated namespace names that are removed for brevity from XML documentation comments. Note that the order matters: e.g. replace 'System.Collections' before 'System' to remove both of them completely. Remarks: -o is valid with every option and required when using -p. Examples: Decompile assembly to console out. ilspycmd sample.dll Decompile assembly to destination directory (single C# file). ilspycmd -o c:\decompiled sample.dll Decompile assembly to destination directory, create a project file, one source file per type. ilspycmd -p -o c:\decompiled sample.dll Decompile assembly to destination directory, create a project file, one source file per type, into nicely nested directories. ilspycmd --nested-directories -p -o c:\decompiled sample.dll Generate a HTML diagrammer containing all type info into a folder next to the input assembly ilspycmd sample.dll --generate-diagrammer Generate a HTML diagrammer containing filtered type info into a custom output folder (including types in the LightJson namespace while excluding types in nested LightJson.Serialization namespace) ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+ ``` ## Generate HTML diagrammers Once you have an output folder in mind, you can adopt either of the following strategies to generate a HTML diagrammer from a .Net assembly using the console app. ### Manually before use **Create the output folder** in your location of choice and inside it **a new shell script**. Using the CMD shell in a Windows environment for example, you'd create a `regenerate.cmd` looking somewhat like this:
..\..\path\to\ilspycmd.exe ..\path\to\your\assembly.dll --generate-diagrammer --outputdir .
With this script in place, run it to (re-)generate the HTML diagrammer at your leisure. Note that `--outputdir .` directs the output to the current directory. ### Automatically If you want to deploy an up-to-date HTML diagrammer as part of your live documentation, you'll want to automate its regeneration to keep it in sync with your code base. For example, you might like to share the diagrammer on a web server or - in general - with users who cannot or may not regenerate it; lacking either access to the ilspycmd console app or permission to use it. In such cases, you can dangle the regeneration off the end of either your build or deployment pipeline. Note that the macros used here apply to [MSBuild](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild) for [Visual Studio](https://learn.microsoft.com/en-us/visualstudio/ide/reference/pre-build-event-post-build-event-command-line-dialog-box) and your mileage may vary with VS for Mac or VS Code. #### After building To regenerate the HTML diagrammer from your output assembly after building, add something like the following to your project file. Note that the `Condition` here is optional and configures this step to only run after `Release` builds. ```xml ``` #### After publishing If you'd rather regenerate the diagram after publishing instead of building, all you have to do is change the `AfterTargets` to `Publish`. Note that the `Target` `Name` doesn't matter here and that the diagrammer is generated into a folder in the `PublishDir` instead of the `ProjectDir`. ```xml ``` ### Usage tips **Compiler-generated** types and their nested types are **excluded by default**. Consider sussing out **big source assemblies** using [ILSpy](https://github.com/icsharpcode/ILSpy) first to get an idea about which subdomains to include in your diagrammers. Otherwise you may experience long build times and large file sizes for the diagrammer as well as a looong type selection opening it. At some point, mermaid may refuse to render all types in your selection because their definitions exceed the maximum input size. If that's where you find yourself, you may want to consider - using `--generate-diagrammer-include` and `--generate-diagrammer-exclude` to **limit the scope of the individual diagrammer to a certain subdomain** - generating **multiple diagrammers for different subdomains**. ### Advanced configuration examples Above examples show how the most important options are used. Let's have a quick look at the remaining ones, which allow for customization in your project setup and diagrams. #### Filter extracted types Sometimes the source assembly contains way more types than are sensible to diagram. Types with metadata for validation or mapping for example. Or auto-generated types. Especially if you want to tailor a diagrammer for a certain target audience and hide away most of the supporting type system to avoid noise and unnecessary questions. In these scenarios you can supply Regular Expressions for types to `--generate-diagrammer-include` (white-list) and `--generate-diagrammer-exclude` (black-list). A third option `--generate-diagrammer-report-excluded` will output a `.txt` containing the list of effectively excluded types next to the HTML diagrammer containing the effectively included types.
ilspycmd.exe --generate-diagrammer-include Your\.Models\..+ --generate-diagrammer-exclude .+\+Metadata|.+\.Data\..+Map --generate-diagrammer-report-excluded ..\path\to\your\assembly.dll --generate-diagrammer --outputdir .
This example - includes all types in the top-level namespace `Your.Models` - while excluding - nested types called `Metadata` and - types ending in `Map` in descendant `.Data.` namespaces. #### Strip namespaces from XML comments You can reduce the noise in the XML documentation comments on classes on your diagrams by supplying a space-separated list of namespaces to omit from the output like so:
ilspycmd.exe --generate-diagrammer-strip-namespaces System.Collections.Generic System ..\path\to\your\assembly.dll --generate-diagrammer --output-folder .
Note how `System` is replaced **after** other namespaces starting with `System.` to achieve complete removal. Otherwise `System.Collections.Generic` wouldn't match the `Collections.Generic` left over after removing `System.`, resulting in partial removal only. #### Adjust for custom XML documentation file names If - for whatever reason - you have customized your XML documentation file output name, you can specify a custom path to pick it up from.
ilspycmd.exe --generate-diagrammer-docs ..\path\to\your\docs.xml ..\path\to\your\assembly.dll --generate-diagrammer --output-folder .
================================================ FILE: ICSharpCode.ILSpyCmd/TypesParser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyCmd { public static class TypesParser { public static HashSet ParseSelection(string[] values) { var possibleValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["class"] = TypeKind.Class, ["struct"] = TypeKind.Struct, ["interface"] = TypeKind.Interface, ["enum"] = TypeKind.Enum, ["delegate"] = TypeKind.Delegate }; HashSet kinds = new HashSet(); if (values.Length == 1 && !possibleValues.Keys.Any(v => values[0].StartsWith(v, StringComparison.OrdinalIgnoreCase))) { foreach (char ch in values[0]) { switch (ch) { case 'c': kinds.Add(TypeKind.Class); break; case 'i': kinds.Add(TypeKind.Interface); break; case 's': kinds.Add(TypeKind.Struct); break; case 'd': kinds.Add(TypeKind.Delegate); break; case 'e': kinds.Add(TypeKind.Enum); break; } } } else { foreach (var value in values) { string v = value; while (v.Length > 0 && !possibleValues.ContainsKey(v)) v = v.Remove(v.Length - 1); if (possibleValues.TryGetValue(v, out var kind)) kinds.Add(kind); } } return kinds; } } } ================================================ FILE: ICSharpCode.ILSpyCmd/ValidationAttributes.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using System.IO; using McMaster.Extensions.CommandLineUtils.Abstractions; using McMaster.Extensions.CommandLineUtils.Validation; namespace ICSharpCode.ILSpyCmd { [AttributeUsage(AttributeTargets.Class)] public sealed class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute { public ProjectOptionRequiresOutputDirectoryValidationAttribute() { } protected override ValidationResult IsValid(object value, ValidationContext context) { if (value is ILSpyCmdProgram obj) { if (obj.CreateCompilableProjectFlag && string.IsNullOrEmpty(obj.OutputDirectory)) { return new ValidationResult("--project cannot be used unless --outputdir is also specified"); } } return ValidationResult.Success; } } [AttributeUsage(AttributeTargets.Property)] public sealed class FileExistsOrNullAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var path = value as string; if (string.IsNullOrEmpty(path)) { return ValidationResult.Success; } if (!Path.IsPathRooted(path) && validationContext.GetService(typeof(CommandLineContext)) is CommandLineContext context) { path = Path.Combine(context.WorkingDirectory, path); } if (File.Exists(path)) { return ValidationResult.Success; } return new ValidationResult($"File '{path}' does not exist!"); } } [AttributeUsage(AttributeTargets.Property)] public sealed class FilesExistAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { switch (value) { case string path: return ValidatePath(path); case string[] paths: { foreach (string path in paths) { ValidationResult result = ValidatePath(path); if (result != ValidationResult.Success) return result; } return ValidationResult.Success; } default: return new ValidationResult($"File '{value}' does not exist!"); } ValidationResult ValidatePath(string path) { if (!string.IsNullOrWhiteSpace(path)) { if (!Path.IsPathRooted(path) && validationContext.GetService(typeof(CommandLineContext)) is CommandLineContext context) { path = Path.Combine(context.WorkingDirectory, path); } if (File.Exists(path)) { return ValidationResult.Success; } } return new ValidationResult($"File '{path}' does not exist!"); } } } } ================================================ FILE: ICSharpCode.ILSpyCmd/packages.lock.json ================================================ { "version": 2, "dependencies": { "net10.0": { "McMaster.Extensions.Hosting.CommandLine": { "type": "Direct", "requested": "[5.0.1, )", "resolved": "5.0.1", "contentHash": "5BwLoyjHJESg0Fns+4rokcPP+kBfeZ+1NrZ03MUJsv9kf26s1mOLNgc2M9ju8lMCmJty+pQ2qyFQypVA+kbYhA==", "dependencies": { "McMaster.Extensions.CommandLineUtils": "5.0.1", "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Hosting": { "type": "Direct", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "8i7e5IBdiKLNqt/+ciWrS8U95Rv5DClaaj7ulkZbimnCi4uREWd+lXzkp3joofFuIPOlAzV4AckxLTIELv2jdg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Configuration.Binder": "10.0.5", "Microsoft.Extensions.Configuration.CommandLine": "10.0.5", "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.5", "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", "Microsoft.Extensions.Configuration.Json": "10.0.5", "Microsoft.Extensions.Configuration.UserSecrets": "10.0.5", "Microsoft.Extensions.DependencyInjection": "10.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Diagnostics": "10.0.5", "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", "Microsoft.Extensions.FileProviders.Physical": "10.0.5", "Microsoft.Extensions.Hosting.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Logging.Configuration": "10.0.5", "Microsoft.Extensions.Logging.Console": "10.0.5", "Microsoft.Extensions.Logging.Debug": "10.0.5", "Microsoft.Extensions.Logging.EventLog": "10.0.5", "Microsoft.Extensions.Logging.EventSource": "10.0.5", "Microsoft.Extensions.Options": "10.0.5" } }, "NuGet.Protocol": { "type": "Direct", "requested": "[7.3.0, )", "resolved": "7.3.0", "contentHash": "QWx4Fko06Act+gVhB9UUc8Hzt0fnA8qQhD5SFn/xEis2ZZVzmatHmMdsc0SV7tyvCUlfG8DzQzYLF7fF4LvTyA==", "dependencies": { "NuGet.Packaging": "7.3.0" } }, "System.Security.Cryptography.Pkcs": { "type": "Direct", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "BJEYUZfXpkPIHo2+oFoUemD5CPMFHPJOkRzXrbj/iZrWsjga3ypj8Rqd9bFlSLupEH4IIdD/aBWm/V1gCiBL9w==" }, "TomsToolbox.Composition.Analyzer": { "type": "Direct", "requested": "[2.22.2, )", "resolved": "2.22.2", "contentHash": "7gYo8ZR2eq3XkrilvUpLbTypeZy6IlD5FB8jah0YPhMOmDGhya4jJ3kfDMTTRt5m258Ou78P69mHMkG6DKZXsg==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.CommandLine": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "or9fOLopMUTJOQVJ3bou4aD6PwvsiKf4kZC4EE5sRRKSkmh+wfk/LekJXRjAX88X+1JA9zHjDo+5fiQ7z3MY/A==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "tchMGQ+zVTO40np/Zzg2Li/TIR8bksQgg4UVXZa0OzeFCKWnIYtxE2FVs+eSmjPGCjMS2voZbwN/mUcYfpSTuA==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "OhTr0O79dP49734lLTqVveivVX9sDXxbI/8vjELAZTHXqoN90mdpgTAgwicJED42iaHMCcZcK6Bj+8wNyBikaw==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", "Microsoft.Extensions.FileProviders.Physical": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "fhdG6UV9lIp70QhNkVyaHciUVq25IPFkczheVJL9bIFvmnJ+Zghaie6dWkDbbVmxZlHl9gj3zTDxMxJs5zNhIA==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Configuration.Json": "10.0.5", "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", "Microsoft.Extensions.FileProviders.Physical": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "vAJHd4yOpmKoK+jBuYV7a3y+Ab9U4ARCc29b6qvMy276RgJFw9LFs0DdsPqOL3ahwzyrX7tM+i4cCxU/RX0qAg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "nCBmCx0Xemlu65ZiWMcXbvfvtznKxf4/YYKF9R28QkqdI9lTikedGqzJ28/xmdGGsxUnsP5/3TQGpiPwVjK0dA==", "dependencies": { "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "dMu5kUPSfol1Rqhmr6nWPSmbFjDe9w6bkoKithG17bWTZA0UyKirTatM5mqYUN3mGpNA0MorlusIoVTh6J7o5g==", "dependencies": { "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", "Microsoft.Extensions.FileSystemGlobbing": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "mOE3ARusNQR0a5x8YOcnUbfyyXGqoAWQtEc7qFOfNJgruDWQLo39Re+3/Lzj5pLPFuFYj8hN4dgKzaSQDKiOCw==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "+Wb7KAMVZTomwJkQrjuPTe5KBzGod7N8XeG+ScxRlkPOB4sZLG4ccVwjV4Phk5BCJt7uIMnGHVoN6ZMVploX+g==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { "Microsoft.Extensions.DependencyInjection": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "cSgxsDgfP0+gmVRPVoNHI/KIDavIZxh+CxE6tSLPlYTogqccDnjBFI9CgEsiNuMP6+fiuXUwhhlTz36uUEpwbQ==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Configuration.Binder": "10.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5", "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "PMs2gha2v24hvH5o5KQem5aNK4mN0BhhCWlMqsg9tzifWKzjeQi2tyPOP/RaWMVvalOhVLcrmoMYPqbnia/epg==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Logging.Configuration": "10.0.5", "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "/VacEkBQ02A8PBXSa6YpbIXCuisYy6JJr62/+ANJDZE+RMBfZMcXJXLfr/LpyLE6pgdp17Wxlt7e7R9zvkwZ3Q==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "0ezhWYJS4/6KrqQel9JL+Tr4n+4EX2TF5EYiaysBWNNEM2c3Gtj1moD39esfgk8OHblSX+UFjtZ3z0c4i9tRvw==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5", "System.Diagnostics.EventLog": "10.0.5" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "vN+aq1hBFXyYvY5Ow9WyeR66drKQxRZmas4lAjh6QWfryPkjTn1uLtX5AFIxyDaZj78v5TG2sELUyvrXpAPQQw==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Logging": "10.0.5", "Microsoft.Extensions.Logging.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "BB9uUW3+6Rxu1R97OB1H/13lUF8P2+H1+eDhpZlK30kDh/6E4EKHBUqTp+ilXQmZLzsRErxON8aBSR6WpUKJdg==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Configuration.Binder": "10.0.5", "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", "Microsoft.Extensions.Options": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, "NuGet.Common": { "type": "Transitive", "resolved": "7.3.0", "contentHash": "y+7cQuzc6zePfo3GdueKgFAfE06pyNv1EGeordAMXotm6WEJw/m7UHv7GSLnib2HEPpzUk4Wvxgn9VgWkIe6Yw==", "dependencies": { "NuGet.Frameworks": "7.3.0" } }, "NuGet.Configuration": { "type": "Transitive", "resolved": "7.3.0", "contentHash": "9qNpnZP73pfGm1MYET78OCW/HNnKNZpTfWPB2z/EyxQnfWtJBANeFyogX8s7S+oWMbR+EG2w6jbDVKdvVJfCXA==", "dependencies": { "NuGet.Common": "7.3.0", "System.Security.Cryptography.ProtectedData": "8.0.0" } }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "7.3.0", "contentHash": "AnhSFUOrCrWp7pGtqhfOxa5HKm4rfpQxuatGDKzpGtTczasj6OcGVtB74PyqW7osdu9/BtkjnkAYvNk5iT82Gg==" }, "NuGet.Packaging": { "type": "Transitive", "resolved": "7.3.0", "contentHash": "yvmJ8LUPUVMm2DAKSK3+QfJKoCxQR1NuyPYLHqFHb1zmUcGwq+IvSl/4JjUivJDztXnI9GSVlE/SjQLsRJPoYw==", "dependencies": { "Newtonsoft.Json": "13.0.3", "NuGet.Configuration": "7.3.0", "NuGet.Versioning": "7.3.0", "System.Security.Cryptography.Pkcs": "8.0.1" } }, "NuGet.Versioning": { "type": "Transitive", "resolved": "7.3.0", "contentHash": "iOQPAdnVgj2U+K3AcdGGGcdS1tWyh3nvr64eqTDFCsDuHySBXSHRhl7eR8hdc0BZHxDjacRstbuaJKCurt2oPw==" }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "wugvy+pBVzjQEnRs9wMTWwoaeNFX3hsaHeVHFDIvJSWXp7wfmNWu3mxAwBIE6pyW+g6+rHa1Of5fTzb0QVqUTA==" }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" }, "icsharpcode.decompiler": { "type": "Project", "dependencies": { "System.Collections.Immutable": "[9.0.0, )", "System.Reflection.Metadata": "[9.0.0, )" } }, "icsharpcode.ilspyx": { "type": "Project", "dependencies": { "ICSharpCode.Decompiler": "[8.0.0-noversion, )", "K4os.Compression.LZ4": "[1.3.8, )", "Mono.Cecil": "[0.11.6, )", "System.Composition.AttributedModel": "[10.0.5, )", "System.Reflection.Metadata": "[10.0.5, )", "System.Runtime.CompilerServices.Unsafe": "[6.1.2, )" } }, "K4os.Compression.LZ4": { "type": "CentralTransitive", "requested": "[1.3.8, )", "resolved": "1.3.8", "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g==" }, "McMaster.Extensions.CommandLineUtils": { "type": "CentralTransitive", "requested": "[5.0.1, )", "resolved": "5.0.1", "contentHash": "dXerCHdnTrlpoQjdpxBISv9OIqqziJph1bKOTK95nPG0m7yDyMyVmQDcE1Z10ZZlQeaod1C8zewOW+MQOgPWhw==" }, "Microsoft.Extensions.Configuration": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Json": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "brBM/WP0YAUYh2+QqSYVdK8eQHYQTtTEUJXJ+84Zkdo2buGLja9VSrMIhgoeBUU7JBmcskAib8Lb/N83bvxgYQ==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.5", "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Mono.Cecil": { "type": "CentralTransitive", "requested": "[0.11.6, )", "resolved": "0.11.6", "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA==" }, "System.Collections.Immutable": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "9.0.0", "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" }, "System.Composition.AttributedModel": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "Vgb7wwB7ya+lbcwccXHZPSJxeKR7tCkWLgFXO9Wcgbu/NgO5DvNAIHtEkXaEESkcvXdD1iqp2JBcLWGT/xDxEw==" }, "System.Reflection.Metadata": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "fEAXJCtauNLYr5ESg3t6HE2Av6urWdJdymxZbuSt/DDqhtNtLtUtXTEpKbp0vkTdyBJwmaha8d2454vSzS/lcQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "CentralTransitive", "requested": "[6.1.2, )", "resolved": "6.1.2", "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" } } } } ================================================ FILE: ICSharpCode.ILSpyX/Abstractions/ILanguage.cs ================================================ // Copyright (c) 2022 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.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Abstractions { public interface ILanguage { bool ShowMember(IEntity member); CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member); string GetEntityName(MetadataFile module, System.Reflection.Metadata.EntityHandle handle, bool fullName, bool omitGenerics); string GetTooltip(IEntity entity); string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames); string EntityToString(IEntity entity, ConversionFlags conversionFlags); } } ================================================ FILE: ICSharpCode.ILSpyX/Abstractions/ITreeNode.cs ================================================ // Copyright (c) 2022 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.Collections.Generic; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX.Abstractions { public interface ITreeNode { object Text { get; } object Icon { get; } IEnumerable Children { get; } void EnsureLazyChildren(); } public interface IResourcesFileTreeNode : ITreeNode { Resource Resource { get; } } public interface ITreeNodeFactory { ITreeNode CreateResourcesList(MetadataFile module); ITreeNode Create(Resource resource); } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/AnalyzerContext.cs ================================================ // 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.Concurrent; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Analyzers { /// /// Provides additional context for analyzers. /// public class AnalyzerContext { public required AssemblyList AssemblyList { get; init; } /// /// CancellationToken. Currently Analyzers do not support cancellation from the UI, but it should be checked nonetheless. /// public CancellationToken CancellationToken { get; init; } /// /// Currently used language. /// public required ILanguage Language { get; init; } /// /// Allows the analyzer to control whether the tree nodes will be sorted. /// Must be set within /// before the results are enumerated. /// public bool SortResults { get; set; } public MethodBodyBlock? GetMethodBody(IMethod method) { if (!method.HasBody || method.MetadataToken.IsNil || method.ParentModule?.MetadataFile == null) return null; var module = method.ParentModule.MetadataFile; var md = module.Metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); try { return module.GetMethodBody(md.RelativeVirtualAddress); } catch (BadImageFormatException) { return null; } } public AnalyzerScope GetScopeOf(IEntity entity) { return new AnalyzerScope(AssemblyList, entity); } readonly ConcurrentDictionary typeSystemCache = new(); public DecompilerTypeSystem GetOrCreateTypeSystem(MetadataFile module) { return typeSystemCache.GetOrAdd(module, m => new DecompilerTypeSystem(m, m.GetAssemblyResolver())); } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/AnalyzerHelpers.cs ================================================ // 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.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers { internal static class AnalyzerHelpers { public static bool IsPossibleReferenceTo(EntityHandle member, MetadataFile module, IMethod analyzedMethod) { if (member.IsNil) return false; MetadataReader metadata = module.Metadata; switch (member.Kind) { case HandleKind.MethodDefinition: return member == analyzedMethod.MetadataToken && module == analyzedMethod.ParentModule?.MetadataFile; case HandleKind.MemberReference: var mr = metadata.GetMemberReference((MemberReferenceHandle)member); if (mr.GetKind() != MemberReferenceKind.Method) return false; return metadata.StringComparer.Equals(mr.Name, analyzedMethod.Name); case HandleKind.MethodSpecification: var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)member); return IsPossibleReferenceTo(ms.Method, module, analyzedMethod); default: return false; } } public static ISymbol? GetParentEntity(DecompilerTypeSystem ts, CustomAttribute customAttribute) { var metadata = ts.MainModule.MetadataFile.Metadata; switch (customAttribute.Parent.Kind) { case HandleKind.MethodDefinition: IMethod parent = (IMethod)ts.MainModule.ResolveEntity(customAttribute.Parent); return parent?.AccessorOwner ?? parent; case HandleKind.FieldDefinition: case HandleKind.PropertyDefinition: case HandleKind.EventDefinition: case HandleKind.TypeDefinition: return ts.MainModule.ResolveEntity(customAttribute.Parent); case HandleKind.AssemblyDefinition: case HandleKind.ModuleDefinition: return ts.MainModule; case HandleKind.GenericParameterConstraint: var gpc = metadata.GetGenericParameterConstraint((GenericParameterConstraintHandle)customAttribute.Parent); var gp = metadata.GetGenericParameter(gpc.Parameter); return ts.MainModule.ResolveEntity(gp.Parent); case HandleKind.GenericParameter: gp = metadata.GetGenericParameter((GenericParameterHandle)customAttribute.Parent); return ts.MainModule.ResolveEntity(gp.Parent); default: return null; } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/AnalyzerScope.cs ================================================ // 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.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpyX.Analyzers { using ICSharpCode.Decompiler.TypeSystem; public class AnalyzerScope { readonly ITypeDefinition typeScope; readonly AssemblyListSnapshot assemblyListSnapshot; /// /// Returns whether this scope is local, i.e., AnalyzedSymbol is only reachable /// from the current module or containing type. /// public bool IsLocal { get; } public ISymbol AnalyzedSymbol { get; } public ITypeDefinition TypeScope => typeScope; readonly Accessibility effectiveAccessibility; public AnalyzerScope(AssemblyList assemblyList, IEntity entity) { assemblyListSnapshot = assemblyList.GetSnapshot(); AnalyzedSymbol = entity; DetermineEffectiveAccessibility(entity, out typeScope, out effectiveAccessibility); IsLocal = effectiveAccessibility.LessThanOrEqual(Accessibility.Private); } public IEnumerable GetModulesInScope(CancellationToken ct) { if (IsLocal) return new[] { TypeScope.ParentModule!.MetadataFile! }; if (effectiveAccessibility.LessThanOrEqual(Accessibility.Internal)) return GetModuleAndAnyFriends(TypeScope, ct); return GetReferencingModules(TypeScope.ParentModule!.MetadataFile!, ct); } public IEnumerable GetAllModules() { return assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult() .Select(asm => asm.GetMetadataFileOrNull()) .Where(x => x != null && !x.IsMetadataOnly)!; } public DecompilerTypeSystem ConstructTypeSystem(MetadataFile module) { return new DecompilerTypeSystem(module, module.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false)); } public IEnumerable GetTypesInScope(CancellationToken ct) { if (IsLocal) { foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) { yield return type; } } else { foreach (var module in GetModulesInScope(ct)) { var typeSystem = ConstructTypeSystem(module); foreach (var type in typeSystem.MainModule.TypeDefinitions) { yield return type; } } } } static void DetermineEffectiveAccessibility(IEntity input, out ITypeDefinition typeScope, out Accessibility accessibility) { if (input is ITypeDefinition td) { accessibility = Accessibility.Public; typeScope = td; } else { accessibility = input.Accessibility; typeScope = input.DeclaringTypeDefinition!; } // Once we reach a private entity, we leave the loop with typeScope set to the class that // contains the private entity = the scope that needs to be searched. // Otherwise (if we don't find a private entity) we return the top-level class. var prevTypeScope = typeScope; while (typeScope != null && !accessibility.LessThanOrEqual(Accessibility.Private)) { accessibility = accessibility.Intersect(typeScope.Accessibility); prevTypeScope = typeScope; typeScope = prevTypeScope.DeclaringTypeDefinition!; } if (typeScope == null) { typeScope = prevTypeScope; } } #region Find modules IEnumerable GetReferencingModules(MetadataFile self, CancellationToken ct) { yield return self; string reflectionTypeScopeName = typeScope.Name; if (typeScope.TypeParameterCount > 0) reflectionTypeScopeName += "`" + typeScope.TypeParameterCount; var toWalkFiles = new Stack(); var checkedFiles = new HashSet(); toWalkFiles.Push(self); checkedFiles.Add(self); IList assemblies = assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult(); do { MetadataFile curFile = toWalkFiles.Pop(); foreach (var assembly in assemblies) { ct.ThrowIfCancellationRequested(); bool found = false; var module = assembly.GetMetadataFileOrNull(); if (module == null || !module.IsAssembly) continue; if (checkedFiles.Contains(module)) continue; var resolver = assembly.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false); foreach (var reference in module.AssemblyReferences) { if (resolver.Resolve(reference) == curFile) { found = true; break; } } if (found && checkedFiles.Add(module)) { if (ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace)) yield return module; if (ModuleForwardsScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace)) toWalkFiles.Push(module); } } } while (toWalkFiles.Count > 0); } IEnumerable GetModuleAndAnyFriends(ITypeDefinition typeScope, CancellationToken ct) { var self = typeScope.ParentModule!.MetadataFile!; yield return self; var typeProvider = MetadataExtensions.MinimalAttributeTypeProvider; var attributes = self.Metadata.CustomAttributes.Select(h => self.Metadata.GetCustomAttribute(h)) .Where(ca => ca.GetAttributeType(self.Metadata).GetFullTypeName(self.Metadata).ToString() == "System.Runtime.CompilerServices.InternalsVisibleToAttribute"); var friendAssemblies = new HashSet(); foreach (var attribute in attributes) { string? assemblyName = attribute.DecodeValue(typeProvider).FixedArguments[0].Value as string; assemblyName = assemblyName?.Split(',')[0]; // strip off any public key info if (assemblyName != null) friendAssemblies.Add(assemblyName); } if (friendAssemblies.Count > 0) { IEnumerable assemblies = assemblyListSnapshot.GetAllAssembliesAsync() .GetAwaiter().GetResult(); foreach (var assembly in assemblies) { ct.ThrowIfCancellationRequested(); if (friendAssemblies.Contains(assembly.ShortName)) { var module = assembly.GetMetadataFileOrNull(); if (module == null || module.IsMetadataOnly) continue; if (ModuleReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace)) yield return module; } } } } bool ModuleReferencesScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace) { bool hasRef = false; foreach (var h in metadata.TypeReferences) { var typeRef = metadata.GetTypeReference(h); if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName) && metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace)) { hasRef = true; break; } } return hasRef; } bool ModuleForwardsScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace) { bool hasForward = false; foreach (var h in metadata.ExportedTypes) { var exportedType = metadata.GetExportedType(h); if (exportedType.IsForwarder && metadata.StringComparer.Equals(exportedType.Name, typeScopeName) && metadata.StringComparer.Equals(exportedType.Namespace, typeScopeNamespace)) { hasForward = true; break; } } return hasForward; } #endregion } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Linq; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { [ExportAnalyzer(Header = "Applied To", Order = 10)] [Shared] class AttributeAppliedToAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { if (!(analyzedSymbol is ITypeDefinition attributeType)) return Empty.Array; var scope = context.GetScopeOf(attributeType); // TODO: DeclSecurity attributes are not supported. if (!IsBuiltinAttribute(attributeType, out var knownAttribute)) { return HandleCustomAttribute(attributeType, scope, context.CancellationToken).Distinct(); } else { return HandleBuiltinAttribute(knownAttribute, scope, context.CancellationToken).SelectMany(s => s); } } bool IsBuiltinAttribute(ITypeDefinition attributeType, out KnownAttribute knownAttribute) { knownAttribute = attributeType.IsBuiltinAttribute(); return !knownAttribute.IsCustomAttribute(); } IEnumerable> HandleBuiltinAttribute(KnownAttribute attribute, AnalyzerScope scope, CancellationToken ct) { IEnumerable ScanTypes(DecompilerTypeSystem ts) { return ts.MainModule.TypeDefinitions .Where(t => t.HasAttribute(attribute)); } IEnumerable ScanMethods(DecompilerTypeSystem ts) { return ts.MainModule.TypeDefinitions .SelectMany(t => t.Members.OfType()) .Where(m => m.HasAttribute(attribute)) .Select(m => m.AccessorOwner ?? m); } IEnumerable ScanFields(DecompilerTypeSystem ts) { return ts.MainModule.TypeDefinitions .SelectMany(t => t.Fields) .Where(f => f.HasAttribute(attribute)); } IEnumerable ScanProperties(DecompilerTypeSystem ts) { return ts.MainModule.TypeDefinitions .SelectMany(t => t.Properties) .Where(p => p.HasAttribute(attribute)); } IEnumerable ScanParameters(DecompilerTypeSystem ts) { return ts.MainModule.TypeDefinitions .SelectMany(t => t.Members.OfType()) .Where(m => m.Parameters.Any(p => p.HasAttribute(attribute))) .Select(m => m.AccessorOwner ?? m); } foreach (Decompiler.Metadata.PEFile module in scope.GetModulesInScope(ct)) { var ts = scope.ConstructTypeSystem(module); ct.ThrowIfCancellationRequested(); switch (attribute) { case KnownAttribute.Serializable: case KnownAttribute.ComImport: case KnownAttribute.StructLayout: yield return ScanTypes(ts); break; case KnownAttribute.DllImport: case KnownAttribute.PreserveSig: case KnownAttribute.MethodImpl: yield return ScanMethods(ts); break; case KnownAttribute.FieldOffset: case KnownAttribute.NonSerialized: yield return ScanFields(ts); break; case KnownAttribute.MarshalAs: yield return ScanFields(ts); yield return ScanParameters(ts); goto case KnownAttribute.Out; case KnownAttribute.Optional: case KnownAttribute.In: case KnownAttribute.Out: yield return ScanParameters(ts); break; case KnownAttribute.IndexerName: yield return ScanProperties(ts); break; } } } IEnumerable HandleCustomAttribute(ITypeDefinition attributeType, AnalyzerScope scope, CancellationToken ct) { var genericContext = new Decompiler.TypeSystem.GenericContext(); // type arguments do not matter for this analyzer. foreach (var module in scope.GetModulesInScope(ct)) { var ts = scope.ConstructTypeSystem(module); ct.ThrowIfCancellationRequested(); var decoder = new FindTypeDecoder(ts.MainModule, attributeType); var referencedParameters = new HashSet(); foreach (var h in module.Metadata.CustomAttributes) { var customAttribute = module.Metadata.GetCustomAttribute(h); if (IsCustomAttributeOfType(customAttribute.Constructor, module.Metadata, decoder)) { if (customAttribute.Parent.Kind == HandleKind.Parameter) { referencedParameters.Add((ParameterHandle)customAttribute.Parent); } else { var parent = AnalyzerHelpers.GetParentEntity(ts, customAttribute); if (parent != null) yield return parent; } } } if (referencedParameters.Count > 0) { foreach (var h in module.Metadata.MethodDefinitions) { var md = module.Metadata.GetMethodDefinition(h); foreach (var p in md.GetParameters()) { if (referencedParameters.Contains(p)) { var method = ts.MainModule.ResolveMethod(h, genericContext); if (method != null) { if (method.IsAccessor) yield return method.AccessorOwner; else yield return method; } break; } } } } } } internal static bool IsCustomAttributeOfType(EntityHandle customAttributeCtor, MetadataReader metadata, FindTypeDecoder decoder) { var declaringAttributeType = customAttributeCtor.GetDeclaringType(metadata); return decoder.GetTypeFromEntity(metadata, declaringAttributeType); } public bool Show(ISymbol symbol) { return symbol is ITypeDefinition type && type.GetNonInterfaceBaseTypes() .Any(t => t.IsKnownType(KnownTypeCode.Attribute)); } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/EventImplementedByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows events that implement an interface event. /// [ExportAnalyzer(Header = "Implemented By", Order = 10)] [Shared] class EventImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IEvent); var scope = context.GetScopeOf((IEvent)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IEvent)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IEvent analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var @event in type.Events) { var baseMembers = InheritanceHelper.GetBaseMembers(@event, true); if (baseMembers.Any(m => m.MetadataToken == token && m.ParentModule?.MetadataFile == module)) yield return @event; } } public bool Show(ISymbol symbol) { return symbol is IEvent entity && entity.DeclaringType.Kind == TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/EventOverriddenByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows events that override an event. /// [ExportAnalyzer(Header = "Overridden By", Order = 20)] [Shared] class EventOverriddenByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IEvent); var scope = context.GetScopeOf((IEvent)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IEvent)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IEvent analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var @event in type.Events) { if (!@event.IsOverride) continue; var baseMembers = InheritanceHelper.GetBaseMembers(@event, false); if (baseMembers.Any(p => p.MetadataToken == token && p.ParentModule?.MetadataFile == module)) { yield return @event; } } } public bool Show(ISymbol symbol) { return symbol is IEvent entity && entity.IsOverridable && entity.DeclaringType.Kind != TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/FieldAccessAnalyzer.cs ================================================ // 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.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ILOpCode = System.Reflection.Metadata.ILOpCode; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Finds methods where this field is read. /// [ExportAnalyzer(Header = "Assigned By", Order = 20)] [Shared] class AssignedByFieldAccessAnalyzer : FieldAccessAnalyzer { public AssignedByFieldAccessAnalyzer() : base(true) { } } /// /// Finds methods where this field is written. /// [ExportAnalyzer(Header = "Read By", Order = 10)] [Shared] class ReadByFieldAccessAnalyzer : FieldAccessAnalyzer { public ReadByFieldAccessAnalyzer() : base(false) { } } /// /// Finds methods where this field is read or written. /// class FieldAccessAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; readonly bool showWrites; // true: show writes; false: show read access public FieldAccessAnalyzer(bool showWrites) { this.showWrites = showWrites; } public bool Show(ISymbol symbol) { return symbol is IField field && (!showWrites || !field.IsConst); } public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IField); var scope = context.GetScopeOf((IEntity)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { if (type.ParentModule?.MetadataFile == null) continue; var mappingInfo = context.Language.GetCodeMappingInfo(type.ParentModule.MetadataFile, type.MetadataToken); var methods = type.GetMembers(m => m is IMethod, Options).OfType(); foreach (var method in methods) { if (IsUsedInMethod((IField)analyzedSymbol, method, mappingInfo, context)) yield return method; } foreach (var property in type.Properties) { if (property.CanGet && IsUsedInMethod((IField)analyzedSymbol, property.Getter, mappingInfo, context)) { yield return property; continue; } if (property.CanSet && IsUsedInMethod((IField)analyzedSymbol, property.Setter, mappingInfo, context)) { yield return property; continue; } } foreach (var @event in type.Events) { if (@event.CanAdd && IsUsedInMethod((IField)analyzedSymbol, @event.AddAccessor, mappingInfo, context)) { yield return @event; continue; } if (@event.CanRemove && IsUsedInMethod((IField)analyzedSymbol, @event.RemoveAccessor, mappingInfo, context)) { yield return @event; continue; } if (@event.CanInvoke && IsUsedInMethod((IField)analyzedSymbol, @event.InvokeAccessor, mappingInfo, context)) { yield return @event; continue; } } } } bool IsUsedInMethod(IField analyzedField, IMethod method, CodeMappingInfo mappingInfo, AnalyzerContext context) { if (method.MetadataToken.IsNil || method.ParentModule?.MetadataFile == null) return false; var module = method.ParentModule.MetadataFile; foreach (var part in mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken)) { var md = module.Metadata.GetMethodDefinition(part); if (!md.HasBody()) continue; MethodBodyBlock body; try { body = module.GetMethodBody(md.RelativeVirtualAddress); } catch (BadImageFormatException) { return false; } if (ScanMethodBody(analyzedField, method, body)) return true; } return false; } bool ScanMethodBody(IField analyzedField, IMethod method, MethodBodyBlock methodBody) { if (methodBody == null || method.ParentModule?.MetadataFile == null) return false; var mainModule = (MetadataModule)method.ParentModule; var blob = methodBody.GetILReader(); var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer while (blob.RemainingBytes > 0) { ILOpCode opCode; try { opCode = blob.DecodeOpCode(); if (!CanBeReference(opCode)) { blob.SkipOperand(opCode); continue; } } catch (BadImageFormatException) { return false; } EntityHandle fieldHandle = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (!fieldHandle.Kind.IsMemberKind()) continue; IField? field; try { field = mainModule.ResolveEntity(fieldHandle, genericContext) as IField; } catch (BadImageFormatException) { continue; } if (field == null) continue; if (field.MetadataToken == analyzedField.MetadataToken && field.ParentModule?.MetadataFile == analyzedField.ParentModule!.MetadataFile) return true; } return false; } bool CanBeReference(ILOpCode code) { switch (code) { case ILOpCode.Ldfld: case ILOpCode.Ldsfld: return !showWrites; case ILOpCode.Stfld: case ILOpCode.Stsfld: return showWrites; case ILOpCode.Ldflda: case ILOpCode.Ldsflda: return true; // always show address-loading default: return false; } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/FindTypeInAttributeDecoder.cs ================================================ // Copyright (c) 2022 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. #nullable enable using System; using System.Reflection.Metadata; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { public enum TokenSearchResult : byte { NoResult = 0, Byte = PrimitiveTypeCode.Byte, SByte = PrimitiveTypeCode.SByte, Int16 = PrimitiveTypeCode.Int16, UInt16 = PrimitiveTypeCode.UInt16, Int32 = PrimitiveTypeCode.Int32, UInt32 = PrimitiveTypeCode.UInt32, Int64 = PrimitiveTypeCode.Int64, UInt64 = PrimitiveTypeCode.UInt64, IntPtr = PrimitiveTypeCode.IntPtr, UIntPtr = PrimitiveTypeCode.UIntPtr, // lowest PrimitiveTypeCode is 1 // highest PrimitiveTypeCode is 28 (0b0001_1100) // TokenSearchResult with a PrimitiveTypeCode set is only used when decoding an enum-type. // It is used for GetUnderlyingEnumType and should be masked out in all other uses. // MSB = Found // 127 = System.Type TypeCodeMask = 0b0111_1111, Found = 0b1000_0000, SystemType = 127, } class FindTypeInAttributeDecoder : ICustomAttributeTypeProvider { readonly MetadataFile declaringModule; readonly MetadataModule currentModule; readonly TypeDefinitionHandle handle; readonly PrimitiveTypeCode primitiveType; /// /// Constructs a FindTypeInAttributeDecoder that can be used to find in signatures from . /// public FindTypeInAttributeDecoder(MetadataModule currentModule, ITypeDefinition type) { this.currentModule = currentModule; this.declaringModule = type.ParentModule?.MetadataFile ?? throw new InvalidOperationException("Cannot use MetadataModule without PEFile as context."); this.handle = (TypeDefinitionHandle)type.MetadataToken; this.primitiveType = type.KnownTypeCode == KnownTypeCode.None ? 0 : type.KnownTypeCode.ToPrimitiveTypeCode(); } public TokenSearchResult GetPrimitiveType(PrimitiveTypeCode typeCode) { return typeCode == primitiveType ? TokenSearchResult.Found : 0; } public TokenSearchResult GetSystemType() => TokenSearchResult.SystemType; public TokenSearchResult GetSZArrayType(TokenSearchResult elementType) => elementType & TokenSearchResult.Found; public TokenSearchResult GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { TokenSearchResult result = TokenSearchResult.NoResult; if (handle.IsEnum(reader, out PrimitiveTypeCode underlyingType)) { result = (TokenSearchResult)underlyingType; } else if (((EntityHandle)handle).IsKnownType(reader, KnownTypeCode.Type)) { result = TokenSearchResult.SystemType; } if (this.handle == handle && reader == declaringModule.Metadata) { result |= TokenSearchResult.Found; } return result; } public TokenSearchResult GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var t = currentModule.ResolveType(handle, default); return GetResultFromResolvedType(t); } public TokenSearchResult GetTypeFromSerializedName(string name) { if (name == null) { return TokenSearchResult.NoResult; } try { IType type = ReflectionHelper.ParseReflectionName(name, new SimpleTypeResolveContext(currentModule)); return GetResultFromResolvedType(type); } catch (ReflectionNameParseException) { return TokenSearchResult.NoResult; } } private TokenSearchResult GetResultFromResolvedType(IType type) { var td = type.GetDefinition(); if (td == null) return TokenSearchResult.NoResult; TokenSearchResult result = TokenSearchResult.NoResult; var underlyingType = td.EnumUnderlyingType?.GetDefinition(); if (underlyingType != null) { result = (TokenSearchResult)underlyingType.KnownTypeCode.ToPrimitiveTypeCode(); } else if (td.KnownTypeCode == KnownTypeCode.Type) { result = TokenSearchResult.SystemType; } if (td.MetadataToken == this.handle && td.ParentModule?.MetadataFile == declaringModule) { result |= TokenSearchResult.Found; } return result; } public PrimitiveTypeCode GetUnderlyingEnumType(TokenSearchResult type) { TokenSearchResult typeCode = type & TokenSearchResult.TypeCodeMask; if (typeCode == 0 || typeCode == TokenSearchResult.SystemType) throw new EnumUnderlyingTypeResolveException(); return (PrimitiveTypeCode)typeCode; } public bool IsSystemType(TokenSearchResult type) => (type & TokenSearchResult.TypeCodeMask) == TokenSearchResult.SystemType; } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows members from all corresponding interfaces the selected member implements. /// [ExportAnalyzer(Header = "Implements", Order = 40)] [Shared] class MemberImplementsInterfaceAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IMember); var member = (IMember)analyzedSymbol; Debug.Assert(!member.IsStatic); var baseMembers = InheritanceHelper.GetBaseMembers(member, includeImplementedInterfaces: true); return baseMembers.Where(m => m.DeclaringTypeDefinition?.Kind == TypeKind.Interface); } public bool Show(ISymbol symbol) { switch (symbol?.SymbolKind) { case SymbolKind.Event: case SymbolKind.Indexer: case SymbolKind.Method: case SymbolKind.Property: var member = (IMember)symbol; var type = member.DeclaringTypeDefinition; return !member.IsStatic && type is not null && (type.Kind == TypeKind.Class || type.Kind == TypeKind.Struct); default: return false; } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MethodImplementedByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows methods that implement an interface method. /// [ExportAnalyzer(Header = "Implemented By", Order = 40)] [Shared] class MethodImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IMethod); var scope = context.GetScopeOf((IEntity)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IMethod)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IMethod analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var method in type.Methods) { var baseMembers = InheritanceHelper.GetBaseMembers(method, true); if (baseMembers.Any(m => m.MetadataToken == token && m.ParentModule?.MetadataFile == module)) yield return method; } } public bool Show(ISymbol entity) { return entity is IMethod method && method.DeclaringType.Kind == TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MethodOverriddenByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows methods that override a method. /// [ExportAnalyzer(Header = "Overridden By", Order = 30)] [Shared] class MethodOverriddenByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IMethod); var scope = context.GetScopeOf((IEntity)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IMethod)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IMethod analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var method in type.Methods) { if (!method.IsOverride) continue; var baseMembers = InheritanceHelper.GetBaseMembers(method, false); if (baseMembers.Any(p => p.MetadataToken == token && p.ParentModule?.MetadataFile == module)) { yield return method; } } } public bool Show(ISymbol entity) { return entity is IMethod method && method.IsOverridable && method.DeclaringType.Kind != TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsedByAnalyzer.cs ================================================ // 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.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Used By", Order = 20)] [Shared] class MethodUsedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; public bool Show(ISymbol symbol) => symbol is IMethod method && !method.IsVirtual && method.ParentModule is not null; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IMethod); var analyzedMethod = (IMethod)analyzedSymbol; var analyzedBaseMethod = (IMethod?)InheritanceHelper.GetBaseMember(analyzedMethod); if (analyzedMethod.ParentModule?.MetadataFile == null) yield break; var mapping = context.Language .GetCodeMappingInfo(analyzedMethod.ParentModule.MetadataFile, analyzedMethod.DeclaringTypeDefinition!.MetadataToken); var parentMethod = mapping.GetParentMethod((MethodDefinitionHandle)analyzedMethod.MetadataToken); if (parentMethod != analyzedMethod.MetadataToken) yield return ((MetadataModule)analyzedMethod.ParentModule).GetDefinition(parentMethod); var scope = context.GetScopeOf(analyzedMethod); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { if (type.ParentModule?.MetadataFile == null) continue; var parentModule = (MetadataModule)type.ParentModule; mapping = null; var methods = type.GetMembers(m => m is IMethod, Options).OfType(); foreach (var method in methods) { if (IsUsedInMethod(analyzedMethod, analyzedBaseMethod, method, context)) { mapping ??= context.Language.GetCodeMappingInfo(parentModule.MetadataFile, type.MetadataToken); var parent = mapping.GetParentMethod((MethodDefinitionHandle)method.MetadataToken); yield return parentModule.GetDefinition(parent); } } foreach (var property in type.Properties) { if (property.CanGet && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, property.Getter, context)) { yield return property; continue; } if (property.CanSet && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, property.Setter, context)) { yield return property; continue; } } foreach (var @event in type.Events) { if (@event.CanAdd && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.AddAccessor, context)) { yield return @event; continue; } if (@event.CanRemove && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.RemoveAccessor, context)) { yield return @event; continue; } if (@event.CanInvoke && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.InvokeAccessor, context)) { yield return @event; continue; } } } } bool IsUsedInMethod(IMethod analyzedEntity, IMethod? analyzedBaseMethod, IMethod method, AnalyzerContext context) { return ScanMethodBody(analyzedEntity, method, analyzedBaseMethod, context.GetMethodBody(method)); } static bool ScanMethodBody(IMethod analyzedMethod, IMethod method, IMethod? analyzedBaseMethod, MethodBodyBlock? methodBody) { if (methodBody == null || method.ParentModule?.MetadataFile == null) return false; var mainModule = (MetadataModule)method.ParentModule; var blob = methodBody.GetILReader(); var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer while (blob.RemainingBytes > 0) { ILOpCode opCode; try { opCode = blob.DecodeOpCode(); if (!IsSupportedOpCode(opCode)) { ILParser.SkipOperand(ref blob, opCode); continue; } } catch (BadImageFormatException) { return false; // unexpected end of blob } var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (!AnalyzerHelpers.IsPossibleReferenceTo(member, mainModule.MetadataFile, analyzedMethod)) { if (analyzedBaseMethod == null || !AnalyzerHelpers.IsPossibleReferenceTo(member, mainModule.MetadataFile, analyzedBaseMethod)) { continue; } } IMember? m; try { m = (mainModule.ResolveEntity(member, genericContext) as IMember)?.MemberDefinition; } catch (BadImageFormatException) { continue; } if (m == null) continue; if (opCode == ILOpCode.Callvirt && analyzedBaseMethod != null) { if (IsSameMember(analyzedBaseMethod, m)) { return true; } } if (IsSameMember(analyzedMethod, m)) { return true; } } return false; } static bool IsSupportedOpCode(ILOpCode opCode) { switch (opCode) { case ILOpCode.Call: case ILOpCode.Callvirt: case ILOpCode.Ldtoken: case ILOpCode.Ldftn: case ILOpCode.Ldvirtftn: case ILOpCode.Newobj: return true; default: return false; } } static bool IsSameMember(IMember analyzedMethod, IMember m) { return m.MetadataToken == analyzedMethod.MetadataToken && m.ParentModule?.MetadataFile == analyzedMethod.ParentModule!.MetadataFile; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs ================================================ // 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.Composition; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Uses", Order = 10)] [Shared] class MethodUsesAnalyzer : IAnalyzer { public bool Show(ISymbol symbol) => symbol is IMethod method && method.HasBody; public IEnumerable Analyze(ISymbol symbol, AnalyzerContext context) { if (symbol is IMethod method && method.ParentModule?.MetadataFile is MetadataFile corFile) { var typeSystem = context.GetOrCreateTypeSystem(corFile); return context.Language.GetCodeMappingInfo(corFile, method.MetadataToken) .GetMethodParts((MethodDefinitionHandle)method.MetadataToken) .SelectMany(h => ScanMethod(h, typeSystem)).Distinct(); } throw new InvalidOperationException("Should never happen."); } IEnumerable ScanMethod(MethodDefinitionHandle handle, DecompilerTypeSystem typeSystem) { var module = typeSystem.MainModule; var md = module.MetadataFile.Metadata.GetMethodDefinition(handle); if (!md.HasBody()) yield break; BlobReader blob; try { blob = module.MetadataFile.GetMethodBody(md.RelativeVirtualAddress).GetILReader(); } catch (BadImageFormatException) { yield break; } var visitor = new TypeDefinitionCollector(); var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer while (blob.RemainingBytes > 0) { ILOpCode opCode; try { opCode = blob.DecodeOpCode(); } catch (BadImageFormatException) { yield break; } switch (opCode.GetOperandType()) { case OperandType.Field: case OperandType.Method: case OperandType.Sig: case OperandType.Tok: var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (member.IsNil) continue; switch (member.Kind) { case HandleKind.StandaloneSignature: break; case HandleKind.TypeDefinition: case HandleKind.TypeReference: case HandleKind.TypeSpecification: IType? ty; try { ty = module.ResolveType(member, genericContext); } catch (BadImageFormatException) { ty = null; } ty?.AcceptVisitor(visitor); break; case HandleKind.MethodDefinition: case HandleKind.MethodSpecification: case HandleKind.MemberReference: case HandleKind.FieldDefinition: IEntity? m; try { m = module.ResolveEntity(member, genericContext); } catch (BadImageFormatException) { m = null; } if (m != null) yield return m; break; } break; default: try { ILParser.SkipOperand(ref blob, opCode); } catch (BadImageFormatException) { yield break; } break; } } foreach (var type in visitor.UsedTypes) { yield return type; } } class TypeDefinitionCollector : TypeVisitor { public readonly List UsedTypes = new List(); public override IType VisitTypeDefinition(ITypeDefinition type) { UsedTypes.Add(type); return base.VisitTypeDefinition(type); } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Used By", Order = 20)] [Shared] class MethodVirtualUsedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; public bool Show(ISymbol symbol) => symbol is IMethod method && method.IsVirtual; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IMethod); var analyzedMethod = (IMethod)analyzedSymbol; if (analyzedMethod.ParentModule?.MetadataFile == null) yield break; if (analyzedMethod.DeclaringTypeDefinition?.MetadataToken.IsNil != false) yield break; var mapping = context.Language .GetCodeMappingInfo(analyzedMethod.ParentModule.MetadataFile, analyzedMethod.DeclaringTypeDefinition?.MetadataToken ?? default); var parentMethod = mapping.GetParentMethod((MethodDefinitionHandle)analyzedMethod.MetadataToken); if (parentMethod != analyzedMethod.MetadataToken) yield return ((MetadataModule)analyzedMethod.ParentModule).GetDefinition(parentMethod); var scope = context.GetScopeOf(analyzedMethod); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { var parentModule = (MetadataModule?)type.ParentModule; if (parentModule?.MetadataFile == null) continue; mapping = context.Language.GetCodeMappingInfo(parentModule.MetadataFile, type.MetadataToken); var methods = type.GetMembers(m => m is IMethod, Options).OfType(); foreach (var method in methods) { if (IsUsedInMethod((IMethod)analyzedSymbol, method, context)) { var parent = mapping.GetParentMethod((MethodDefinitionHandle)method.MetadataToken); yield return parentModule.GetDefinition(parent); } } foreach (var property in type.Properties) { if (property.CanGet && IsUsedInMethod((IMethod)analyzedSymbol, property.Getter, context)) { yield return property; continue; } if (property.CanSet && IsUsedInMethod((IMethod)analyzedSymbol, property.Setter, context)) { yield return property; continue; } } foreach (var @event in type.Events) { if (@event.CanAdd && IsUsedInMethod((IMethod)analyzedSymbol, @event.AddAccessor, context)) { yield return @event; continue; } if (@event.CanRemove && IsUsedInMethod((IMethod)analyzedSymbol, @event.RemoveAccessor, context)) { yield return @event; continue; } if (@event.CanInvoke && IsUsedInMethod((IMethod)analyzedSymbol, @event.InvokeAccessor, context)) { yield return @event; continue; } } } } bool IsUsedInMethod(IMethod analyzedEntity, IMethod method, AnalyzerContext context) { return ScanMethodBody(analyzedEntity, method, context.GetMethodBody(method)); } static bool ScanMethodBody(IMethod analyzedMethod, IMethod method, MethodBodyBlock? methodBody) { if (methodBody == null || method.ParentModule?.MetadataFile == null) return false; var mainModule = (MetadataModule)method.ParentModule; var blob = methodBody.GetILReader(); var genericContext = new Decompiler.TypeSystem.GenericContext(); while (blob.RemainingBytes > 0) { var opCode = blob.DecodeOpCode(); switch (opCode.GetOperandType()) { case OperandType.Method: case OperandType.Sig: case OperandType.Tok: var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); if (member.IsNil) continue; switch (member.Kind) { case HandleKind.MethodDefinition: case HandleKind.MethodSpecification: case HandleKind.MemberReference: var m = (mainModule.ResolveEntity(member, genericContext) as IMember)?.MemberDefinition; if (m != null && m.MetadataToken == analyzedMethod.MetadataToken && m.ParentModule?.MetadataFile == analyzedMethod.ParentModule!.MetadataFile) { return true; } break; } break; default: ILParser.SkipOperand(ref blob, opCode); break; } } return false; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyImplementedByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows properties that implement an interface property. /// [ExportAnalyzer(Header = "Implemented By", Order = 10)] [Shared] class PropertyImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IProperty); var scope = context.GetScopeOf((IProperty)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IProperty)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IProperty analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var property in type.Properties) { var baseMembers = InheritanceHelper.GetBaseMembers(property, true); if (baseMembers.Any(m => m.MetadataToken == token && m.ParentModule?.MetadataFile == module)) yield return property; } } public bool Show(ISymbol symbol) { return symbol is IProperty entity && entity.DeclaringType.Kind == TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows properties that override a property. /// [ExportAnalyzer(Header = "Overridden By", Order = 20)] [Shared] class PropertyOverriddenByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is IProperty); var scope = context.GetScopeOf((IProperty)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in AnalyzeType((IProperty)analyzedSymbol, type)) yield return result; } } IEnumerable AnalyzeType(IProperty analyzedEntity, ITypeDefinition type) { if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null) yield break; var token = analyzedEntity.MetadataToken; var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken; var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile; var allTypes = type.GetAllBaseTypeDefinitions(); if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module)) yield break; foreach (var property in type.Properties) { if (!property.IsOverride) continue; var baseMembers = InheritanceHelper.GetBaseMembers(property, false); if (baseMembers.Any(p => p.MetadataToken == token && p.ParentModule?.MetadataFile == module)) { yield return property; } } } public bool Show(ISymbol entity) { return entity is IProperty property && property.IsOverridable && property.DeclaringType.Kind != TypeKind.Interface; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Finds all entities that expose a type. /// [ExportAnalyzer(Header = "Exposed By", Order = 40)] [Shared] class TypeExposedByAnalyzer : IAnalyzer { public bool Show(ISymbol entity) => entity is ITypeDefinition; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is ITypeDefinition); var scope = context.GetScopeOf((ITypeDefinition)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in ScanType((ITypeDefinition)analyzedSymbol, type, context)) yield return result; } } IEnumerable ScanType(ITypeDefinition analyzedType, ITypeDefinition type, AnalyzerContext context) { if (analyzedType.Kind == TypeKind.Enum && type.MetadataToken == analyzedType.MetadataToken && type.ParentModule?.MetadataFile == analyzedType.ParentModule?.MetadataFile) yield break; if (!context.Language.ShowMember(type)) yield break; var visitor = new TypeDefinitionUsedVisitor(analyzedType, true); foreach (IField field in type.Fields) { if (TypeIsExposedBy(visitor, field)) yield return field; } foreach (IProperty property in type.Properties) { if (TypeIsExposedBy(visitor, property)) yield return property; } foreach (IEvent @event in type.Events) { if (TypeIsExposedBy(visitor, @event)) yield return @event; } foreach (IMethod method in type.Methods) { if (TypeIsExposedBy(visitor, method)) yield return method; } } bool TypeIsExposedBy(TypeDefinitionUsedVisitor visitor, IField field) { if (field.Accessibility == Accessibility.Private) return false; visitor.Found = false; field.ReturnType.AcceptVisitor(visitor); return visitor.Found; } bool TypeIsExposedBy(TypeDefinitionUsedVisitor visitor, IProperty property) { if (property.Accessibility == Accessibility.Private) { if (!property.IsExplicitInterfaceImplementation) return false; } visitor.Found = false; property.ReturnType.AcceptVisitor(visitor); foreach (var p in property.Parameters) { p.Type.AcceptVisitor(visitor); } return visitor.Found; } bool TypeIsExposedBy(TypeDefinitionUsedVisitor visitor, IEvent @event) { if (@event.Accessibility == Accessibility.Private) { if (!@event.IsExplicitInterfaceImplementation) return false; } visitor.Found = false; @event.ReturnType.AcceptVisitor(visitor); return visitor.Found; } bool TypeIsExposedBy(TypeDefinitionUsedVisitor visitor, IMethod method) { if (method.Accessibility == Accessibility.Private) { if (!method.IsExplicitInterfaceImplementation) return false; } visitor.Found = false; method.ReturnType.AcceptVisitor(visitor); foreach (var p in method.Parameters) { p.Type.AcceptVisitor(visitor); } return visitor.Found; } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs ================================================ // 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.Collections.Generic; using System.Composition; using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Finds all extension methods defined for a type. /// [ExportAnalyzer(Header = "Extension Methods", Order = 50)] [Shared] class TypeExtensionMethodsAnalyzer : IAnalyzer { public bool Show(ISymbol symbol) => symbol is ITypeDefinition entity && !entity.IsStatic; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is ITypeDefinition); var scope = context.GetScopeOf((ITypeDefinition)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { foreach (var result in ScanType((ITypeDefinition)analyzedSymbol, type, context)) yield return result; } } IEnumerable ScanType(ITypeDefinition analyzedType, ITypeDefinition type, AnalyzerContext context) { if (!type.HasExtensions) yield break; if (analyzedType.ParentModule?.MetadataFile == null) yield break; foreach (IMethod method in type.Methods) { if (!method.IsExtensionMethod) continue; var firstParamType = method.Parameters[0].Type.GetDefinition(); if (firstParamType != null && firstParamType.MetadataToken == analyzedType.MetadataToken && firstParamType.ParentModule?.MetadataFile == analyzedType.ParentModule.MetadataFile) yield return method; } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs ================================================ // 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.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows methods that instantiate a type. /// [ExportAnalyzer(Header = "Instantiated By", Order = 20)] [Shared] class TypeInstantiatedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is ITypeDefinition); var scope = context.GetScopeOf((ITypeDefinition)analyzedSymbol); foreach (var type in scope.GetTypesInScope(context.CancellationToken)) { if (type.ParentModule?.MetadataFile == null) continue; var mappingInfo = context.Language.GetCodeMappingInfo(type.ParentModule.MetadataFile, type.MetadataToken); var methods = type.GetMembers(m => m is IMethod, Options).OfType(); foreach (var method in methods) { if (IsUsedInMethod((ITypeDefinition)analyzedSymbol, method, mappingInfo, context)) yield return method; } foreach (var property in type.Properties) { if (property.CanGet && IsUsedInMethod((ITypeDefinition)analyzedSymbol, property.Getter, mappingInfo, context)) { yield return property; continue; } if (property.CanSet && IsUsedInMethod((ITypeDefinition)analyzedSymbol, property.Setter, mappingInfo, context)) { yield return property; continue; } } foreach (var @event in type.Events) { if (@event.CanAdd && IsUsedInMethod((ITypeDefinition)analyzedSymbol, @event.AddAccessor, mappingInfo, context)) { yield return @event; continue; } if (@event.CanRemove && IsUsedInMethod((ITypeDefinition)analyzedSymbol, @event.RemoveAccessor, mappingInfo, context)) { yield return @event; continue; } if (@event.CanInvoke && IsUsedInMethod((ITypeDefinition)analyzedSymbol, @event.InvokeAccessor, mappingInfo, context)) { yield return @event; continue; } } } } bool IsUsedInMethod(ITypeDefinition analyzedEntity, IMethod method, CodeMappingInfo mappingInfo, AnalyzerContext context) { return ScanMethodBody(analyzedEntity, method, context.GetMethodBody(method)); } bool ScanMethodBody(ITypeDefinition analyzedEntity, IMethod method, MethodBodyBlock? methodBody) { if (methodBody == null || method.ParentModule?.MetadataFile == null) return false; var blob = methodBody.GetILReader(); var module = (MetadataModule)method.ParentModule; var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer while (blob.RemainingBytes > 0) { ILOpCode opCode; EntityHandle handle; try { opCode = blob.DecodeOpCode(); switch (opCode) { case ILOpCode.Newobj: case ILOpCode.Call when analyzedEntity.Kind == TypeKind.Struct: case ILOpCode.Initobj: handle = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); break; default: blob.SkipOperand(opCode); continue; } } catch (BadImageFormatException) { continue; } ITypeDefinition? foundTypeDefinition; try { switch (handle.Kind) { case HandleKind.MethodDefinition: case HandleKind.MemberReference: var ctor = module.ResolveMethod(handle, genericContext); if (ctor == null || !ctor.IsConstructor) { continue; } foundTypeDefinition = ctor.DeclaringTypeDefinition; break; case HandleKind.TypeDefinition: case HandleKind.TypeReference: foundTypeDefinition = module.ResolveType(handle, genericContext)?.GetDefinition(); break; default: continue; } } catch (BadImageFormatException) { continue; } if (foundTypeDefinition?.ParentModule == null) { continue; } if (foundTypeDefinition.MetadataToken == analyzedEntity.MetadataToken && foundTypeDefinition.ParentModule.MetadataFile == analyzedEntity.ParentModule!.MetadataFile) { return true; } } return false; } public bool Show(ISymbol symbol) => symbol is ITypeDefinition entity && !entity.IsAbstract && !entity.IsStatic; } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs ================================================ // 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.Collections.Immutable; using System.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { /// /// Shows entities that use a type. /// [ExportAnalyzer(Header = "Used By", Order = 30)] [Shared] class TypeUsedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { Debug.Assert(analyzedSymbol is ITypeDefinition); var analyzedType = (ITypeDefinition)analyzedSymbol; var scope = context.GetScopeOf(analyzedType); context.SortResults = true; return scope.GetModulesInScope(context.CancellationToken) .AsParallel().AsOrdered() .SelectMany(AnalyzeModuleAndFilter); IEnumerable AnalyzeModuleAndFilter(MetadataFile module) { return AnalyzeModule(analyzedType, scope, module) .Distinct() .Where(s => !analyzedType.Equals((s as IEntity)?.DeclaringTypeDefinition)); } } static IEnumerable AnalyzeModule(ITypeDefinition analyzedType, AnalyzerScope scope, MetadataFile module) { var metadata = module.Metadata; var typeSystem = scope.ConstructTypeSystem(module); var decoder = new FindTypeDecoder(typeSystem.MainModule, analyzedType); //// resolve type refs //int rowCount = metadata.GetTableRowCount(TableIndex.TypeRef); //BitSet typeReferences = new BitSet(rowCount); //for (int row = 0; row < rowCount; row++) //{ // var h = MetadataTokens.TypeReferenceHandle(row + 1); // typeReferences[row] = decoder.GetTypeFromReference(metadata, h, 0); //} //// resolve type specs //rowCount = metadata.GetTableRowCount(TableIndex.TypeSpec); //BitSet typeSpecifications = new BitSet(rowCount); //for (int row = 0; row < rowCount; row++) //{ // var h = MetadataTokens.TypeSpecificationHandle(row + 1); // typeSpecifications[row] = decoder.GetTypeFromSpecification(metadata, default, h, 0); //} foreach (ISymbol result in FindUsesInAttributes(typeSystem, metadata, decoder, analyzedType)) yield return result; foreach (var h in metadata.TypeDefinitions) { var td = metadata.GetTypeDefinition(h); bool found = decoder.GetTypeFromEntity(metadata, td.GetBaseTypeOrNil()); foreach (var ih in td.GetInterfaceImplementations()) { var ii = metadata.GetInterfaceImplementation(ih); found |= decoder.GetTypeFromEntity(metadata, ii.Interface); } found |= FindUsesInGenericConstraints(metadata, td.GetGenericParameters(), decoder); if (found) yield return typeSystem.MainModule.GetDefinition(h); } foreach (var h in metadata.MethodDefinitions) { var md = metadata.GetMethodDefinition(h); var msig = md.DecodeSignature(decoder, default); bool found = FindTypeDecoder.AnyInMethodSignature(msig); found |= FindUsesInGenericConstraints(metadata, md.GetGenericParameters(), decoder); if (found || ScanMethodBody(analyzedType, module, md, decoder)) { var method = typeSystem.MainModule.GetDefinition(h); yield return method.AccessorOwner ?? method; } } foreach (var h in metadata.FieldDefinitions) { var fd = metadata.GetFieldDefinition(h); if (fd.DecodeSignature(decoder, default)) yield return typeSystem.MainModule.GetDefinition(h); } foreach (var h in metadata.PropertyDefinitions) { var pd = metadata.GetPropertyDefinition(h); var psig = pd.DecodeSignature(decoder, default); if (FindTypeDecoder.AnyInMethodSignature(psig)) yield return typeSystem.MainModule.GetDefinition(h); } foreach (var h in metadata.EventDefinitions) { var ed = metadata.GetEventDefinition(h); if (decoder.GetTypeFromEntity(metadata, ed.Type)) yield return typeSystem.MainModule.GetDefinition(h); } } static bool FindUsesInGenericConstraints(MetadataReader metadata, GenericParameterHandleCollection collection, FindTypeDecoder decoder) { foreach (var h in collection) { var gp = metadata.GetGenericParameter(h); foreach (var hc in gp.GetConstraints()) { var gc = metadata.GetGenericParameterConstraint(hc); if (decoder.GetTypeFromEntity(metadata, gc.Type)) return true; } } return false; } static IEnumerable FindUsesInAttributes(DecompilerTypeSystem typeSystem, MetadataReader metadata, FindTypeDecoder decoder, ITypeDefinition analyzedType) { var attrDecoder = new FindTypeInAttributeDecoder(typeSystem.MainModule, analyzedType); var referencedParameters = new HashSet(); foreach (var h in metadata.CustomAttributes) { var customAttribute = metadata.GetCustomAttribute(h); CustomAttributeValue value; try { value = customAttribute.DecodeValue(attrDecoder); } catch (EnumUnderlyingTypeResolveException) { continue; } if (AttributeAppliedToAnalyzer.IsCustomAttributeOfType(customAttribute.Constructor, metadata, decoder) || AnalyzeCustomAttributeValue(value)) { if (customAttribute.Parent.Kind == HandleKind.Parameter) { referencedParameters.Add((ParameterHandle)customAttribute.Parent); } else { var parent = AnalyzerHelpers.GetParentEntity(typeSystem, customAttribute); if (parent != null) yield return parent; } } } if (referencedParameters.Count > 0) { foreach (var h in metadata.MethodDefinitions) { var md = metadata.GetMethodDefinition(h); foreach (var p in md.GetParameters()) { if (referencedParameters.Contains(p)) { var method = typeSystem.MainModule.ResolveMethod(h, default); if (method != null) { if (method.IsAccessor) yield return method.AccessorOwner; else yield return method; } break; } } } } } private static bool AnalyzeCustomAttributeValue(CustomAttributeValue attribute) { foreach (var fa in attribute.FixedArguments) { if (CheckAttributeValue(fa.Value)) return true; } foreach (var na in attribute.NamedArguments) { if (CheckAttributeValue(na.Value)) return true; } return false; bool CheckAttributeValue(object? value) { if (value is TokenSearchResult typeofType) { if ((typeofType & TokenSearchResult.Found) != 0) return true; } else if (value is ImmutableArray> arr) { foreach (var element in arr) { if (CheckAttributeValue(element.Value)) return true; } } return false; } } static bool ScanMethodBody(ITypeDefinition analyzedType, MetadataFile module, in MethodDefinition md, FindTypeDecoder decoder) { if (!md.HasBody()) return false; var methodBody = module.GetMethodBody(md.RelativeVirtualAddress); var metadata = module.Metadata; if (!methodBody.LocalSignature.IsNil) { try { var ss = metadata.GetStandaloneSignature(methodBody.LocalSignature); if (HandleStandaloneSignature(ss)) { return true; } } catch (BadImageFormatException) { // Issue #2197: ignore invalid local signatures } } var blob = methodBody.GetILReader(); while (blob.RemainingBytes > 0) { var opCode = blob.DecodeOpCode(); switch (opCode.GetOperandType()) { case OperandType.Field: case OperandType.Method: case OperandType.Sig: case OperandType.Tok: case OperandType.Type: if (HandleMember(MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()))) return true; break; default: blob.SkipOperand(opCode); break; } } return false; bool HandleMember(EntityHandle member) { if (member.IsNil) return false; switch (member.Kind) { case HandleKind.TypeReference: return decoder.GetTypeFromReference(metadata, (TypeReferenceHandle)member, 0); case HandleKind.TypeSpecification: return decoder.GetTypeFromSpecification(metadata, default, (TypeSpecificationHandle)member, 0); case HandleKind.TypeDefinition: return decoder.GetTypeFromDefinition(metadata, (TypeDefinitionHandle)member, 0); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)member); return HandleMember(fd.GetDeclaringType()) || fd.DecodeSignature(decoder, default); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)member); if (HandleMember(md.GetDeclaringType())) return true; var msig = md.DecodeSignature(decoder, default); if (msig.ReturnType) return true; foreach (var t in msig.ParameterTypes) { if (t) return true; } break; case HandleKind.MemberReference: var mr = metadata.GetMemberReference((MemberReferenceHandle)member); if (HandleMember(mr.Parent)) return true; switch (mr.GetKind()) { case MemberReferenceKind.Method: msig = mr.DecodeMethodSignature(decoder, default); if (msig.ReturnType) return true; foreach (var t in msig.ParameterTypes) { if (t) return true; } break; case MemberReferenceKind.Field: return mr.DecodeFieldSignature(decoder, default); } break; case HandleKind.MethodSpecification: var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)member); if (HandleMember(ms.Method)) return true; var mssig = ms.DecodeSignature(decoder, default); foreach (var t in mssig) { if (t) return true; } break; case HandleKind.StandaloneSignature: var ss = metadata.GetStandaloneSignature((StandaloneSignatureHandle)member); return HandleStandaloneSignature(ss); } return false; } bool HandleStandaloneSignature(StandaloneSignature signature) { switch (signature.GetKind()) { case StandaloneSignatureKind.Method: var msig = signature.DecodeMethodSignature(decoder, default); if (msig.ReturnType) return true; foreach (var t in msig.ParameterTypes) { if (t) return true; } break; case StandaloneSignatureKind.LocalVariables: var sig = signature.DecodeLocalSignature(decoder, default); foreach (var t in sig) { if (t) return true; } break; } return false; } } public bool Show(ISymbol symbol) => symbol is ITypeDefinition; } class TypeDefinitionUsedVisitor : TypeVisitor { public readonly ITypeDefinition TypeDefinition; public bool Found { get; set; } readonly bool topLevelOnly; public TypeDefinitionUsedVisitor(ITypeDefinition definition, bool topLevelOnly) { this.TypeDefinition = definition; this.topLevelOnly = topLevelOnly; } public override IType VisitTypeDefinition(ITypeDefinition type) { Found |= TypeDefinition.MetadataToken == type.MetadataToken && TypeDefinition.ParentModule!.MetadataFile == type.ParentModule?.MetadataFile; return base.VisitTypeDefinition(type); } public override IType VisitParameterizedType(ParameterizedType type) { if (topLevelOnly) return type.GenericType.AcceptVisitor(this); return base.VisitParameterizedType(type); } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/ExportAnalyzerAttribute.cs ================================================ // 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.Composition; using System.Linq; using System.Reflection; namespace ICSharpCode.ILSpyX.Analyzers { [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ExportAnalyzerAttribute : ExportAttribute, IAnalyzerMetadata { public ExportAnalyzerAttribute() : base("Analyzer", typeof(IAnalyzer)) { } public required string Header { get; init; } public int Order { get; set; } public static IEnumerable<(ExportAnalyzerAttribute AttributeData, Type AnalyzerType)> GetAnnotatedAnalyzers() { foreach (var type in typeof(ExportAnalyzerAttribute).Assembly.GetTypes()) { if (type.GetCustomAttribute(typeof(ExportAnalyzerAttribute), false) is ExportAnalyzerAttribute exportAnalyzerAttribute) { yield return (exportAnalyzerAttribute, type); } } } } } ================================================ FILE: ICSharpCode.ILSpyX/Analyzers/IAnalyzer.cs ================================================ // 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.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers { /// /// Base interface for all analyzers. You can register an analyzer for any by implementing /// this interface and adding an . /// public interface IAnalyzer { /// /// Returns true, if the analyzer should be shown for a symbol, otherwise false. /// bool Show(ISymbol symbol); /// /// Returns all symbols found by this analyzer. /// IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context); } public interface IAnalyzerMetadata { string Header { get; } int Order { get; } } } ================================================ FILE: ICSharpCode.ILSpyX/ApiVisibility.cs ================================================ // 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 // 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. namespace ICSharpCode.ILSpyX { public enum ApiVisibility { PublicOnly, PublicAndInternal, All } } ================================================ FILE: ICSharpCode.ILSpyX/AssemblyList.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using ICSharpCode.ILSpyX.Extensions; using ICSharpCode.ILSpyX.FileLoaders; namespace ICSharpCode.ILSpyX { /// /// A list of assemblies. /// public sealed class AssemblyList { readonly Thread ownerThread; readonly SynchronizationContext? synchronizationContext; readonly AssemblyListManager manager; readonly string listName; /// Dirty flag, used to mark modifications so that the list is saved later bool dirty; readonly object lockObj = new object(); /// /// The assemblies in this list. /// Needs locking for multi-threaded access! /// Write accesses are allowed on the GUI thread only (but still need locking!) /// /// /// Technically read accesses need locking when done on non-GUI threads... but whenever possible, use the /// thread-safe method. /// readonly ObservableCollection assemblies = new ObservableCollection(); /// /// Assembly lookup by filename. /// Usually byFilename.Values == assemblies; but when an assembly is loaded by a background thread, /// that assembly is added to byFilename immediately, and to assemblies only later on the main thread. /// readonly Dictionary byFilename = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Exists for testing only. /// internal AssemblyList() { ownerThread = Thread.CurrentThread; manager = null!; listName = "Testing Only"; } internal AssemblyList(AssemblyListManager manager, string listName) { this.manager = manager ?? throw new ArgumentNullException(nameof(manager)); this.listName = listName; this.ApplyWinRTProjections = manager.ApplyWinRTProjections; this.UseDebugSymbols = manager.UseDebugSymbols; ownerThread = Thread.CurrentThread; synchronizationContext = SynchronizationContext.Current; assemblies.CollectionChanged += Assemblies_CollectionChanged; } /// /// Loads an assembly list from XML. /// internal AssemblyList(AssemblyListManager manager, XElement listElement) : this(manager, (string?)listElement.Attribute("name") ?? AssemblyListManager.DefaultListName) { foreach (var asm in listElement.Elements("Assembly")) { OpenAssembly((string)asm); } this.dirty = false; // OpenAssembly() sets dirty, so reset it afterwards } /// /// Creates a copy of an assembly list. /// public AssemblyList(AssemblyList list, string newName) : this(list.manager, newName) { lock (lockObj) { lock (list.lockObj) { this.assemblies.AddRange(list.assemblies); } } this.dirty = false; } public event NotifyCollectionChangedEventHandler CollectionChanged { add { VerifyAccess(); this.assemblies.CollectionChanged += value; } remove { VerifyAccess(); this.assemblies.CollectionChanged -= value; } } public bool ApplyWinRTProjections { get; set; } public bool UseDebugSymbols { get; set; } public FileLoaderRegistry LoaderRegistry => this.manager.LoaderRegistry; /// /// Gets the loaded assemblies. This method is thread-safe. /// public LoadedAssembly[] GetAssemblies() { lock (lockObj) { return assemblies.ToArray(); } } internal AssemblyListSnapshot GetSnapshot() { lock (lockObj) { return new AssemblyListSnapshot(assemblies.ToImmutableArray()); } } /// /// Gets all loaded assemblies recursively, including assemblies found in bundles or packages. /// public Task> GetAllAssemblies() { return GetSnapshot().GetAllAssembliesAsync(); } public int Count { get { lock (lockObj) { return assemblies.Count; } } } /// /// Saves this assembly list to XML. /// internal XElement SaveAsXml() { return new XElement( "List", new XAttribute("name", this.ListName), assemblies.Where(asm => !asm.IsAutoLoaded).Select(asm => new XElement("Assembly", asm.FileName)) ); } /// /// Gets the name of this list. /// public string ListName { get { return listName; } } public void Move(LoadedAssembly[] assembliesToMove, int index) { VerifyAccess(); lock (lockObj) { foreach (LoadedAssembly asm in assembliesToMove) { int nodeIndex = assemblies.IndexOf(asm); Debug.Assert(nodeIndex >= 0); if (nodeIndex < index) index--; assemblies.RemoveAt(nodeIndex); } Array.Reverse(assembliesToMove); foreach (LoadedAssembly asm in assembliesToMove) { assemblies.Insert(index, asm); } } } void Assemblies_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { Debug.Assert(Monitor.IsEntered(lockObj)); if (CollectionChangeHasEffectOnSave(e)) { RefreshSave(); } } static bool CollectionChangeHasEffectOnSave(NotifyCollectionChangedEventArgs e) { // Auto-loading dependent assemblies shouldn't trigger saving the assembly list switch (e.Action) { case NotifyCollectionChangedAction.Add: return e.NewItems.EmptyIfNull().Cast().Any(asm => !asm.IsAutoLoaded); case NotifyCollectionChangedAction.Remove: return e.OldItems.EmptyIfNull().Cast().Any(asm => !asm.IsAutoLoaded); default: return true; } } public void RefreshSave() { // Whenever the assembly list is modified, mark it as dirty // and enqueue a task that saves it once the UI has finished modifying the assembly list. if (!dirty) { dirty = true; BeginInvoke( delegate { if (dirty) { dirty = false; this.manager.SaveList(this); } } ); } } /// /// Find an assembly that was previously opened. /// public LoadedAssembly? FindAssembly(string file) { file = Path.GetFullPath(file); lock (lockObj) { if (byFilename.TryGetValue(file, out var asm)) return asm; } return null; } public LoadedAssembly Open(string assemblyUri, bool isAutoLoaded = false) { return OpenAssembly(assemblyUri, isAutoLoaded); } /// /// Opens an assembly from disk. /// Returns the existing assembly node if it is already loaded. /// /// /// If called on the UI thread, the newly opened assembly is added to the list synchronously. /// If called on another thread, the newly opened assembly won't be returned by GetAssemblies() /// until the UI thread gets around to adding the assembly. /// public LoadedAssembly OpenAssembly(string file, bool isAutoLoaded = false) { file = Path.GetFullPath(file); return OpenAssembly(file, () => { var newAsm = new LoadedAssembly(this, file, fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols) { IsAutoLoaded = isAutoLoaded }; return newAsm; }); } /// /// Opens an assembly from a stream. /// public LoadedAssembly OpenAssembly(string file, Stream? stream, bool isAutoLoaded = false) { file = Path.GetFullPath(file); return OpenAssembly(file, () => { var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream), fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols); newAsm.IsAutoLoaded = isAutoLoaded; return newAsm; }); } LoadedAssembly OpenAssembly(string file, Func load) { bool isUIThread = ownerThread == Thread.CurrentThread; LoadedAssembly? asm; lock (lockObj) { if (byFilename.TryGetValue(file, out asm)) return asm; asm = load(); Debug.Assert(asm.FileName == file); byFilename.Add(asm.FileName, asm); if (isUIThread) { assemblies.Add(asm); } } if (!isUIThread) { BeginInvoke(delegate () { lock (lockObj) { assemblies.Add(asm); } }); } return asm; } /// /// Replace the assembly object model from a crafted stream, without disk I/O /// Returns null if it is not already loaded. /// public LoadedAssembly? HotReplaceAssembly(string file, Stream stream) { VerifyAccess(); file = Path.GetFullPath(file); lock (lockObj) { if (!byFilename.TryGetValue(file, out LoadedAssembly? target)) return null; int index = this.assemblies.IndexOf(target); if (index < 0) return null; var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream), fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols); newAsm.IsAutoLoaded = target.IsAutoLoaded; Debug.Assert(newAsm.FileName == file); byFilename[file] = newAsm; this.assemblies[index] = newAsm; return newAsm; } } public LoadedAssembly? ReloadAssembly(string file) { VerifyAccess(); file = Path.GetFullPath(file); var target = this.assemblies.FirstOrDefault(asm => file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase)); if (target == null) return null; return ReloadAssembly(target); } public LoadedAssembly? ReloadAssembly(LoadedAssembly target) { VerifyAccess(); var index = this.assemblies.IndexOf(target); if (index < 0) return null; var newAsm = new LoadedAssembly(this, target.FileName, pdbFileName: target.PdbFileName, fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols); newAsm.IsAutoLoaded = target.IsAutoLoaded; lock (lockObj) { this.assemblies.Remove(target); this.assemblies.Insert(index, newAsm); } return newAsm; } public void Unload(LoadedAssembly assembly) { VerifyAccess(); lock (lockObj) { assemblies.Remove(assembly); byFilename.Remove(assembly.FileName); } } public void Clear() { VerifyAccess(); lock (lockObj) { assemblies.Clear(); byFilename.Clear(); } } public void Sort(IComparer comparer) { Sort(0, int.MaxValue, comparer); } public void Sort(int index, int count, IComparer comparer) { VerifyAccess(); lock (lockObj) { List list = new List(assemblies); list.Sort(index, Math.Min(count, list.Count - index), comparer); assemblies.Clear(); assemblies.AddRange(list); } } private void BeginInvoke(Action action) { if (synchronizationContext == null) { action(); } else { synchronizationContext.Post(new SendOrPostCallback(_ => action()), null); } } private void VerifyAccess() { if (this.ownerThread != Thread.CurrentThread) throw new InvalidOperationException("This method must always be called on the thread that owns the assembly list: " + ownerThread.ManagedThreadId + " " + ownerThread.Name); } } } ================================================ FILE: ICSharpCode.ILSpyX/AssemblyListManager.cs ================================================ // 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 // 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.ObjectModel; using System.IO; using System.Linq; using System.Xml.Linq; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpyX { /// /// Manages the available assembly lists. /// /// Contains the list of list names; and provides methods for loading/saving and creating/deleting lists. /// public sealed class AssemblyListManager { public const string DotNet4List = ".NET 4 (WPF)"; public const string DotNet35List = ".NET 3.5"; public const string ASPDotNetMVC3List = "ASP.NET (MVC3)"; private readonly ISettingsProvider settingsProvider; public AssemblyListManager(ISettingsProvider settingsProvider) { this.settingsProvider = settingsProvider; XElement doc = this.settingsProvider["AssemblyLists"]; foreach (var list in doc.Elements("List")) { var name = (string?)list.Attribute("name"); if (name != null) { AssemblyLists.Add(name); } } } public bool ApplyWinRTProjections { get; set; } public bool UseDebugSymbols { get; set; } public ObservableCollection AssemblyLists { get; } = []; public FileLoaderRegistry LoaderRegistry { get; } = new(); /// /// Loads an assembly list from the ILSpySettings. /// If no list with the specified name is found, the default list is loaded instead. /// public AssemblyList LoadList(string listName) { AssemblyList list = DoLoadList(listName); if (!AssemblyLists.Contains(list.ListName)) AssemblyLists.Add(list.ListName); return list; } AssemblyList DoLoadList(string? listName) { XElement doc = this.settingsProvider["AssemblyLists"]; if (listName != null) { foreach (var list in doc.Elements("List")) { if ((string?)list.Attribute("name") == listName) { return new AssemblyList(this, list); } } } return new AssemblyList(this, listName ?? DefaultListName); } public bool CloneList(string selectedAssemblyList, string newListName) { var list = DoLoadList(selectedAssemblyList); var newList = new AssemblyList(list, newListName); return AddListIfNotExists(newList); } public bool RenameList(string selectedAssemblyList, string newListName) { var list = DoLoadList(selectedAssemblyList); var newList = new AssemblyList(list, newListName); return DeleteList(selectedAssemblyList) && AddListIfNotExists(newList); } public const string DefaultListName = "(Default)"; /// /// Saves the specified assembly list into the config file. /// public void SaveList(AssemblyList list) { this.settingsProvider.Update( root => { XElement? doc = root.Element("AssemblyLists"); if (doc == null) { doc = new XElement("AssemblyLists"); root.Add(doc); } XElement? listElement = doc.Elements("List") .FirstOrDefault(e => (string?)e.Attribute("name") == list.ListName); if (listElement != null) listElement.ReplaceWith(list.SaveAsXml()); else doc.Add(list.SaveAsXml()); }); } public bool AddListIfNotExists(AssemblyList list) { if (!AssemblyLists.Contains(list.ListName)) { AssemblyLists.Add(list.ListName); SaveList(list); return true; } return false; } public bool DeleteList(string Name) { if (AssemblyLists.Remove(Name)) { this.settingsProvider.Update( delegate (XElement root) { XElement? doc = root.Element("AssemblyLists"); if (doc == null) { return; } XElement? listElement = doc.Elements("List").FirstOrDefault(e => (string?)e.Attribute("name") == Name); if (listElement != null) listElement.Remove(); }); return true; } return false; } public void ClearAll() { AssemblyLists.Clear(); this.settingsProvider.Update( root => { XElement? doc = root.Element("AssemblyLists"); doc?.Remove(); }); } public void CreateDefaultAssemblyLists() { if (AssemblyLists.Count > 0) return; if (!AssemblyLists.Contains(DotNet4List)) { AssemblyList dotnet4 = CreateDefaultList(DotNet4List); if (dotnet4.Count > 0) { AddListIfNotExists(dotnet4); } } if (!AssemblyLists.Contains(DotNet35List)) { AssemblyList dotnet35 = CreateDefaultList(DotNet35List); if (dotnet35.Count > 0) { AddListIfNotExists(dotnet35); } } if (!AssemblyLists.Contains(ASPDotNetMVC3List)) { AssemblyList mvc = CreateDefaultList(ASPDotNetMVC3List); if (mvc.Count > 0) { AddListIfNotExists(mvc); } } } public AssemblyList CreateList(string name) { return new AssemblyList(this, name); } public AssemblyList CreateDefaultList(string name, string? path = null, string? newName = null) { var list = new AssemblyList(this, newName ?? name); switch (name) { case DotNet4List: AddToListFromGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); break; case DotNet35List: AddToListFromGAC("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); break; case ASPDotNetMVC3List: AddToListFromGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.DynamicData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); AddToListFromGAC("System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); AddToListFromGAC("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); AddToListFromGAC("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); break; case object _ when path != null: foreach (var file in Directory.GetFiles(path, "*.dll")) { var dllname = Path.GetFileName(file); if (DoIncludeFile(dllname)) AddToListFromDirectory(file); } break; } return list; void AddToListFromGAC(string fullName) { AssemblyNameReference reference = AssemblyNameReference.Parse(fullName); string? file = UniversalAssemblyResolver.GetAssemblyInGac(reference); if (file != null) list.OpenAssembly(file); } void AddToListFromDirectory(string file) { if (File.Exists(file)) list.OpenAssembly(file); } bool DoIncludeFile(string fileName) { if (fileName == "Microsoft.DiaSymReader.Native.amd64.dll") return false; if (fileName.EndsWith("_cor3.dll", StringComparison.OrdinalIgnoreCase)) return false; if (char.IsUpper(fileName[0])) return true; if (fileName == "netstandard.dll") return true; if (fileName == "mscorlib.dll") return true; return false; } } } } ================================================ FILE: ICSharpCode.ILSpyX/AssemblyListSnapshot.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpyX.Extensions; using ICSharpCode.ILSpyX.FileLoaders; namespace ICSharpCode.ILSpyX { class AssemblyListSnapshot { readonly ImmutableArray assemblies; Dictionary? asmLookupByFullName; Dictionary? asmLookupByShortName; Dictionary>? asmLookupByShortNameGrouped; public ImmutableArray Assemblies => assemblies; public AssemblyListSnapshot(ImmutableArray assemblies) { this.assemblies = assemblies; } public async Task TryGetModuleAsync(IAssemblyReference reference, string tfm) { bool isWinRT = reference.IsWindowsRuntime; if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal)) { tfm = ".NETFramework,Version=v4"; } string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName); var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName); if (lookup == null) { lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false); lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup); } if (lookup.TryGetValue(key, out MetadataFile? module)) return module; return null; } public async Task TryGetSimilarModuleAsync(IAssemblyReference reference) { var lookup = LazyInit.VolatileRead(ref asmLookupByShortNameGrouped); if (lookup == null) { lookup = await CreateLoadedAssemblyShortNameGroupLookupAsync().ConfigureAwait(false); lookup = LazyInit.GetOrSet(ref asmLookupByShortNameGrouped, lookup); } if (!lookup.TryGetValue(reference.Name, out var candidates)) return null; return candidates.FirstOrDefault(c => c.version >= reference.Version).module ?? candidates.Last().module; } private async Task> CreateLoadedAssemblyLookupAsync(bool shortNames) { var result = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (LoadedAssembly loaded in assemblies) { try { var module = await loaded.GetMetadataFileOrNullAsync().ConfigureAwait(false); if (module == null) continue; var reader = module.Metadata; if (reader == null || !reader.IsAssembly) continue; string tfm = await loaded.GetTargetFrameworkIdAsync().ConfigureAwait(false); if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal)) { tfm = ".NETFramework,Version=v4"; } string key = tfm + ";" + (shortNames ? module.Name : module.FullName); if (!result.ContainsKey(key)) { result.Add(key, module); } } catch (BadImageFormatException) { continue; } } return result; } private async Task>> CreateLoadedAssemblyShortNameGroupLookupAsync() { var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (LoadedAssembly loaded in assemblies) { try { var module = await loaded.GetMetadataFileOrNullAsync().ConfigureAwait(false); var reader = module?.Metadata; if (reader == null || !reader.IsAssembly) continue; var asmDef = reader.GetAssemblyDefinition(); var asmDefName = reader.GetString(asmDef.Name); var line = (module!, version: asmDef.Version); if (!result.TryGetValue(asmDefName, out var existing)) { existing = new List<(MetadataFile module, Version version)>(); result.Add(asmDefName, existing); existing.Add(line); continue; } int index = existing.BinarySearch(line.version, l => l.version); index = index < 0 ? ~index : index + 1; existing.Insert(index, line); } catch (BadImageFormatException) { continue; } } return result; } /// /// Gets all loaded assemblies recursively, including assemblies found in bundles or packages. /// public async Task> GetAllAssembliesAsync() { var results = new List(assemblies.Length); foreach (var asm in assemblies) { LoadResult result; try { result = await asm.GetLoadResultAsync().ConfigureAwait(false); } catch { results.Add(asm); continue; } if (result.Package != null) { AddDescendants(result.Package.RootFolder); } else if (result.MetadataFile != null) { results.Add(asm); } } void AddDescendants(PackageFolder folder) { foreach (var subFolder in folder.Folders) { AddDescendants(subFolder); } foreach (var entry in folder.Entries) { if (!entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !entry.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) continue; var asm = folder.ResolveFileName(entry.Name); if (asm == null) continue; results.Add(asm); } } return results; } } } ================================================ FILE: ICSharpCode.ILSpyX/Extensions/CollectionExtensions.cs ================================================ // Copyright (c) 2022 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; using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpyX.Extensions { public static class CollectionExtensions { internal static void AddRange(this ICollection list, IEnumerable items) { foreach (T item in items) if (!list.Contains(item)) list.Add(item); } public static T? PeekOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); return stack.Peek(); } public static int BinarySearch(this IList list, T item, int start, int count, IComparer comparer) { if (list == null) throw new ArgumentNullException(nameof(list)); if (start < 0 || start >= list.Count) throw new ArgumentOutOfRangeException(nameof(start), start, "Value must be between 0 and " + (list.Count - 1)); if (count < 0 || count > list.Count - start) throw new ArgumentOutOfRangeException(nameof(count), count, "Value must be between 0 and " + (list.Count - start)); int end = start + count - 1; while (start <= end) { int pivot = (start + end) / 2; int result = comparer.Compare(item, list[pivot]); if (result == 0) return pivot; if (result < 0) end = pivot - 1; else start = pivot + 1; } return ~start; } public static int BinarySearch(this IList instance, TKey itemKey, Func keySelector) where TKey : IComparable, IComparable { if (instance == null) throw new ArgumentNullException(nameof(instance)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); int start = 0; int end = instance.Count - 1; while (start <= end) { int m = (start + end) / 2; TKey key = keySelector(instance[m]); int result = key.CompareTo(itemKey); if (result == 0) return m; if (result < 0) start = m + 1; else end = m - 1; } return ~start; } public static void InsertSorted(this IList list, T item, IComparer comparer) { if (list == null) throw new ArgumentNullException(nameof(list)); if (comparer == null) throw new ArgumentNullException(nameof(comparer)); if (list.Count == 0) { list.Add(item); } else { int index = list.BinarySearch(item, 0, list.Count, comparer); list.Insert(index < 0 ? ~index : index, item); } } internal static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) { key = pair.Key; value = pair.Value; } internal static IEnumerable EmptyIfNull(this IEnumerable? inst) => inst ?? Enumerable.Empty(); internal static IEnumerable EmptyIfNull(this IEnumerable? inst) => inst ?? Enumerable.Empty(); internal static IList EmptyIfNull(this IList? inst) => inst ?? EmptyList.Instance; internal static IList EmptyIfNull(this IList? inst) => inst ?? Array.Empty(); } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/ArchiveFileLoader.cs ================================================ // Copyright (c) 2024 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.IO; using System.Threading.Tasks; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class ArchiveFileLoader : IFileLoader { public Task Load(string fileName, Stream stream, FileLoadContext settings) { if (settings.ParentBundle != null) { return Task.FromResult(null); } try { var zip = LoadedPackage.FromZipFile(fileName); var result = zip != null ? new LoadResult { Package = zip } : null; return Task.FromResult(result); } catch (InvalidDataException) { return Task.FromResult(null); } } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/BundleFileLoader.cs ================================================ // Copyright (c) 2024 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.IO; using System.Threading.Tasks; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class BundleFileLoader : IFileLoader { public Task Load(string fileName, Stream stream, FileLoadContext settings) { if (settings.ParentBundle != null) { return Task.FromResult(null); } var bundle = LoadedPackage.FromBundle(fileName); var result = bundle != null ? new LoadResult { Package = bundle } : null; return Task.FromResult(result); } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/FileLoaderRegistry.cs ================================================ // Copyright (c) 2024 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; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class FileLoaderRegistry { readonly List registeredLoaders = new List(); public IReadOnlyList RegisteredLoaders => registeredLoaders; public void Register(IFileLoader loader) { if (loader is null) { throw new ArgumentNullException(nameof(loader)); } registeredLoaders.Add(loader); } public FileLoaderRegistry() { Register(new XamarinCompressedFileLoader()); Register(new WebCilFileLoader()); Register(new MetadataFileLoader()); Register(new BundleFileLoader()); // bundles are PE files with a special signature, prefer over normal PE files Register(new PEFileLoader()); // prefer PE format over archives, because ZIP has no fixed header Register(new ArchiveFileLoader()); } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs ================================================ // Copyright (c) 2024 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.IO; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class LoadResult { public MetadataFile? MetadataFile { get; init; } public Exception? FileLoadException { get; init; } public LoadedPackage? Package { get; init; } public bool IsSuccess => FileLoadException == null; } public record FileLoadContext(bool ApplyWinRTProjections, LoadedAssembly? ParentBundle); public interface IFileLoader { Task Load(string fileName, Stream stream, FileLoadContext context); } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/MetadataFileLoader.cs ================================================ // Copyright (c) 2024 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.IO; using System.Reflection.Metadata; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; using static ICSharpCode.Decompiler.Metadata.MetadataFile; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class MetadataFileLoader : IFileLoader { public Task Load(string fileName, Stream stream, FileLoadContext settings) { try { var kind = Path.GetExtension(fileName).Equals(".pdb", StringComparison.OrdinalIgnoreCase) ? MetadataFileKind.ProgramDebugDatabase : MetadataFileKind.Metadata; var metadata = MetadataReaderProvider.FromMetadataStream(stream, MetadataStreamOptions.PrefetchMetadata | MetadataStreamOptions.LeaveOpen); var metadataFile = new MetadataFile(kind, fileName, metadata); return Task.FromResult(new LoadResult { MetadataFile = metadataFile }); } catch (BadImageFormatException) { return Task.FromResult(null); } } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/PEFileLoader.cs ================================================ // Copyright (c) 2024 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.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class PEFileLoader : IFileLoader { public async Task Load(string fileName, Stream stream, FileLoadContext context) { if (stream.Length < 2 || stream.ReadByte() != 'M' || stream.ReadByte() != 'Z') { return null; } return await LoadPEFile(fileName, stream, context).ConfigureAwait(false); } public static Task LoadPEFile(string fileName, Stream stream, FileLoadContext context) { MetadataReaderOptions options = context.ApplyWinRTProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None; stream.Position = 0; PEFile module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage | PEStreamOptions.LeaveOpen, metadataOptions: options); return Task.FromResult(new LoadResult { MetadataFile = module }); } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/WebCilFileLoader.cs ================================================ // Copyright (c) 2024 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.IO; using System.Reflection.Metadata; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class WebCilFileLoader : IFileLoader { public Task Load(string fileName, Stream stream, FileLoadContext settings) { if (settings.ParentBundle != null) { return Task.FromResult(null); } MetadataReaderOptions options = settings.ApplyWinRTProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None; var wasm = WebCilFile.FromFile(fileName, options); var result = wasm != null ? new LoadResult { MetadataFile = wasm } : null; return Task.FromResult(result); } } } ================================================ FILE: ICSharpCode.ILSpyX/FileLoaders/XamarinCompressedFileLoader.cs ================================================ // Copyright (c) 2024 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.Buffers; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.Metadata; using K4os.Compression.LZ4; namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class XamarinCompressedFileLoader : IFileLoader { public async Task Load(string fileName, Stream stream, FileLoadContext context) { const uint CompressedDataMagic = 0x5A4C4158; // Magic used for Xamarin compressed module header ('XALZ', little-endian) using var fileReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); // Read compressed file header var magic = fileReader.ReadUInt32(); if (magic != CompressedDataMagic) return null; _ = fileReader.ReadUInt32(); // skip index into descriptor table, unused int uncompressedLength = (int)fileReader.ReadUInt32(); int compressedLength = (int)stream.Length; // Ensure we read all of compressed data ArrayPool pool = ArrayPool.Shared; var src = pool.Rent(compressedLength); var dst = pool.Rent(uncompressedLength); try { // fileReader stream position is now at compressed module data await stream.ReadAsync(src, 0, compressedLength).ConfigureAwait(false); // Decompress LZ4Codec.Decode(src, 0, compressedLength, dst, 0, uncompressedLength); // Load module from decompressed data buffer using (var uncompressedStream = new MemoryStream(dst, writable: false)) { MetadataReaderOptions options = context.ApplyWinRTProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None; return new LoadResult { MetadataFile = new PEFile(fileName, uncompressedStream, PEStreamOptions.PrefetchEntireImage, metadataOptions: options) }; } } finally { pool.Return(dst); pool.Return(src); } } } } ================================================ FILE: ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj ================================================ net10.0 enable true nullable True ..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk en-US False False False ICSharpCode.ILSpyX 8.0.0.0-noversion ILSpyX Platform ILSpy Contributors MIT https://github.com/icsharpcode/ILSpy/ Core cross-platform library used by ILSpy. PackageReadme.md ic#code ILSpyX git https://github.com/icsharpcode/ILSpy.git ../ICSharpCode.Decompiler/DecompilerNuGetPackageIcon.png false Copyright 2022-$([System.DateTime]::Now.Year) AlphaSierraPapa C# Decompiler ILSpy true embedded true true true true true true ILSpyUpdateAssemblyInfo; $(GetPackageVersionDependsOn) all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive ================================================ FILE: ICSharpCode.ILSpyX/LanguageVersion.cs ================================================ // 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 // 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. namespace ICSharpCode.ILSpyX { /// /// Version-DisplayName pair used in UI scenarios, for example ILSpy's language version dropdown. /// public class LanguageVersion { public string Version { get; } public string DisplayName { get; } public LanguageVersion(string version, string? name = null) { Version = version ?? ""; DisplayName = name ?? Version.ToString(); } public override string ToString() { return $"[LanguageVersion DisplayName={DisplayName}, Version={Version}]"; } } } ================================================ FILE: ICSharpCode.ILSpyX/LoadedAssembly.cs ================================================ // 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 // 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.Diagnostics; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.PdbProvider; #nullable enable namespace ICSharpCode.ILSpyX { /// /// Represents a file loaded into ILSpy. /// /// Note: this class is misnamed. /// The file is not necessarily an assembly, nor is it necessarily loaded. /// /// A LoadedAssembly can refer to: /// * a .NET module (single-file) loaded into ILSpy /// * a non-existant file /// * a file of unknown format that could not be loaded /// * a .nupkg file or .NET core bundle /// * a standalone portable pdb file or metadata stream /// * a file that is still being loaded in the background /// [DebuggerDisplay("[LoadedAssembly {shortName}]")] public sealed class LoadedAssembly { /// /// Maps from MetadataFile (successfully loaded .NET module) back to the LoadedAssembly instance /// that was used to load the module. /// internal static readonly ConditionalWeakTable loadedAssemblies = new ConditionalWeakTable(); readonly Task loadingTask; readonly AssemblyList assemblyList; readonly string fileName; readonly string shortName; readonly IAssemblyResolver? providedAssemblyResolver; readonly FileLoaderRegistry? fileLoaders; readonly bool applyWinRTProjections; readonly bool useDebugSymbols; public LoadedAssembly? ParentBundle { get; } public LoadedAssembly(AssemblyList assemblyList, string fileName, Task? stream = null, FileLoaderRegistry? fileLoaders = null, IAssemblyResolver? assemblyResolver = null, string? pdbFileName = null, bool applyWinRTProjections = false, bool useDebugSymbols = false) { this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList)); this.fileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); this.PdbFileName = pdbFileName; this.providedAssemblyResolver = assemblyResolver; this.fileLoaders = fileLoaders; this.applyWinRTProjections = applyWinRTProjections; this.useDebugSymbols = useDebugSymbols; this.loadingTask = Task.Run(() => LoadAsync(stream)); // requires that this.fileName is set this.shortName = Path.GetFileNameWithoutExtension(fileName); } public LoadedAssembly(LoadedAssembly bundle, string fileName, Task? stream, FileLoaderRegistry? fileLoaders = null, IAssemblyResolver? assemblyResolver = null, bool applyWinRTProjections = false, bool useDebugSymbols = false) : this(bundle.assemblyList, fileName, stream, fileLoaders, assemblyResolver, null, applyWinRTProjections, useDebugSymbols) { this.ParentBundle = bundle; } string? targetFrameworkId; /// /// Returns a target framework identifier in the form '<framework>Version=v<version>'. /// Returns an empty string if no TargetFrameworkAttribute was found /// or the file doesn't contain an assembly header, i.e., is only a module. /// /// Throws an exception if the file does not contain any .NET metadata (e.g. file of unknown format). /// public async Task GetTargetFrameworkIdAsync() { var value = LazyInit.VolatileRead(ref targetFrameworkId); if (value == null) { var assembly = await GetMetadataFileAsync().ConfigureAwait(false); value = assembly.DetectTargetFrameworkId() ?? string.Empty; value = LazyInit.GetOrSet(ref targetFrameworkId, value); } return value; } string? runtimePack; public async Task GetRuntimePackAsync() { var value = LazyInit.VolatileRead(ref runtimePack); if (value == null) { var assembly = await GetMetadataFileAsync().ConfigureAwait(false); value = assembly.DetectRuntimePack() ?? string.Empty; value = LazyInit.GetOrSet(ref runtimePack, value); } return value; } public ReferenceLoadInfo LoadedAssemblyReferencesInfo { get; } = new ReferenceLoadInfo(); IDebugInfoProvider? debugInfoProvider; /// /// Gets the . /// public Task GetLoadResultAsync() { return loadingTask; } /// /// Gets the . /// public async Task GetMetadataFileAsync() { var loadResult = await loadingTask.ConfigureAwait(false); if (loadResult.MetadataFile != null) return loadResult.MetadataFile; else throw loadResult.FileLoadException ?? new MetadataFileNotSupportedException(); } /// /// Gets the . /// Returns null in case of load errors. /// public MetadataFile? GetMetadataFileOrNull() { try { var loadResult = loadingTask.GetAwaiter().GetResult(); return loadResult.MetadataFile; } catch (Exception ex) { System.Diagnostics.Trace.TraceError(ex.ToString()); return null; } } /// /// Gets the . /// Returns null in case of load errors. /// public async Task GetMetadataFileOrNullAsync() { try { var loadResult = await loadingTask.ConfigureAwait(false); return loadResult.MetadataFile; } catch (Exception ex) { System.Diagnostics.Trace.TraceError(ex.ToString()); return null; } } ICompilation? typeSystem; /// /// Gets a type system containing all types from this assembly + primitive types from mscorlib. /// Returns null in case of load errors. /// /// /// This is an uncached type system. /// public ICompilation? GetTypeSystemOrNull() { var value = Volatile.Read(ref this.typeSystem); if (value == null) { var module = GetMetadataFileOrNull(); if (module == null || module.IsMetadataOnly) return null; value = new SimpleCompilation( module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), MinimalCorlib.Instance); value = LazyInit.GetOrSet(ref this.typeSystem, value); } return value; } readonly object typeSystemWithOptionsLockObj = new object(); ICompilation? typeSystemWithOptions; TypeSystemOptions? currentTypeSystemOptions; public ICompilation? GetTypeSystemOrNull(TypeSystemOptions options) { lock (typeSystemWithOptionsLockObj) { if (typeSystemWithOptions != null && options == currentTypeSystemOptions) return typeSystemWithOptions; var module = GetMetadataFileOrNull(); if (module == null || module.IsMetadataOnly) return null; currentTypeSystemOptions = options; return typeSystemWithOptions = new SimpleCompilation( module.WithOptions(options | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), MinimalCorlib.Instance); } } public AssemblyList AssemblyList => assemblyList; public string FileName => fileName; public string ShortName => shortName; public string Text { get { if (IsLoaded && !HasLoadError) { var result = GetLoadResultAsync().GetAwaiter().GetResult(); if (result.MetadataFile != null) { switch (result.MetadataFile.Kind) { case MetadataFile.MetadataFileKind.PortableExecutable: var metadata = result.MetadataFile.Metadata; string? versionOrInfo; if (metadata.IsAssembly) { versionOrInfo = metadata.GetAssemblyDefinition().Version?.ToString(); string tfId = GetTargetFrameworkIdAsync().GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(tfId)) versionOrInfo += ", " + tfId.Replace("Version=", " "); } else { versionOrInfo = ".netmodule"; } if (versionOrInfo == null) return ShortName; return string.Format("{0} ({1})", ShortName, versionOrInfo); case MetadataFile.MetadataFileKind.ProgramDebugDatabase: return ShortName + " (Debug Metadata)"; case MetadataFile.MetadataFileKind.Metadata: return ShortName + " (Metadata)"; default: return ShortName; } } } return ShortName; } } /// /// Gets whether loading finished for this file (either successfully or unsuccessfully). /// public bool IsLoaded => loadingTask.IsCompleted; /// /// Gets whether this file was loaded successfully as an assembly (not as a bundle). /// public bool IsLoadedAsValidAssembly { get { return loadingTask.Status == TaskStatus.RanToCompletion && loadingTask.Result.MetadataFile is { IsMetadataOnly: false }; } } /// /// Gets whether loading failed (file does not exist, unknown file format). /// Returns false for valid assemblies and valid bundles. /// public bool HasLoadError => loadingTask.IsFaulted; public bool IsAutoLoaded { get; set; } /// /// Gets the PDB file name or null, if no PDB was found or it's embedded. /// public string? PdbFileName { get; private set; } async Task LoadAsync(Task? streamTask) { using var stream = await PrepareStream(); FileLoadContext settings = new FileLoadContext(applyWinRTProjections, ParentBundle); LoadResult? result = null; if (fileLoaders != null) { foreach (var loader in fileLoaders.RegisteredLoaders) { // In each iteration any of the following things may happen: // Load returns null because the loader is unable to handle the file, we simply continue without recording the result. // Load returns a non-null value that is either a valid result or an exception: // - if it's a success, we use that and end the loop, // - if it's an error, we remember the error, discarding any previous errors. // Load throws an exception, remember the error, discarding any previous errors. stream.Position = 0; try { var nextResult = await loader.Load(fileName, stream, settings).ConfigureAwait(false); if (nextResult != null) { result = nextResult; if (result.IsSuccess) { break; } } } catch (Exception ex) { result = new LoadResult { FileLoadException = ex }; } } } if (result?.IsSuccess != true) { stream.Position = 0; try { result = await PEFileLoader.LoadPEFile(fileName, stream, settings).ConfigureAwait(false); } catch (Exception ex) { result = new LoadResult { FileLoadException = ex }; } } if (result.MetadataFile != null) { lock (loadedAssemblies) { loadedAssemblies.Add(result.MetadataFile, this); } if (result.MetadataFile is PEFile module) { debugInfoProvider = LoadDebugInfo(module); } } else if (result.Package != null) { result.Package.LoadedAssembly = this; } else if (result.FileLoadException != null) { throw result.FileLoadException; } return result; async Task PrepareStream() { // runs on background thread var stream = streamTask != null ? await streamTask.ConfigureAwait(false) : null; if (stream != null) { // Read the module from a precrafted stream if (!stream.CanSeek) { var memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); stream.Close(); memoryStream.Position = 0; stream = memoryStream; } return stream; } else { return new FileStream(fileName, FileMode.Open, FileAccess.Read); } } } IDebugInfoProvider? LoadDebugInfo(PEFile? module) { if (module == null) { return null; } if (useDebugSymbols) { try { return (PdbFileName != null ? DebugInfoUtils.FromFile(module, PdbFileName) : null) ?? DebugInfoUtils.LoadSymbols(module); } catch (IOException) { } catch (UnauthorizedAccessException) { } catch (InvalidOperationException) { // ignore any errors during symbol loading } } return null; } public async Task LoadDebugInfo(string fileName) { this.PdbFileName = fileName; var assembly = await GetMetadataFileAsync().ConfigureAwait(false); debugInfoProvider = await Task.Run(() => LoadDebugInfo(assembly as PEFile)); return debugInfoProvider; } sealed class MyAssemblyResolver : IAssemblyResolver { readonly LoadedAssembly parent; readonly bool loadOnDemand; readonly bool applyWinRTProjections; readonly IAssemblyResolver? providedAssemblyResolver; readonly AssemblyList assemblyList; readonly AssemblyListSnapshot alreadyLoadedAssemblies; readonly Task tfmTask; readonly ReferenceLoadInfo referenceLoadInfo; public MyAssemblyResolver(LoadedAssembly parent, AssemblyListSnapshot assemblyListSnapshot, bool loadOnDemand, bool applyWinRTProjections) { this.parent = parent; this.loadOnDemand = loadOnDemand; this.applyWinRTProjections = applyWinRTProjections; this.providedAssemblyResolver = parent.providedAssemblyResolver; this.assemblyList = parent.assemblyList; // Note: we cache a copy of the assembly list in the constructor, so that the // resolve calls only search-by-asm-name in the assemblies that were already loaded // at the time of the GetResolver() call. this.alreadyLoadedAssemblies = assemblyListSnapshot; // If we didn't do this, we'd also search in the assemblies that we just started to load // in previous Resolve() calls; but we don't want to wait for those to be loaded. this.tfmTask = parent.GetTargetFrameworkIdAsync(); this.referenceLoadInfo = parent.LoadedAssemblyReferencesInfo; } public MetadataFile? Resolve(IAssemblyReference reference) { return ResolveAsync(reference).GetAwaiter().GetResult(); } /// /// 0) if we're inside a package, look for filename.dll in parent directories /// 1) try to find exact match by tfm + full asm name in loaded assemblies /// 2) try to find match in search paths /// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well /// 4) look in /dotnet/shared/{runtime-pack}/{closest-version} /// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs /// 7) Search the GAC /// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /// 9) try to find match by asm name (no tfm/version) in loaded assemblies /// public async Task ResolveAsync(IAssemblyReference reference) { MetadataFile? module; // 0) if we're inside a package, look for filename.dll in parent directories if (providedAssemblyResolver != null) { module = await providedAssemblyResolver.ResolveAsync(reference).ConfigureAwait(false); if (module != null) return module; } string tfm = await tfmTask.ConfigureAwait(false); // 1) try to find exact match by tfm + full asm name in loaded assemblies module = await alreadyLoadedAssemblies.TryGetModuleAsync(reference, tfm).ConfigureAwait(false); if (module != null) { referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Info, "Success - Found in Assembly List"); return module; } string? file = parent.GetUniversalResolver(applyWinRTProjections).FindAssemblyFile(reference); if (file != null) { // Load assembly from disk LoadedAssembly? asm; if (loadOnDemand) { asm = assemblyList.OpenAssembly(file, isAutoLoaded: true); } else { asm = assemblyList.FindAssembly(file); } if (asm != null) { referenceLoadInfo.AddMessage(reference.FullName, MessageKind.Info, "Success - Loading from: " + file); return await asm.GetMetadataFileOrNullAsync().ConfigureAwait(false); } return null; } else { // Assembly not found; try to find a similar-enough already-loaded assembly module = await alreadyLoadedAssemblies.TryGetSimilarModuleAsync(reference).ConfigureAwait(false); if (module == null) { referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Error, "Could not find reference: " + reference.FullName); } else { referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + module.FileName); } return module; } } public MetadataFile? ResolveModule(MetadataFile mainModule, string moduleName) { return ResolveModuleAsync(mainModule, moduleName).GetAwaiter().GetResult(); } public async Task ResolveModuleAsync(MetadataFile mainModule, string moduleName) { if (providedAssemblyResolver != null) { var module = await providedAssemblyResolver.ResolveModuleAsync(mainModule, moduleName).ConfigureAwait(false); if (module != null) return module; } string? directory = Path.GetDirectoryName(mainModule.FileName); if (directory != null) { string file = Path.Combine(directory, moduleName); if (File.Exists(file)) { // Load module from disk LoadedAssembly? asm; if (loadOnDemand) { asm = assemblyList.OpenAssembly(file, isAutoLoaded: true); } else { asm = assemblyList.FindAssembly(file); } if (asm != null) { return await asm.GetMetadataFileOrNullAsync().ConfigureAwait(false); } } } // Module does not exist on disk, look for one with a matching name in the assemblylist: foreach (LoadedAssembly loaded in alreadyLoadedAssemblies.Assemblies) { var module = await loaded.GetMetadataFileOrNullAsync().ConfigureAwait(false); var reader = module?.Metadata; if (reader == null || reader.IsAssembly) continue; var moduleDef = reader.GetModuleDefinition(); if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase)) { referenceLoadInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List"); return module; } } return null; } } public IAssemblyResolver GetAssemblyResolver(bool loadOnDemand = true, bool applyWinRTProjections = false) { return new MyAssemblyResolver(this, AssemblyList.GetSnapshot(), loadOnDemand, applyWinRTProjections); } internal IAssemblyResolver GetAssemblyResolver(AssemblyListSnapshot snapshot, bool loadOnDemand = true, bool applyWinRTProjections = false) { return new MyAssemblyResolver(this, snapshot, loadOnDemand, applyWinRTProjections); } private UniversalAssemblyResolver GetUniversalResolver(bool applyWinRTProjections) { return LazyInitializer.EnsureInitialized(ref this.universalResolver, () => { var targetFramework = this.GetTargetFrameworkIdAsync().GetAwaiter().GetResult(); var runtimePack = this.GetRuntimePackAsync().GetAwaiter().GetResult(); var readerOptions = applyWinRTProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None; var rootedPath = Path.IsPathRooted(this.FileName) ? this.FileName : null; return new UniversalAssemblyResolver(rootedPath, throwOnError: false, targetFramework, runtimePack, PEStreamOptions.PrefetchEntireImage, readerOptions); })!; } public AssemblyReferenceClassifier GetAssemblyReferenceClassifier(bool applyWinRTProjections) { return GetUniversalResolver(applyWinRTProjections); } /// /// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available. /// public IDebugInfoProvider? GetDebugInfoOrNull() { if (GetMetadataFileOrNull() == null) return null; return debugInfoProvider; } UniversalAssemblyResolver? universalResolver; } } ================================================ FILE: ICSharpCode.ILSpyX/LoadedAssemblyExtensions.cs ================================================ using System; using System.IO; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX { public static class LoadedAssemblyExtensions { /// /// This method creates a Cecil object model from a PEFile. It is intended as helper method for plugins. /// Note that this method is expensive and creates high memory pressure! /// Note that accessing the Cecil objects created by this method after an assembly has been unloaded by ILSpy /// might lead to or similar. /// /// Use only as last resort if there is something missing in the official ILSpy API. /// Consider creating an issue at https://github.com/icsharpcode/ILSpy/issues/new /// and discussing the problem with us. public unsafe static Mono.Cecil.ModuleDefinition CreateCecilObjectModel(this PEFile file) { if (!file.Reader.IsEntireImageAvailable) throw new InvalidOperationException("Need full image to create Cecil object model!"); var image = file.Reader.GetEntireImage(); return Mono.Cecil.ModuleDefinition.ReadModule(new UnmanagedMemoryStream(image.Pointer, image.Length)); } public static IAssemblyResolver GetAssemblyResolver(this MetadataFile file, bool loadOnDemand = true) { return GetLoadedAssembly(file).GetAssemblyResolver(loadOnDemand); } internal static IAssemblyResolver GetAssemblyResolver(this MetadataFile file, AssemblyListSnapshot snapshot, bool loadOnDemand = true) { return GetLoadedAssembly(file).GetAssemblyResolver(snapshot, loadOnDemand); } public static IDebugInfoProvider? GetDebugInfoOrNull(this MetadataFile file) { return GetLoadedAssembly(file).GetDebugInfoOrNull(); } public static ICompilation? GetTypeSystemOrNull(this MetadataFile file) { return GetLoadedAssembly(file).GetTypeSystemOrNull(); } public static ICompilation? GetTypeSystemWithDecompilerSettingsOrNull(this MetadataFile file, DecompilerSettings settings) { return GetLoadedAssembly(file).GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(settings)); } public static LoadedAssembly GetLoadedAssembly(this MetadataFile file) { if (file == null) throw new ArgumentNullException(nameof(file)); LoadedAssembly? loadedAssembly; lock (LoadedAssembly.loadedAssemblies) { if (!LoadedAssembly.loadedAssemblies.TryGetValue(file, out loadedAssembly)) throw new ArgumentException("The specified file is not associated with a LoadedAssembly!"); } return loadedAssembly; } } } ================================================ FILE: ICSharpCode.ILSpyX/LoadedPackage.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.IO.MemoryMappedFiles; using System.Linq; using System.Reflection; using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX { /// /// NuGet package or .NET bundle: /// public class LoadedPackage { public enum PackageKind { Zip, Bundle, } /// /// Gets the LoadedAssembly instance representing this bundle. /// internal LoadedAssembly? LoadedAssembly { get; set; } public PackageKind Kind { get; } public SingleFileBundle.Header BundleHeader { get; set; } /// /// List of all entries, including those in sub-directories within the package. /// public IReadOnlyList Entries { get; } public PackageFolder RootFolder { get; } public LoadedPackage(PackageKind kind, IEnumerable entries) { this.Kind = kind; this.Entries = entries.ToArray(); var topLevelEntries = new List(); var folders = new Dictionary(); var rootFolder = new PackageFolder(this, null, ""); folders.Add("", rootFolder); foreach (var entry in this.Entries) { var (dirname, filename) = SplitName(entry.Name); if (!string.IsNullOrEmpty(filename)) { GetFolder(dirname).Entries.Add(new FolderEntry(filename, entry)); } } this.RootFolder = rootFolder; static (string, string) SplitName(string filename) { int pos = filename.LastIndexOfAny(new char[] { '/', '\\' }); if (pos == -1) return ("", filename); // file in root else return (filename.Substring(0, pos), filename.Substring(pos + 1)); } PackageFolder GetFolder(string name) { if (folders.TryGetValue(name, out var result)) return result; var (dirname, basename) = SplitName(name); PackageFolder parent = GetFolder(dirname); result = new PackageFolder(this, parent, basename); parent.Folders.Add(result); folders.Add(name, result); return result; } } public static LoadedPackage FromZipFile(string file) { Debug.WriteLine($"LoadedPackage.FromZipFile({file})"); using var archive = ZipFile.OpenRead(file); return new LoadedPackage(PackageKind.Zip, archive.Entries.Select(entry => new ZipFileEntry(file, entry))); } /// /// Load a .NET single-file bundle. /// public static LoadedPackage? FromBundle(string fileName) { using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); try { if (!SingleFileBundle.IsBundle(view, out long bundleHeaderOffset)) return null; var manifest = SingleFileBundle.ReadManifest(view, bundleHeaderOffset); var entries = manifest.Entries.Select(e => new BundleEntry(fileName, view, e)).ToList(); var result = new LoadedPackage(PackageKind.Bundle, entries); result.BundleHeader = manifest; view = null; // don't dispose the view, we're still using it in the bundle entries return result; } catch (InvalidDataException) { return null; } finally { view?.Dispose(); } } /// /// Entry inside a package folder. Effectively renames the entry. /// sealed class FolderEntry : PackageEntry { readonly PackageEntry originalEntry; public override string Name { get; } public override string FullName => originalEntry.Name; public FolderEntry(string name, PackageEntry originalEntry) { this.Name = name; this.originalEntry = originalEntry; } public override ManifestResourceAttributes Attributes => originalEntry.Attributes; public override string PackageQualifiedFileName => originalEntry.PackageQualifiedFileName; public override ResourceType ResourceType => originalEntry.ResourceType; public override Stream? TryOpenStream() => originalEntry.TryOpenStream(); public override long? TryGetLength() => originalEntry.TryGetLength(); } sealed class ZipFileEntry : PackageEntry { readonly string zipFile; public override string Name { get; } public override string PackageQualifiedFileName => $"zip://{zipFile};{Name}"; public override string FullName => Name; public ZipFileEntry(string zipFile, ZipArchiveEntry entry) { this.zipFile = zipFile; this.Name = entry.FullName; } public override Stream? TryOpenStream() { Debug.WriteLine("Decompress " + Name); using var archive = ZipFile.OpenRead(zipFile); var entry = archive.GetEntry(Name); if (entry == null) return null; var memoryStream = new MemoryStream(); using (var s = entry.Open()) { s.CopyTo(memoryStream); } memoryStream.Position = 0; return memoryStream; } public override long? TryGetLength() { Debug.WriteLine("TryGetLength " + Name); using var archive = ZipFile.OpenRead(zipFile); var entry = archive.GetEntry(Name); if (entry == null) return null; return entry.Length; } } sealed class BundleEntry : PackageEntry { readonly string bundleFile; readonly MemoryMappedViewAccessor view; readonly SingleFileBundle.Entry entry; public BundleEntry(string bundleFile, MemoryMappedViewAccessor view, SingleFileBundle.Entry entry) { this.bundleFile = bundleFile; this.view = view; this.entry = entry; } public override string Name => entry.RelativePath; public override string FullName => Name; public override string PackageQualifiedFileName => $"bundle://{bundleFile};{Name}"; public override Stream TryOpenStream() { Debug.WriteLine("Open bundle member " + Name); if (entry.CompressedSize == 0) { return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, entry.Offset, entry.Size); } else { Stream compressedStream = new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, entry.Offset, entry.CompressedSize); using var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress); Stream decompressedStream = new MemoryStream((int)entry.Size); deflateStream.CopyTo(decompressedStream); if (decompressedStream.Length != entry.Size) { throw new InvalidDataException($"Corrupted single-file entry '{entry.RelativePath}'. Declared decompressed size '{entry.Size}' is not the same as actual decompressed size '{decompressedStream.Length}'."); } decompressedStream.Seek(0, SeekOrigin.Begin); return decompressedStream; } } public override long? TryGetLength() { return entry.Size; } } } public abstract class PackageEntry : Resource { /// /// Gets the file name of the entry (may include path components, relative to the package root). /// public abstract override string Name { get; } /// /// Gets the full file name including the full file name of the package (prefixed with e.g., bundle:// or zip://). /// public abstract string PackageQualifiedFileName { get; } /// /// Gets the full name of the file name relative to the package root. /// public abstract string FullName { get; } } public sealed class PackageFolder : IAssemblyResolver { /// /// Gets the short name of the folder. /// public string Name { get; } readonly LoadedPackage package; readonly PackageFolder? parent; internal PackageFolder(LoadedPackage package, PackageFolder? parent, string name) { this.package = package; this.parent = parent; this.Name = name; } public PackageFolder? Parent => parent; public List Folders { get; } = new List(); public List Entries { get; } = new List(); public MetadataFile? Resolve(IAssemblyReference reference) { var asm = ResolveFileName(reference.Name + ".dll"); if (asm != null) { return asm.GetMetadataFileOrNull(); } return parent?.Resolve(reference); } public Task ResolveAsync(IAssemblyReference reference) { var asm = ResolveFileName(reference.Name + ".dll"); if (asm != null) { return asm.GetMetadataFileOrNullAsync(); } if (parent != null) { return parent.ResolveAsync(reference); } return Task.FromResult(null); } public MetadataFile? ResolveModule(MetadataFile mainModule, string moduleName) { var asm = ResolveFileName(moduleName + ".dll"); if (asm != null) { return asm.GetMetadataFileOrNull(); } return parent?.ResolveModule(mainModule, moduleName); } public Task ResolveModuleAsync(MetadataFile mainModule, string moduleName) { var asm = ResolveFileName(moduleName + ".dll"); if (asm != null) { return asm.GetMetadataFileOrNullAsync(); } if (parent != null) { return parent.ResolveModuleAsync(mainModule, moduleName); } return Task.FromResult(null); } readonly Dictionary assemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); public LoadedAssembly? ResolveFileName(string name) { if (package.LoadedAssembly == null) return null; lock (assemblies) { if (assemblies.TryGetValue(name, out var asm)) return asm; var entry = Entries.FirstOrDefault(e => string.Equals(name, e.Name, StringComparison.OrdinalIgnoreCase)); if (entry != null) { asm = new LoadedAssembly( package.LoadedAssembly, entry.Name, fileLoaders: package.LoadedAssembly.AssemblyList.LoaderRegistry, assemblyResolver: this, stream: Task.Run(entry.TryOpenStream), applyWinRTProjections: package.LoadedAssembly.AssemblyList.ApplyWinRTProjections, useDebugSymbols: package.LoadedAssembly.AssemblyList.UseDebugSymbols ); } else { asm = null; } assemblies.Add(name, asm); return asm; } } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/ClassDiagrammer.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { /// Contains type info and metadata for generating a HTML class diagrammer from a source assembly. /// Serialized into JSON by . public sealed class ClassDiagrammer { internal const string NewLine = "\n"; internal string SourceAssemblyName { get; set; } = null!; internal string SourceAssemblyVersion { get; set; } = null!; /// Types selectable in the diagrammer, grouped by their /// to facilitate a structured type selection. internal Dictionary TypesByNamespace { get; set; } = null!; /// Types not included in the , /// but referenced by s that are. /// Contains display names (values; similar to ) /// by their referenced IDs (keys; similar to ). internal Dictionary OutsideReferences { get; set; } = null!; /// Types excluded from the ; /// used to support . internal string[] Excluded { get; set; } = null!; /// A -like structure with collections /// of property relations to one or many other s. public abstract class Relationships { /// Relations to zero or one other instances of s included in the , /// with the display member names as keys and the related as values. /// This is because member names must be unique within the owning , /// while the related may be the same for multiple properties. public Dictionary? HasOne { get; set; } /// Relations to zero to infinite other instances of s included in the , /// with the display member names as keys and the related as values. /// This is because member names must be unique within the owning , /// while the related may be the same for multiple properties. public Dictionary? HasMany { get; set; } } /// The mermaid class diagram definition, inheritance and relationships metadata /// and XML documentation for a from the source assembly. public sealed class Type : Relationships { /// Uniquely identifies the in the scope of the source assembly /// as well as any HTML diagrammer generated from it. /// Should match \w+ to be safe to use as select option value and /// part of the DOM id of the SVG node rendered for this type. /// May be the type name itself. internal string Id { get; set; } = null!; /// The human-readable label for the type, if different from . /// Not guaranteed to be unique in the scope of the . public string? Name { get; set; } /// Contains the definition of the type and its own (not inherited) flat members /// in mermaid class diagram syntax, see https://mermaid.js.org/syntax/classDiagram.html . public string Body { get; set; } = null!; /// The base type directly implemented by this type, with the as key /// and the (optional) differing display name as value of the single entry /// - or null if the base type is . /// Yes, Christopher Lambert, there can only be one. For now. /// But using the same interface as for is convenient /// and who knows - at some point the .Net bus may roll up with multi-inheritance. /// Then this'll look visionary! public Dictionary? BaseType { get; set; } /// Interfaces directly implemented by this type, with their as keys /// and their (optional) differing display names as values. public Dictionary? Interfaces { get; set; } /// Contains inherited members by the of their /// for the consumer to choose which of them to display in an inheritance scenario. public IDictionary? Inherited { get; set; } /// Contains the XML documentation comments for this type /// (using a key) and its members, if available. public IDictionary? XmlDocs { get; set; } /// Members inherited from an ancestor type specified by the Key of . public class InheritedMembers : Relationships { /// The simple, non-complex members inherited from another /// in mermaid class diagram syntax. public string? FlatMembers { get; set; } } } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/ClassDiagrammerFactory.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { using CD = ClassDiagrammer; /* See class diagram syntax * reference (may be outdated!) https://mermaid.js.org/syntax/classDiagram.html * lexical definition https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/diagrams/class/parser/classDiagram.jison */ /// Produces mermaid class diagram syntax for a filtered list of types from a specified .Net assembly. public partial class ClassDiagrammerFactory { private readonly XmlDocumentationFormatter? xmlDocs; private readonly DecompilerSettings decompilerSettings; private ITypeDefinition[]? selectedTypes; private Dictionary? uniqueIds; private Dictionary? labels; private Dictionary? outsideReferences; public ClassDiagrammerFactory(XmlDocumentationFormatter? xmlDocs) { this.xmlDocs = xmlDocs; //TODO not sure LanguageVersion.Latest is the wisest choice here; maybe cap this for better mermaid compatibility? decompilerSettings = new DecompilerSettings(Decompiler.CSharp.LanguageVersion.Latest) { AutomaticProperties = true // for IsHidden to return true for backing fields }; } public CD BuildModel(string assemblyPath, string? include, string? exclude) { CSharpDecompiler decompiler = new(assemblyPath, decompilerSettings); MetadataModule mainModule = decompiler.TypeSystem.MainModule; IEnumerable allTypes = mainModule.TypeDefinitions; selectedTypes = FilterTypes(allTypes, include == null ? null : new Regex(include, RegexOptions.Compiled), exclude == null ? null : new Regex(exclude, RegexOptions.Compiled)).ToArray(); // generate dictionary to read names from later uniqueIds = GenerateUniqueIds(selectedTypes); labels = []; outsideReferences = []; Dictionary typesByNamespace = selectedTypes.GroupBy(t => t.Namespace).OrderBy(g => g.Key).ToDictionary(g => g.Key, ns => ns.OrderBy(t => t.FullName).Select(type => type.Kind == TypeKind.Enum ? BuildEnum(type) : BuildType(type)).ToArray()); string[] excluded = allTypes.Except(selectedTypes).Select(t => t.ReflectionName).ToArray(); return new CD { SourceAssemblyName = mainModule.AssemblyName, SourceAssemblyVersion = mainModule.AssemblyVersion.ToString(), TypesByNamespace = typesByNamespace, OutsideReferences = outsideReferences, Excluded = excluded }; } /// The default strategy for pre-filtering the available in the HTML diagrammer. /// Applies as well as /// matching by and not by . /// The types to effectively include in the HTML diagrammer. protected virtual IEnumerable FilterTypes(IEnumerable typeDefinitions, Regex? include, Regex? exclude) => typeDefinitions.Where(type => IsIncludedByDefault(type) && (include?.IsMatch(type.ReflectionName) != false) // applying optional whitelist filter && (exclude?.IsMatch(type.ReflectionName) != true)); // applying optional blacklist filter /// The strategy for deciding whether a should be included /// in the HTML diagrammer by default. Excludes compiler-generated and their nested types. protected virtual bool IsIncludedByDefault(ITypeDefinition type) => !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(); } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/EmbeddedResource.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.IO; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { public partial class GenerateHtmlDiagrammer { /// A helper for loading resources embedded in the nested html folder. private static class EmbeddedResource { internal static string ReadText(string resourceName) { Stream stream = GetStream(resourceName); using StreamReader reader = new(stream); return reader.ReadToEnd(); } internal static void CopyTo(string outputFolder, string resourceName) { Stream resourceStream = GetStream(resourceName); using FileStream output = new(Path.Combine(outputFolder, resourceName), FileMode.Create, FileAccess.Write); resourceStream.CopyTo(output); } private static Stream GetStream(string resourceName) { var type = typeof(EmbeddedResource); var assembly = type.Assembly; var fullResourceName = $"{type.Namespace}.html.{resourceName}"; Stream? stream = assembly.GetManifestResourceStream(fullResourceName); if (stream == null) throw new FileNotFoundException("Resource not found.", fullResourceName); return stream; } } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Extensions/StringExtensions.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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; using System.Text.RegularExpressions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions { internal static class StringExtensions { /// Replaces all consecutive horizontal white space characters in /// with while leaving line breaks intact. internal static string NormalizeHorizontalWhiteSpace(this string input, string normalizeTo = " ") => Regex.Replace(input, @"[ \t]+", normalizeTo); /// Replaces all occurrences of in /// with . internal static string ReplaceAll(this string input, IEnumerable oldValues, string? newValue) => oldValues.Aggregate(input, (aggregate, oldValue) => aggregate.Replace(oldValue, newValue)); /// Joins the specified to a single one /// using the specified as a delimiter. /// Whether to pad the start and end of the string with the as well. internal static string Join(this IEnumerable? strings, string separator, bool pad = false) { if (strings == null) return string.Empty; var joined = string.Join(separator, strings); return pad ? string.Concat(separator, joined, separator) : joined; } /// Formats all items in using the supplied strategy /// and returns a string collection - even if the incoming is null. internal static IEnumerable FormatAll(this IEnumerable? collection, Func format) => collection?.Select(format) ?? []; } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Extensions/TypeExtensions.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.CodeAnalysis; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions { internal static class TypeExtensions { internal static bool IsObject(this IType t) => t.IsKnownType(KnownTypeCode.Object); internal static bool IsInterface(this IType t) => t.Kind == TypeKind.Interface; internal static bool TryGetNullableType(this IType type, [MaybeNullWhen(false)] out IType typeArg) { bool isNullable = type.IsKnownType(KnownTypeCode.NullableOfT); typeArg = isNullable ? type.TypeArguments.Single() : null; return isNullable; } } internal static class MemberInfoExtensions { /// Groups the into a dictionary /// with keys. internal static Dictionary GroupByDeclaringType(this IEnumerable members) where T : IMember => members.GroupByDeclaringType(m => m); /// Groups the into a dictionary /// with keys using . internal static Dictionary GroupByDeclaringType(this IEnumerable objectsWithMembers, Func getMember) => objectsWithMembers.GroupBy(m => getMember(m).DeclaringType).ToDictionary(g => g.Key, g => g.ToArray()); } internal static class DictionaryExtensions { /// Returns the s value for the specified /// if available and otherwise the default for . internal static Tout? GetValue(this IDictionary dictionary, T key) => dictionary.TryGetValue(key, out Tout? value) ? value : default; } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.BuildTypes.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { using CD = ClassDiagrammer; partial class ClassDiagrammerFactory { private CD.Type BuildEnum(ITypeDefinition type) { IField[] fields = type.GetFields(f => f.IsConst && f.IsStatic && f.Accessibility == Accessibility.Public).ToArray(); Dictionary? docs = xmlDocs?.GetXmlDocs(type, fields); string name = GetName(type), typeId = GetId(type); var body = fields.Select(f => f.Name).Prepend("<>") .Join(CD.NewLine + " ", pad: true).TrimEnd(' '); return new CD.Type { Id = typeId, Name = name == typeId ? null : name, Body = $"class {typeId} {{{body}}}", XmlDocs = docs }; } private CD.Type BuildType(ITypeDefinition type) { string typeId = GetId(type); IMethod[] methods = GetMethods(type).ToArray(); IProperty[] properties = type.GetProperties().ToArray(); IProperty[] hasOneRelations = GetHasOneRelations(properties); (IProperty property, IType elementType)[] hasManyRelations = GetManyRelations(properties); var propertyNames = properties.Select(p => p.Name).ToArray(); IField[] fields = GetFields(type, properties); #region split members up by declaring type // enables the diagrammer to exclude inherited members from derived types if they are already rendered in a base type Dictionary flatPropertiesByType = properties.Except(hasOneRelations) .Except(hasManyRelations.Select(r => r.property)).GroupByDeclaringType(); Dictionary hasOneRelationsByType = hasOneRelations.GroupByDeclaringType(); Dictionary hasManyRelationsByType = hasManyRelations.GroupByDeclaringType(r => r.property); Dictionary fieldsByType = fields.GroupByDeclaringType(); Dictionary methodsByType = methods.GroupByDeclaringType(); #endregion #region build diagram definitions for the type itself and members declared by it string members = flatPropertiesByType.GetValue(type).FormatAll(FormatFlatProperty) .Concat(methodsByType.GetValue(type).FormatAll(FormatMethod)) .Concat(fieldsByType.GetValue(type).FormatAll(FormatField)) .Join(CD.NewLine + " ", pad: true); // see https://mermaid.js.org/syntax/classDiagram.html#annotations-on-classes string? annotation = type.IsInterface() ? "Interface" : type.IsAbstract ? type.IsSealed ? "Service" : "Abstract" : null; string body = annotation == null ? members.TrimEnd(' ') : members + $"<<{annotation}>>" + CD.NewLine; #endregion Dictionary? docs = xmlDocs?.GetXmlDocs(type, fields, properties, methods); #region build diagram definitions for inherited members by declaring type string explicitTypePrefix = typeId + " : "; // get ancestor types this one is inheriting members from Dictionary inheritedMembersByType = type.GetNonInterfaceBaseTypes().Where(t => t != type && !t.IsObject()) // and group inherited members by declaring type .ToDictionary(GetId, t => { IEnumerable flatMembers = flatPropertiesByType.GetValue(t).FormatAll(p => explicitTypePrefix + FormatFlatProperty(p)) .Concat(methodsByType.GetValue(t).FormatAll(m => explicitTypePrefix + FormatMethod(m))) .Concat(fieldsByType.GetValue(t).FormatAll(f => explicitTypePrefix + FormatField(f))); return new CD.Type.InheritedMembers { FlatMembers = flatMembers.Any() ? flatMembers.Join(CD.NewLine) : null, HasOne = MapHasOneRelations(hasOneRelationsByType, t), HasMany = MapHasManyRelations(hasManyRelationsByType, t) }; }); #endregion string typeName = GetName(type); return new CD.Type { Id = typeId, Name = typeName == typeId ? null : typeName, Body = $"class {typeId} {{{body}}}", HasOne = MapHasOneRelations(hasOneRelationsByType, type), HasMany = MapHasManyRelations(hasManyRelationsByType, type), BaseType = GetBaseType(type), Interfaces = GetInterfaces(type), Inherited = inheritedMembersByType, XmlDocs = docs }; } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.FlatMembers.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { partial class ClassDiagrammerFactory { /// Wraps a method configurable via /// that can be used to determine whether a member should be hidden. private bool IsHidden(IEntity entity) => CSharpDecompiler.MemberIsHidden(entity.ParentModule!.MetadataFile, entity.MetadataToken, decompilerSettings); private IField[] GetFields(ITypeDefinition type, IProperty[] properties) // only display fields that are not backing properties of the same name and type => type.GetFields(f => !IsHidden(f) // removes compiler-generated backing fields /* tries to remove remaining manual backing fields by matching type and name */ && !properties.Any(p => f.ReturnType.Equals(p.ReturnType) && Regex.IsMatch(f.Name, "_?" + p.Name, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.NonBacktracking))).ToArray(); private static IEnumerable GetMethods(ITypeDefinition type) { return type.GetMethods(IsDeclaredMethod); bool IsDeclaredMethod(IMethod m) { if (m.IsOperator || m.IsCompilerGenerated()) return false; if (m.DeclaringTypeDefinition == type) { // include methods if self-declared return true; } else { // but exclude methods declared by object and their overrides, if inherited if (m.DeclaringType.IsObject()) return false; IMember? baseMember; if (m.IsOverride && (baseMember = InheritanceHelper.GetBaseMember(m)) != null) { if (baseMember.DeclaringType.IsObject()) return false; } } return true; } } private string FormatMethod(IMethod method) { string parameters = method.Parameters.Select(p => $"{GetName(p.Type)} {p.Name}").Join(", "); string? modifier = method.IsAbstract ? "*" : method.IsStatic ? "$" : default; string name = method.Name; if (method.IsExplicitInterfaceImplementation) { IMember member = method.ExplicitlyImplementedInterfaceMembers.Single(); name = GetName(member.DeclaringType) + '.' + member.Name; } string? typeArguments = method.TypeArguments.Count == 0 ? null : $"❰{method.TypeArguments.Select(GetName).Join(", ")}❱"; return $"{GetAccessibility(method.Accessibility)}{name}{typeArguments}({parameters}){modifier} {GetName(method.ReturnType)}"; } private string FormatFlatProperty(IProperty property) { char? visibility = GetAccessibility(property.Accessibility); string? modifier = property.IsAbstract ? "*" : property.IsStatic ? "$" : default; return $"{visibility}{GetName(property.ReturnType)} {property.Name}{modifier}"; } private string FormatField(IField field) { string? modifier = field.IsAbstract ? "*" : field.IsStatic ? "$" : default; return $"{GetAccessibility(field.Accessibility)}{GetName(field.ReturnType)} {field.Name}{modifier}"; } // see https://stackoverflow.com/a/16024302 for accessibility modifier flags private static char? GetAccessibility(Accessibility access) => access switch { Accessibility.Private => '-', Accessibility.ProtectedAndInternal or Accessibility.Internal => '~', Accessibility.Protected or Accessibility.ProtectedOrInternal => '#', Accessibility.Public => '+', _ => default, }; } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.Relationships.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { using CD = ClassDiagrammer; partial class ClassDiagrammerFactory { private IProperty[] GetHasOneRelations(IProperty[] properties) => properties.Where(property => { IType type = property.ReturnType; if (type.TryGetNullableType(out var typeArg)) type = typeArg; return selectedTypes!.Contains(type); }).ToArray(); private (IProperty property, IType elementType)[] GetManyRelations(IProperty[] properties) => properties.Select(property => { IType elementType = property.ReturnType.GetElementTypeFromIEnumerable(property.Compilation, true, out bool? isGeneric); if (isGeneric == false && elementType.IsObject()) { IProperty[] indexers = property.ReturnType.GetProperties( p => p.IsIndexer && !p.ReturnType.IsObject(), GetMemberOptions.IgnoreInheritedMembers).ToArray(); // TODO mayb order by declaring type instead of filtering if (indexers.Length > 0) elementType = indexers[0].ReturnType; } return isGeneric == true && selectedTypes!.Contains(elementType) ? (property, elementType) : default; }).Where(pair => pair != default).ToArray(); /// Returns the relevant direct super type the inherits from /// in a format matching . private Dictionary? GetBaseType(IType type) { IType? relevantBaseType = type.DirectBaseTypes.SingleOrDefault(t => !t.IsInterface() && !t.IsObject()); return relevantBaseType == null ? default : new[] { BuildRelationship(relevantBaseType) }.ToDictionary(r => r.to, r => r.label); } /// Returns the direct interfaces implemented by /// in a format matching . private Dictionary? GetInterfaces(ITypeDefinition type) { var interfaces = type.DirectBaseTypes.Where(t => t.IsInterface()).ToArray(); return interfaces.Length == 0 ? null : interfaces.Select(i => BuildRelationship(i)).GroupBy(r => r.to) .ToDictionary(g => g.Key, g => g.Select(r => r.label).ToArray()); } /// Returns the one-to-one relations from to other s /// in a format matching . private Dictionary? MapHasOneRelations(Dictionary hasOneRelationsByType, IType type) => hasOneRelationsByType.GetValue(type)?.Select(p => { IType type = p.ReturnType; string label = p.Name; if (p.IsIndexer) label += $"[{p.Parameters.Single().Type.Name} {p.Parameters.Single().Name}]"; if (type.TryGetNullableType(out var typeArg)) { type = typeArg; label += " ?"; } return BuildRelationship(type, label); }).ToDictionary(r => r.label!, r => r.to); /// Returns the one-to-many relations from to other s /// in a format matching . private Dictionary? MapHasManyRelations(Dictionary hasManyRelationsByType, IType type) => hasManyRelationsByType.GetValue(type)?.Select(relation => { (IProperty property, IType elementType) = relation; return BuildRelationship(elementType, property.Name); }).ToDictionary(r => r.label!, r => r.to); /// Builds references to super types and (one/many) relations, /// recording outside references on the way and applying labels if required. /// The type to reference. /// Used only for property one/many relations. private (string to, string? label) BuildRelationship(IType type, string? propertyName = null) { (string id, IType? openGeneric) = GetIdAndOpenGeneric(type); AddOutsideReference(id, openGeneric ?? type); // label the relation with the property name if provided or the closed generic type for super types string? label = propertyName ?? (openGeneric == null ? null : GetName(type)); return (to: id, label); } private void AddOutsideReference(string typeId, IType type) { if (!selectedTypes!.Contains(type) && outsideReferences?.ContainsKey(typeId) == false) outsideReferences.Add(typeId, type.Namespace + '.' + GetName(type)); } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.TypeIds.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { using CD = ClassDiagrammer; public partial class ClassDiagrammerFactory { /// Generates a dictionary of unique and short, but human readable identifiers for /// to be able to safely reference them in any combination. private static Dictionary GenerateUniqueIds(IEnumerable types) { Dictionary uniqueIds = []; var groups = types.GroupBy(t => t.Name); // simplified handling for the majority of unique types foreach (var group in groups.Where(g => g.Count() == 1)) uniqueIds[group.First()] = SanitizeTypeName(group.Key); // number non-unique types foreach (var group in groups.Where(g => g.Count() > 1)) { var counter = 0; foreach (var type in group) uniqueIds[type] = type.Name + ++counter; } return uniqueIds; } private string GetId(IType type) => GetIdAndOpenGeneric(type).id; /// For a non- or open generic , returns a unique identifier and null. /// For a closed generic , returns the open generic type and the unique identifier of it. /// That helps connecting closed generic references (e.g. Store<int>) to their corresponding /// open generic (e.g. Store<T>) like in . private (string id, IType? openGeneric) GetIdAndOpenGeneric(IType type) { // get open generic type if type is a closed generic (i.e. has type args none of which are parameters) var openGeneric = type is ParameterizedType generic && !generic.TypeArguments.Any(a => a is ITypeParameter) ? generic.GenericType : null; type = openGeneric ?? type; // reference open instead of closed generic type if (uniqueIds!.TryGetValue(type, out var uniqueId)) return (uniqueId, openGeneric); // types included by FilterTypes // types excluded by FilterTypes string? typeParams = type.TypeParameterCount == 0 ? null : ("_" + type.TypeParameters.Select(GetId).Join("_")); var id = SanitizeTypeName(type.FullName.Replace('.', '_')) + typeParams; // to achieve uniqueness for types with same FullName (i.e. generic overloads) uniqueIds![type] = id; // update dictionary to avoid re-generation return (id, openGeneric); } private static string SanitizeTypeName(string typeName) => typeName.Replace('<', '_').Replace('>', '_'); // for module of executable } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.TypeNames.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { public partial class ClassDiagrammerFactory { /// Returns a cached display name for . private string GetName(IType type) { if (labels!.TryGetValue(type, out string? value)) return value; // return cached value return labels[type] = GenerateName(type); // generate and cache new value } /// Generates a display name for . private string GenerateName(IType type) { // non-generic types if (type.TypeParameterCount < 1) { if (type is ArrayType array) return GetName(array.ElementType) + "[]"; if (type is ByReferenceType byReference) return "&" + GetName(byReference.ElementType); ITypeDefinition? typeDefinition = type.GetDefinition(); if (typeDefinition == null) return type.Name; if (typeDefinition.KnownTypeCode == KnownTypeCode.None) { if (type.DeclaringType == null) return type.Name.Replace('<', '❰').Replace('>', '❱'); // for module of executable else return type.DeclaringType.Name + '+' + type.Name; // nested types } return KnownTypeReference.GetCSharpNameByTypeCode(typeDefinition.KnownTypeCode) ?? type.Name; } // nullable types if (type.TryGetNullableType(out var nullableType)) return GetName(nullableType) + "?"; // other generic types string typeArguments = type.TypeArguments.Select(GetName).Join(", "); return type.Name + $"❰{typeArguments}❱"; } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/GenerateHtmlDiagrammer.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { /// The command for creating an HTML5 diagramming app with an API optimized for binding command line parameters. /// To use it outside of that context, set its properties and call . public partial class GenerateHtmlDiagrammer { internal const string RepoUrl = "https://github.com/icsharpcode/ILSpy"; public required string Assembly { get; set; } public string? OutputFolder { get; set; } public string? Include { get; set; } public string? Exclude { get; set; } public bool JsonOnly { get; set; } public bool ReportExcludedTypes { get; set; } public string? XmlDocs { get; set; } /// Namespaces to strip from . /// Implemented as a list of exact replacements instead of a single, more powerful RegEx because replacement in /// /// happens on the unstructured string where matching and replacing the namespaces of referenced types, members and method parameters /// using RegExes would add a lot of complicated RegEx-heavy code for a rather unimportant feature. public IEnumerable? StrippedNamespaces { get; set; } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/Generator.Run.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Diagnostics; using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { partial class GenerateHtmlDiagrammer { public void Run() { var assemblyPath = Assembly; XmlDocumentationFormatter? xmlDocs = CreateXmlDocsFormatter(assemblyPath); ClassDiagrammer model = BuildModel(assemblyPath, xmlDocs); GenerateOutput(assemblyPath, model); } protected virtual XmlDocumentationFormatter? CreateXmlDocsFormatter(string assemblyPath) { var xmlDocsPath = XmlDocs == null ? Path.ChangeExtension(assemblyPath, ".xml") : XmlDocs; XmlDocumentationFormatter? xmlDocs = null; if (File.Exists(xmlDocsPath)) xmlDocs = new XmlDocumentationFormatter(new XmlDocumentationProvider(xmlDocsPath), StrippedNamespaces?.ToArray()); else Debug.WriteLine("No XML documentation file found. Continuing without."); return xmlDocs; } protected virtual ClassDiagrammer BuildModel(string assemblyPath, XmlDocumentationFormatter? xmlDocs) => new ClassDiagrammerFactory(xmlDocs).BuildModel(assemblyPath, Include, Exclude); private string SerializeModel(ClassDiagrammer diagrammer) { object jsonModel = new { diagrammer.OutsideReferences, /* convert collections to dictionaries for easier access in ES using * for (let [key, value] of Object.entries(dictionary)) */ TypesByNamespace = diagrammer.TypesByNamespace.ToDictionary(ns => ns.Key, ns => ns.Value.ToDictionary(t => t.Id, t => t)) }; // wrap model including the data required for doing the template replacement in a JS build task if (JsonOnly) { jsonModel = new { diagrammer.SourceAssemblyName, diagrammer.SourceAssemblyVersion, BuilderVersion = DecompilerVersionInfo.FullVersionWithCommitHash, RepoUrl, // pre-serialize to a string so that we don't have to re-serialize it in the JS build task Model = Serialize(jsonModel) }; } return Serialize(jsonModel); } private static JsonSerializerOptions serializerOptions = new() { WriteIndented = true, // avoid outputting null properties unnecessarily DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private static string Serialize(object json) => JsonSerializer.Serialize(json, serializerOptions); private void GenerateOutput(string assemblyPath, ClassDiagrammer model) { string modelJson = SerializeModel(model); // If no out folder is specified, default to a " diagrammer" folder next to the input assembly. var outputFolder = OutputFolder ?? Path.Combine( Path.GetDirectoryName(assemblyPath) ?? string.Empty, Path.GetFileNameWithoutExtension(assemblyPath) + " diagrammer"); if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); if (JsonOnly) { File.WriteAllText(Path.Combine(outputFolder, "model.json"), modelJson); Debug.WriteLine("Successfully generated model.json for HTML diagrammer."); } else { var htmlTemplate = EmbeddedResource.ReadText("template.html"); var html = htmlTemplate .Replace("{{SourceAssemblyName}}", model.SourceAssemblyName) .Replace("{{SourceAssemblyVersion}}", model.SourceAssemblyVersion) .Replace("{{BuilderVersion}}", DecompilerVersionInfo.FullVersionWithCommitHash) .Replace("{{RepoUrl}}", RepoUrl) .Replace("{{Model}}", modelJson); File.WriteAllText(Path.Combine(outputFolder, "index.html"), html); // copy required resources to output folder while flattening paths if required foreach (var resource in new[] { "styles.css", "ILSpy.ico", "script.js" }) EmbeddedResource.CopyTo(outputFolder, resource); Debug.WriteLine("Successfully generated HTML diagrammer."); } if (ReportExcludedTypes) { string excludedTypes = model.Excluded.Join(Environment.NewLine); File.WriteAllText(Path.Combine(outputFolder, "excluded types.txt"), excludedTypes); } } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/ReadMe.md ================================================ # How does it work? To **extract the type info from the source assembly**, ILSpy side-loads it including all its dependencies. The extracted type info is **structured into a model optimized for the HTML diagrammer** and serialized to JSON. The model is a mix between drop-in type definitions in mermaid class diagram syntax and destructured metadata about relations, inheritance and documentation comments. > The JSON type info is injected into the `template.html` alongside other resources like the `script.js` at corresponding `{{placeholders}}`. It comes baked into the HTML diagrammer to enable > - accessing the data and > - importing the mermaid module from a CDN > > locally without running a web server [while also avoiding CORS restrictions.](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#file_origins) In the final step, the **HTML diagrammer app re-assembles the type info** based on the in-app type selection and rendering options **to generate [mermaid class diagrams](https://mermaid.js.org/syntax/classDiagram.html)** with the types, their relations and as much inheritance detail as you need. ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/XmlDocumentationFormatter.cs ================================================ // Copyright (c) 2024 Holger Schmidt // // 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.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.MermaidDiagrammer.Extensions; namespace ICSharpCode.ILSpyX.MermaidDiagrammer { /// Wraps the to prettify XML documentation comments. /// Make sure to enable XML documentation output, see /// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output . public class XmlDocumentationFormatter { /// Matches XML indent. protected const string linePadding = @"^[ \t]+|[ \t]+$"; /// Matches reference tags including "see href", "see cref" and "paramref name" /// with the cref value being prefixed by symbol-specific letter and a colon /// including the quotes around the attribute value and the closing slash of the tag containing the attribute. protected const string referenceAttributes = @"(see\s.ref=""(.:)?)|(paramref\sname="")|(""\s/)"; private readonly IDocumentationProvider docs; private readonly Regex noiseAndPadding; public XmlDocumentationFormatter(IDocumentationProvider docs, string[]? strippedNamespaces) { this.docs = docs; List regexes = new() { linePadding, referenceAttributes }; if (strippedNamespaces?.Length > 0) regexes.AddRange(strippedNamespaces.Select(ns => $"({ns.Replace(".", "\\.")}\\.)")); noiseAndPadding = new Regex(regexes.Join("|"), RegexOptions.Multiline); // builds an OR | combined regex } internal Dictionary? GetXmlDocs(ITypeDefinition type, params IMember[][] memberCollections) { Dictionary? docs = new(); AddXmlDocEntry(docs, type); foreach (IMember[] members in memberCollections) { foreach (IMember member in members) AddXmlDocEntry(docs, member); } return docs?.Keys.Count != 0 ? docs : default; } protected virtual string? GetDoco(IEntity entity) { string? comment = docs.GetDocumentation(entity)? .ReplaceAll(["", ""], null) .ReplaceAll(["", ""], ClassDiagrammer.NewLine).Trim() // to format .Replace('<', '[').Replace('>', ']'); // to prevent ugly escaped output return comment == null ? null : noiseAndPadding.Replace(comment, string.Empty).NormalizeHorizontalWhiteSpace(); } private void AddXmlDocEntry(Dictionary docs, IEntity entity) { string? doc = GetDoco(entity); if (string.IsNullOrEmpty(doc)) return; string key = entity is IMember member ? member.Name : string.Empty; docs[key] = doc; } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/.eslintrc.js ================================================ module.exports = { 'env': { 'commonjs': true, 'es6': true, 'browser': true }, 'extends': 'eslint:recommended', 'parserOptions': { 'sourceType': 'module', 'ecmaVersion': 'latest' }, 'rules': { 'indent': ['error', 4, { 'SwitchCase': 1 }], 'semi': ['error', 'always'] } }; ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/.gitignore ================================================ /node_modules /class-diagrammer.html /model.json /package-lock.json ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/README.txt ================================================ To edit the HTML/JS/CSS for the HTML diagrammer, open this folder in Visual Studio Code. In that environment you'll find tasks (see https://code.visualstudio.com/Docs/editor/tasks to run and configure) that you can run to 1. Generate a model.json using the current Debug build of ilspycmd. This is required to build a diagrammer for testing in development using task 3. 2. Transpile the .less into .css that is tracked by source control and embedded into ILSpyX. 3. Generate a diagrammer for testing in development from template.html and the model.json generated by task 1. 4. Auto-rebuild the development diagrammer by running either task 2 or 3 when the corresponding source files change. ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/gulpfile.js ================================================ const gulp = require('gulp'); const less = require('gulp-less'); const fs = require('fs'); function transpileLess (done) { gulp .src('styles.less') // source file(s) to process .pipe(less()) // pass them through the LESS compiler .pipe(gulp.dest(f => f.base)); // Use the base directory of the source file for output done(); // signal task completion } function generateHtmlDiagrammer (done) { // Read and parse model.json fs.readFile('model.json', 'utf8', function (err, data) { if (err) { console.error('Error reading model.json:', err); done(err); return; } const model = JSON.parse(data); // Parse the JSON data // Read template.html fs.readFile('template.html', 'utf8', function (err, templateContent) { if (err) { console.error('Error reading template.html:', err); done(err); return; } // Replace placeholders in template with values from model let outputContent = templateContent; for (const [key, value] of Object.entries(model)) { const placeholder = `{{${key}}}`; // Create the placeholder outputContent = outputContent.replace(new RegExp(placeholder, 'g'), value); // Replace all occurrences } // Save the replaced content fs.writeFile('class-diagrammer.html', outputContent, 'utf8', function (err) { if (err) { console.error('Error writing class-diagrammer.html:', err); done(err); return; } console.log('class-diagrammer.html generated successfully.'); done(); // Signal completion }); }); }); } exports.transpileLess = transpileLess; exports.generateHtmlDiagrammer = generateHtmlDiagrammer; /* Run individual build tasks first, then start watching for changes see https://code.visualstudio.com/Docs/languages/CSS#_automating-sassless-compilation */ exports.autoRebuildOnChange = gulp.series(transpileLess, generateHtmlDiagrammer, function (done) { // Watch for changes in source files and rerun the corresponding build task gulp.watch('styles.less', gulp.series(transpileLess)); gulp.watch(['template.html', 'model.json'], gulp.series(generateHtmlDiagrammer)); done(); // signal task completion }); ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/package.json ================================================ { "devDependencies": { "eslint": "^8.57.1", "gulp": "^4.0.2", "gulp-less": "^5.0.0" } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/script.js ================================================ /*globals mermaid:false*/ (async () => { const getById = id => document.getElementById(id), triggerChangeOn = element => { element.dispatchEvent(new Event('change')); }, hasProperty = (obj, name) => Object.prototype.hasOwnProperty.call(obj, name); const checkable = (() => { const checked = ':checked', inputsByName = name => `input[name=${name}]`, getInput = (name, filter, context) => (context || document).querySelector(inputsByName(name) + filter), getInputs = (name, context) => (context || document).querySelectorAll(inputsByName(name)); return { getValue: (name, context) => getInput(name, checked, context).value, onChange: (name, handle, context) => { for (let input of getInputs(name, context)) input.onchange = handle; }, setChecked: (name, value, triggerChange, context) => { const input = getInput(name, `[value="${value}"]`, context); input.checked = true; if (triggerChange !== false) triggerChangeOn(input); } }; })(); const collapse = (() => { const open = 'open', isOpen = element => element.classList.contains(open), /** Toggles the open class on the collapse. * @param {HTMLElement} element The collapse to toggle. * @param {boolean} force The state to force. */ toggle = (element, force) => element.classList.toggle(open, force); return { toggle, open: element => { if (isOpen(element)) return false; // return whether collapse was opened by this process return toggle(element, true); }, initToggles: () => { for (let trigger of [...document.querySelectorAll('.toggle[href],[data-toggles]')]) { trigger.addEventListener('click', event => { event.preventDefault(); // to avoid pop-state event const trigger = event.currentTarget; trigger.ariaExpanded = !(trigger.ariaExpanded === 'true'); toggle(document.querySelector(trigger.attributes.href?.value || trigger.dataset.toggles)); }); } } }; })(); const notify = (() => { const toaster = getById('toaster'); return message => { const toast = document.createElement('span'); toast.innerText = message; toaster.appendChild(toast); // fades in the message setTimeout(() => { toast.classList.add('leaving'); // fades out the message // ...and removes it. Note this timeout has to match the animation duration for '.leaving' in the .less file. setTimeout(() => { toast.remove(); }, 1000); }, 5000); }; })(); const output = (function () { const output = getById('output'), hasSVG = () => output.childElementCount > 0, getSVG = () => hasSVG() ? output.children[0] : null, updateSvgViewBox = (svg, viewBox) => { if (svg.originalViewBox === undefined) { const vb = svg.viewBox.baseVal; svg.originalViewBox = { x: vb.x, y: vb.y, width: vb.width, height: vb.height, }; } svg.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`); }; // enable zooming SVG using Ctrl + mouse wheel const zoomFactor = 0.1, panFactor = 2023; // to go with the Zeitgeist output.addEventListener('wheel', event => { if (!event.ctrlKey || !hasSVG()) return; event.preventDefault(); const svg = getSVG(), delta = event.deltaY < 0 ? 1 : -1, zoomDelta = 1 + zoomFactor * delta, viewBox = svg.viewBox.baseVal; viewBox.width *= zoomDelta; viewBox.height *= zoomDelta; updateSvgViewBox(svg, viewBox); }); // enable panning SVG by grabbing and dragging let isPanning = false, panStartX = 0, panStartY = 0; output.addEventListener('mousedown', event => { isPanning = true; panStartX = event.clientX; panStartY = event.clientY; }); output.addEventListener('mouseup', () => { isPanning = false; }); output.addEventListener('mousemove', event => { if (!isPanning || !hasSVG()) return; event.preventDefault(); const svg = getSVG(), viewBox = svg.viewBox.baseVal, dx = event.clientX - panStartX, dy = event.clientY - panStartY; viewBox.x -= dx * panFactor / viewBox.width; viewBox.y -= dy * panFactor / viewBox.height; panStartX = event.clientX; panStartY = event.clientY; updateSvgViewBox(svg, viewBox); }); return { getDiagramTitle: () => output.dataset.title, setSVG: svg => { output.innerHTML = svg; }, getSVG, resetZoomAndPan: () => { const svg = getSVG(); if (svg !== null) updateSvgViewBox(svg, svg.originalViewBox); } }; })(); const mermaidExtensions = (() => { const logLevel = (() => { /* int indexes as well as string values can identify a valid log level; see log levels and logger definition at https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/logger.ts . Note the names correspond to console output methods https://developer.mozilla.org/en-US/docs/Web/API/console .*/ const names = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'], maxIndex = names.length - 1, getIndex = level => { const index = Number.isInteger(level) ? level : names.indexOf(level); return index < 0 ? maxIndex : Math.min(index, maxIndex); // normalize, but return maxIndex (i.e. lowest level) by default }; let requested; // the log level index of the in-coming config or the default return { /** Sets the desired log level. * @param {string|int} level The name or index of the desired log level. */ setRequested: level => { requested = getIndex(level); }, /** Returns all names above (not including) the given level. * @param {int} level The excluded lower boundary log level index (not name). * @returns an array. */ above: level => names.slice(level + 1), /** Indicates whether the log level is configured to be enabled. * @param {string|int} level The log level to test. * @returns a boolean. */ isEnabled: level => requested <= getIndex(level) }; })(); /** Calculates the shortest distance in pixels between a point * represented by 'top' and 'left' and the closest side of an axis-aligned rectangle. * Returns 0 if the point is inside or on the edge of the rectangle. * Inspired by https://gamedev.stackexchange.com/a/50722 . * @param {int} top The distance of the point from the top of the viewport. * @param {int} left The distance of the point from the left of the viewport. * @param {DOMRect} rect The bounding box to get the distance to. * @returns {int} The distance of the outside point or 0. */ function getDistanceToRect(top, left, rect) { const dx = Math.max(rect.left, Math.min(left, rect.right)), dy = Math.max(rect.top, Math.min(top, rect.bottom)); return Math.sqrt((left - dx) * (left - dx) + (top - dy) * (top - dy)); } /** Calculates the distance between two non-overlapping axis-aligned rectangles. * Returns 0 if the rectangles touch or overlap. * @param {DOMRect} a The first bounding box. * @param {DOMRect} b The second bounding box. * @returns {int} The distance between the two bounding boxes or 0 if they touch or overlap. */ function getDistance(a, b) { /** Gets coordinate pairs for the corners of a rectangle r. * @param {DOMRect} r the rectangle. * @returns {Array}} */ const getCorners = r => [[r.top, r.left], [r.top, r.right], [r.bottom, r.left], [r.bottom, r.right]], /** Gets the distances of the corners of rectA to rectB. */ getCornerDistances = (rectA, rectB) => getCorners(rectA).map(c => getDistanceToRect(c[0], c[1], rectB)), aRect = a.getBoundingClientRect(), bRect = b.getBoundingClientRect(), cornerDistances = getCornerDistances(aRect, bRect).concat(getCornerDistances(bRect, aRect)); return Math.min(...cornerDistances); } function interceptConsole(interceptorsByLevel) { const originals = {}; for (let [level, interceptor] of Object.entries(interceptorsByLevel)) { if (typeof console[level] !== 'function') continue; originals[level] = console[level]; console[level] = function () { interceptor.call(this, originals[level], arguments); }; } return () => { // call to detach interceptors for (let [level, original] of Object.entries(originals)) console[level] = original; }; } let renderedEdges = [], // contains info about the arrows between types on the diagram once rendered lastRenderedDiagram; function getRelationLabels(svg, typeId) { const edgeLabels = [...svg.querySelectorAll('.edgeLabels span.edgeLabel span')], extension = 'extension'; return renderedEdges.filter(e => e.v === typeId // type name needs to match && e.value.arrowTypeStart !== extension && e.value.arrowTypeEnd !== extension) // exclude inheritance arrows .map(edge => { const labelHtml = edge.value.label, // filter edge labels with matching HTML labels = edgeLabels.filter(l => l.outerHTML === labelHtml); if (labels.length === 1) return labels[0]; // return the only matching label else if (labels.length < 1) console.error( "Tried to find a relation label for the following edge (by its value.label) but couldn't.", edge); else { // there are multiple edge labels with the same HTML (i.e. matching relation name) // find the path that is rendered for the edge const path = svg.querySelector('.edgePaths>path.relation#' + edge.value.id), labelsByDistance = labels.sort((a, b) => getDistance(path, a) - getDistance(path, b)); console.warn('Found multiple relation labels matching the following edge (by its value.label). Returning the closest/first.', edge, labelsByDistance); return labelsByDistance[0]; // and return the matching label closest to it } }); } return { init: config => { /* Override console.info to intercept a message posted by mermaid including information about the edges (represented by arrows between types in the rendered diagram) to access the relationship info parsed from the diagram descriptions of selected types. This works around the mermaid API currently not providing access to this information and it being hard to reconstruct from the rendered SVG alone. Why do we need that info? Knowing about the relationships between types, we can find the label corresponding to a relation and attach XML documentation information to it, if available. See how getRelationLabels is used. */ const requiredLevel = 2, // to enable intercepting info message interceptors = { info: function (overridden, args) { // intercept message containing rendered edges if (args[2] === 'Graph in recursive render: XXX') renderedEdges = args[3].edges; // only forward to overridden method if this log level was originally enabled if (logLevel.isEnabled(requiredLevel)) overridden.call(this, ...args); } }; logLevel.setRequested(config.logLevel); // remember original log level // lower configured log level if required to guarantee above interceptor gets called if (!logLevel.isEnabled(requiredLevel)) config.logLevel = requiredLevel; // suppress console output for higher log levels accidentally activated by lowering to required level for (let level of logLevel.above(requiredLevel)) if (!logLevel.isEnabled(level)) interceptors[level] = () => { }; const detachInterceptors = interceptConsole(interceptors); // attaches console interceptors mermaid.initialize(config); // init the mermaid sub-system with interceptors in place detachInterceptors(); // to avoid intercepting messages outside of that context we're not interested in }, /** Processes the type selection into mermaid diagram syntax (and the corresponding XML documentation data, if available). * @param {object} typeDetails An object with the IDs of types to display in detail (i.e. with members) for keys * and objects with the data structure of ClassDiagrammer.Type (excluding the Id) for values. * @param {function} getTypeLabel A strategy for getting the type label for a type ID. * @param {string} direction The layout direction of the resulting diagram. * @param {object} showInherited A regular expression matching things to exclude from the diagram definition. * @returns {object} An object like { diagram, detailedTypes, xmlDocs } with 'diagram' being the mermaid diagram syntax, * 'xmlDocs' the corresponding XML documentation to be injected into the rendered diagram in the 'postProcess' step and * 'detailedTypes' being a flat list of IDs of types that will be rendered in detail (including their members and relations). */ processTypes: (typeDetails, getTypeLabel, direction, showInherited) => { const detailedTypes = Object.keys(typeDetails), // types that will be rendered including their members and relations xmlDocs = {}, // to be appended with docs of selected types below getAncestorTypes = typeDetails => Object.keys(typeDetails.Inherited), isRendered = type => detailedTypes.includes(type), mayNeedLabelling = new Set(), cleanUpDiagramMmd = mmd => mmd.replace(/(\r?\n){3,}/g, '\n\n'), // squash more than two consecutive line breaks down into two // renders base type and interfaces depending on settings and selected types renderSuperType = (supertTypeId, link, typeId, name, displayAll) => { /* display relation arrow if either the user chose to display this kind of super type or the super type is selected to be rendered anyway and we might as well for completeness */ if (displayAll || isRendered(supertTypeId)) { const label = name ? ' : ' + name : ''; diagram += `${supertTypeId} <|${link} ${typeId}${label}\n`; mayNeedLabelling.add(supertTypeId); } }, /* TODO watch https://github.com/mermaid-js/mermaid/issues/6034 for a solution to render multiple self-references, which is currently broken. E.g. for LightJson.JsonValue (compare console log) */ // renders HasOne and HasMany relations renderRelations = (typeId, relations, many) => { if (relations) // expecting object; only process if not null or undefined for (let [label, relatedId] of Object.entries(relations)) { const nullable = label.endsWith(' ?'); const cardinality = many ? '"*" ' : nullable ? '"?" ' : ''; if (nullable) label = label.substring(0, label.length - 2); // nullability is expressed via cardinality diagram += `${typeId} --> ${cardinality}${relatedId} : ${label}\n`; mayNeedLabelling.add(relatedId); } }, renderInheritedMembers = (typeId, details) => { const ancestorTypes = getAncestorTypes(details); // only include inherited members in sub classes if they aren't already rendered in a super class for (let [ancestorType, members] of Object.entries(details.Inherited)) { if (isRendered(ancestorType)) continue; // inherited members will be rendered in base type let ancestorsOfDetailedAncestors = ancestorTypes.filter(t => detailedTypes.includes(t)) // get detailed ancestor types .map(type => getAncestorTypes(typeDetails[type])) // select their ancestor types .reduce((union, ancestors) => union.concat(ancestors), []); // squash them into a one-dimensional array (ignoring duplicates) // skip displaying inherited members already displayed by detailed ancestor types if (ancestorsOfDetailedAncestors.includes(ancestorType)) continue; diagram += members.FlatMembers + '\n'; renderRelations(typeId, members.HasOne); renderRelations(typeId, members.HasMany, true); } }; // init diagram code with header and layout direction to be appended to below let diagram = 'classDiagram' + '\n' + 'direction ' + direction + '\n\n'; // process selected types for (let [typeId, details] of Object.entries(typeDetails)) { mayNeedLabelling.add(typeId); diagram += details.Body + '\n\n'; if (details.BaseType) // expecting object; only process if not null or undefined for (let [baseTypeId, label] of Object.entries(details.BaseType)) renderSuperType(baseTypeId, '--', typeId, label, showInherited.types); if (details.Interfaces) // expecting object; only process if not null or undefined for (let [ifaceId, labels] of Object.entries(details.Interfaces)) for (let label of labels) renderSuperType(ifaceId, '..', typeId, label, showInherited.interfaces); renderRelations(typeId, details.HasOne); renderRelations(typeId, details.HasMany, true); xmlDocs[typeId] = details.XmlDocs; if (showInherited.members && details.Inherited) renderInheritedMembers(typeId, details); } for (let typeId of mayNeedLabelling) { const label = getTypeLabel(typeId); if (label !== typeId) diagram += `class ${typeId} ["${label}"]\n`; } diagram = cleanUpDiagramMmd(diagram); lastRenderedDiagram = diagram; // store diagram syntax for export return { diagram, detailedTypes, xmlDocs }; }, getDiagram: () => lastRenderedDiagram, /** Enhances the SVG rendered by mermaid by injecting xmlDocs if available * and attaching type click handlers, if available. * @param {SVGElement} svg The SVG containing the rendered mermaid diagram. * @param {object} options An object like { xmlDocs, onTypeClick } * with 'xmlDocs' being the XML docs by type ID * and 'onTypeClick' being an event listener for the click event * that gets the event and the typeId as parameters. */ postProcess: (svg, options) => { // matches 'MyClass2' from generated id attributes in the form of 'classId-MyClass2-0' const typeIdFromDomId = /(?<=classId-)\w+(?=-\d+)/; for (let entity of svg.querySelectorAll('g.nodes>g.node').values()) { const typeId = typeIdFromDomId.exec(entity.id)[0]; // clone to have a modifiable collection without affecting the original const docs = structuredClone((options.xmlDocs || [])[typeId]); // splice in XML documentation as label titles if available if (docs) { const typeKey = '', nodeLabel = 'span.nodeLabel', title = entity.querySelector('.label-group'), relationLabels = getRelationLabels(svg, typeId), setDocs = (label, member) => { label.title = docs[member]; delete docs[member]; }, documentOwnLabel = (label, member) => { setDocs(label, member); ownLabels = ownLabels.filter(l => l !== label); // remove label }; let ownLabels = [...entity.querySelectorAll('g.label ' + nodeLabel)]; // document the type label itself if (hasProperty(docs, typeKey)) documentOwnLabel(title.querySelector(nodeLabel), typeKey); // loop through documented members longest name first for (let member of Object.keys(docs).sort((a, b) => b.length - a.length)) { // matches only whole words in front of method signatures starting with ( const memberName = new RegExp(`(? memberName.test(l.textContent)), related = relationLabels.find(l => l.textContent === member); if (related) matchingLabels.push(related); if (matchingLabels.length === 0) continue; // members may be rendered in an ancestor type if (matchingLabels.length > 1) console.warn( `Expected to find one member or relation label for ${title.textContent}.${member}` + ' to attach the XML documentation to but found multiple. Applying the first.', matchingLabels); documentOwnLabel(matchingLabels[0], member); } } if (typeof options.onTypeClick === 'function') entity.addEventListener('click', function (event) { options.onTypeClick.call(this, event, typeId); }); } } }; })(); const state = (() => { const typeUrlDelimiter = '-', originalTitle = document.head.getElementsByTagName('title')[0].textContent; const restore = async data => { if (data.d) layoutDirection.set(data.d); if (data.t) { inheritanceFilter.setFlagHash(data.i || ''); // if types are set, enable deselecting all options typeSelector.setSelected(data.t.split(typeUrlDelimiter)); await render(true); } }; function updateQueryString(href, params) { // see https://developer.mozilla.org/en-US/docs/Web/API/URL const url = new URL(href), search = url.searchParams; for (const [name, value] of Object.entries(params)) { //see https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams if (value === null || value === undefined || value === '') search.delete(name); else if (Array.isArray(value)) { search.delete(name); for (let item of value) search.append(name, item); } else search.set(name, value); } url.search = search.toString(); return url.href; } window.onpopstate = async event => { await restore(event.state); }; return { update: () => { const types = typeSelector.getSelected(), t = Object.keys(types).join(typeUrlDelimiter), d = layoutDirection.get(), i = inheritanceFilter.getFlagHash(), data = { t, d, i }, typeNames = Object.values(types).map(t => t.Name); history.pushState(data, '', updateQueryString(location.href, data)); // record selected types in title so users see which selection they return to when using a history link document.title = (typeNames.length ? typeNames.join(', ') + ' - ' : '') + originalTitle; }, restore: async () => { if (!location.search) return; // assume fresh open and don't try to restore state, preventing inheritance options from being unset const search = new URLSearchParams(location.search); await restore({ d: search.get('d'), i: search.get('i'), t: search.get('t') }); } }; })(); const typeSelector = (() => { const select = getById('type-select'), preFilter = getById('pre-filter-types'), renderBtn = getById('render'), model = JSON.parse(getById('model').innerHTML), tags = { optgroup: 'OPTGROUP', option: 'option' }, getNamespace = option => option.parentElement.nodeName === tags.optgroup ? option.parentElement.label : '', getOption = typeId => select.querySelector(tags.option + `[value='${typeId}']`); // fill select list for (let [namespace, types] of Object.entries(model.TypesByNamespace)) { let optionParent; if (namespace) { const group = document.createElement(tags.optgroup); group.label = namespace; select.appendChild(group); optionParent = group; } else optionParent = select; for (let typeId of Object.keys(types)) { const type = types[typeId], option = document.createElement(tags.option); option.value = typeId; if (!type.Name) type.Name = typeId; // set omitted label to complete structure option.innerText = type.Name; optionParent.appendChild(option); } } // only enable render button if types are selected select.onchange = () => { renderBtn.disabled = select.selectedOptions.length < 1; }; preFilter.addEventListener('input', () => { const regex = preFilter.value ? new RegExp(preFilter.value, 'i') : null; for (let option of select.options) option.hidden = regex !== null && !regex.test(option.innerHTML); // toggle option groups hidden depending on whether they have visible children for (let group of select.getElementsByTagName(tags.optgroup)) group.hidden = regex !== null && [...group.children].filter(o => !o.hidden).length === 0; }); return { focus: () => select.focus(), focusFilter: () => preFilter.focus(), setSelected: types => { for (let option of select.options) option.selected = types.includes(option.value); triggerChangeOn(select); }, toggleOption: typeId => { const option = getOption(typeId); if (option !== null) { option.selected = !option.selected; triggerChangeOn(select); } }, /** Returns the types selected by the user in the form of an object with the type IDs for keys * and objects with the data structure of ClassDiagrammer.Type (excluding the Id) for values. */ getSelected: () => Object.fromEntries([...select.selectedOptions].map(option => { const namespace = getNamespace(option), typeId = option.value, details = model.TypesByNamespace[namespace][typeId]; return [typeId, details]; })), moveSelection: up => { // inspired by https://stackoverflow.com/a/25851154 for (let option of select.selectedOptions) { if (up && option.previousElementSibling) { // move up option.parentElement.insertBefore(option, option.previousElementSibling); } else if (!up && option.nextElementSibling) { // move down // see https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore option.parentElement.insertBefore(option, option.nextElementSibling.nextElementSibling); } } }, //TODO add method returning namespace to add to title getLabel: typeId => { const option = getOption(typeId); return option ? option.innerText : model.OutsideReferences[typeId]; } }; })(); const inheritanceFilter = (() => { const baseType = getById('show-base-types'), interfaces = getById('show-interfaces'), members = getById('show-inherited-members'), getFlags = () => { return { types: baseType.checked, interfaces: interfaces.checked, members: members.checked }; }; // automatically re-render on change for (let checkbox of [baseType, interfaces, members]) checkbox.onchange = async () => { await render(); }; return { getFlags, getFlagHash: () => Object.entries(getFlags()) .filter(([, value]) => value) // only true flags .map(([key]) => key[0]).join(''), // first character of each flag setFlagHash: hash => { baseType.checked = hash.includes('t'); interfaces.checked = hash.includes('i'); members.checked = hash.includes('m'); } }; })(); const layoutDirection = (() => { const inputName = 'direction'; // automatically re-render on change checkable.onChange(inputName, async () => { await render(); }); return { get: () => checkable.getValue(inputName), set: (value, event) => { const hasEvent = event !== undefined; checkable.setChecked(inputName, value, hasEvent); if (hasEvent) event.preventDefault(); } }; })(); const render = async isRestoringState => { const { diagram, detailedTypes, xmlDocs } = mermaidExtensions.processTypes( typeSelector.getSelected(), typeSelector.getLabel, layoutDirection.get(), inheritanceFilter.getFlags()); console.info(diagram); const titledDiagram = diagram + '\naccTitle: ' + output.getDiagramTitle().replaceAll('\n', '#10;') + '\n'; /* Renders response and deconstructs returned object because we're only interested in the svg. Note that the ID supplied as the first argument must not match any existing element ID unless you want its contents to be replaced. See https://mermaid.js.org/config/usage.html#api-usage */ const { svg } = await mermaid.render('foo', titledDiagram); output.setSVG(svg); mermaidExtensions.postProcess(output.getSVG(), { xmlDocs, onTypeClick: async (event, typeId) => { // toggle selection and re-render on clicking entity typeSelector.toggleOption(typeId); await render(); } }); exportOptions.enable(detailedTypes.length > 0); if (!isRestoringState) state.update(); }; const filterSidebar = (() => { const filterForm = getById('filter'), resizing = 'resizing', toggleBtn = getById('filter-toggle'), toggle = () => collapse.toggle(filterForm); // enable rendering by hitting Enter on filter form filterForm.onsubmit = async (event) => { event.preventDefault(); await render(); }; // enable adjusting max sidebar width (() => { const filterWidthOverride = getById('filter-width'), // a style tag dedicated to overriding the default filter max-width minWidth = 210, maxWidth = window.innerWidth / 2; // limit the width of the sidebar let isDragging = false; // tracks whether the sidebar is being dragged let pickedUp = 0; // remembers where the dragging started from let widthBefore = 0; // remembers the width when dragging starts let change = 0; // remembers the total distance of the drag toggleBtn.addEventListener('mousedown', (event) => { isDragging = true; pickedUp = event.clientX; widthBefore = filterForm.offsetWidth; }); document.addEventListener('mousemove', (event) => { if (!isDragging) return; const delta = event.clientX - pickedUp, newWidth = Math.max(minWidth, Math.min(maxWidth, widthBefore + delta)); change = delta; filterForm.classList.add(resizing); filterWidthOverride.innerHTML = `#filter.open { max-width: ${newWidth}px; }`; }); document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; filterForm.classList.remove(resizing); }); // enable toggling filter info on click toggleBtn.addEventListener('click', () => { if (Math.abs(change) < 5) toggle(); // prevent toggling for small, accidental drags change = 0; // reset the remembered distance to enable subsequent clicks }); })(); return { toggle, open: () => collapse.open(filterForm) }; })(); /* Shamelessly copied from https://github.com/mermaid-js/mermaid-live-editor/blob/develop/src/lib/components/Actions.svelte with only a few modifications after I failed to get the solutions described here working: https://stackoverflow.com/questions/28226677/save-inline-svg-as-jpeg-png-svg/28226736#28226736 The closest I got was with this example https://canvg.js.org/examples/offscreen , but the shapes would remain empty. */ const exporter = (() => { const getSVGstring = (svg, width, height) => { height && svg?.setAttribute('height', `${height}px`); width && svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage if (!svg) svg = getSvgEl(); return svg.outerHTML.replaceAll('
', '
') .replaceAll(/]*)>/g, (m, g) => ``); }; const toBase64 = utf8String => { const bytes = new TextEncoder().encode(utf8String); return window.btoa(String.fromCharCode.apply(null, bytes)); }; const getBase64SVG = (svg, width, height) => toBase64(getSVGstring(svg, width, height)); const exportImage = (event, exporter, imagemodeselected, userimagesize) => { const canvas = document.createElement('canvas'); const svg = document.querySelector('#output svg'); if (!svg) { throw new Error('svg not found'); } const box = svg.getBoundingClientRect(); canvas.width = box.width; canvas.height = box.height; if (imagemodeselected === 'width') { const ratio = box.height / box.width; canvas.width = userimagesize; canvas.height = userimagesize * ratio; } else if (imagemodeselected === 'height') { const ratio = box.width / box.height; canvas.width = userimagesize * ratio; canvas.height = userimagesize; } const context = canvas.getContext('2d'); if (!context) { throw new Error('context not found'); } context.fillStyle = 'white'; context.fillRect(0, 0, canvas.width, canvas.height); const image = new Image(); image.onload = exporter(context, image); image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`; event.stopPropagation(); event.preventDefault(); }; const getSvgEl = () => { const svgEl = document.querySelector('#output svg').cloneNode(true); svgEl.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link')) .map((l) => l.href) .find((h) => h.includes('font-awesome')); if (fontAwesomeCdnUrl == null) { return svgEl; } const styleEl = document.createElement('style'); styleEl.innerText = `@import url("${fontAwesomeCdnUrl}");'`; svgEl.prepend(styleEl); return svgEl; }; const simulateDownload = (download, href) => { const a = document.createElement('a'); a.download = download; a.href = href; a.click(); a.remove(); }; const downloadImage = (context, image) => { return () => { const { canvas } = context; context.drawImage(image, 0, 0, canvas.width, canvas.height); simulateDownload( exportOptions.getFileName('png'), canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream') ); }; }; const tryWriteToClipboard = blob => { try { if (!blob) throw new Error('blob is empty'); void navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]); return true; } catch (error) { console.error(error); return false; } }; const copyPNG = (context, image) => { return () => { const { canvas } = context; context.drawImage(image, 0, 0, canvas.width, canvas.height); canvas.toBlob(blob => { tryWriteToClipboard(blob); }); }; }; const tryWriteTextToClipboard = async text => { try { if (!text) throw new Error('text is empty'); await navigator.clipboard.writeText(text); return true; } catch (error) { console.error(error); return false; } }; const copyText = async (event, text) => { if (await tryWriteTextToClipboard(text)) { event.stopPropagation(); event.preventDefault(); } }; return { isClipboardAvailable: () => hasProperty(window, 'ClipboardItem'), onCopyPNG: (event, imagemodeselected, userimagesize) => { exportImage(event, copyPNG, imagemodeselected, userimagesize); }, onCopySVG: event => { void copyText(event, getSVGstring()); }, onCopyMMD: (event, diagram) => { void copyText(event, diagram); }, onDownloadPNG: (event, imagemodeselected, userimagesize) => { exportImage(event, downloadImage, imagemodeselected, userimagesize); }, onDownloadSVG: () => { simulateDownload(exportOptions.getFileName('svg'), `data:image/svg+xml;base64,${getBase64SVG()}`); }, onDownloadMMD: diagram => { simulateDownload(exportOptions.getFileName('mmd'), `data:text/vnd.mermaid;base64,${toBase64(diagram)}`); } }; })(); const exportOptions = (() => { let wereOpened = false; // used to track whether user was able to see save options and may quick-save const container = getById('exportOptions'), toggle = getById('exportOptions-toggle'), saveBtn = getById('save'), copyBtn = getById('copy'), saveAs = 'saveAs', png = 'png', svg = 'svg', isDisabled = () => toggle.hidden, // using toggle visibility as indicator open = () => { wereOpened = true; return collapse.open(container); }, copy = event => { if (isDisabled()) return; // allow the default for copying text if no types are rendered if (!exporter.isClipboardAvailable()) notify('The clipboard seems unavailable in this browser :('); else { const type = checkable.getValue(saveAs); try { if (type === png) { const [dimension, size] = getDimensions(); exporter.onCopyPNG(event, dimension, size); } else if (type === svg) exporter.onCopySVG(event); else exporter.onCopyMMD(event, mermaidExtensions.getDiagram()); notify(`The diagram ${type.toUpperCase()} is in your clipboard.`); } catch (e) { notify(e.toString()); } } }, save = event => { const type = checkable.getValue(saveAs); if (type === png) { const [dimension, size] = getDimensions(); exporter.onDownloadPNG(event, dimension, size); } else if (type === svg) exporter.onDownloadSVG(); else exporter.onDownloadMMD(mermaidExtensions.getDiagram()); }; const getDimensions = (() => { const inputName = 'dimension', scale = 'scale', dimensions = getById('dimensions'), scaleInputs = container.querySelectorAll('#scale-controls input'); // enable toggling dimension controls checkable.onChange(saveAs, event => { collapse.toggle(dimensions, event.target.value === png); }, container); // enable toggling scale controls checkable.onChange(inputName, event => { const disabled = event.target.value !== scale; for (let input of scaleInputs) input.disabled = disabled; }, container); return () => { let dimension = checkable.getValue(inputName); // return dimension to scale to desired size if not exporting in current size if (dimension !== 'auto') dimension = checkable.getValue(scale); return [dimension, getById('scale-size').value]; }; })(); if (exporter.isClipboardAvailable()) copyBtn.onclick = copy; else copyBtn.hidden = true; saveBtn.onclick = save; return { copy, getFileName: ext => `${saveBtn.dataset.assembly}-diagram-${new Date().toISOString().replace(/[Z:.]/g, '')}.${ext}`, enable: enable => { if (!enable) collapse.toggle(container, false); // make sure the container is closed when disabling toggle.hidden = !enable; }, quickSave: event => { if (isDisabled()) return; // allow the default for saving HTML doc if no types are rendered if (wereOpened) { save(event); // allow quick save return; } const filterOpened = filterSidebar.open(), optionsOpenend = open(); /* Make sure the collapses containing the save options are open and visible when user hits Ctrl + S. If neither needed opening, trigger saving. I.e. hitting Ctrl + S again should do it. */ if (!filterOpened && !optionsOpenend) save(event); else event.preventDefault(); // prevent saving HTML page } }; })(); // displays pressed keys and highlights mouse cursor for teaching usage and other presentations const controlDisplay = (function () { let used = new Set(), enabled = false, wheelTimeout; const alt = 'Alt', display = getById('pressed-keys'), // a label displaying the keys being pressed and mouse wheel being scrolled mouse = getById('mouse'), // a circle tracking the mouse to make following it easier translateKey = key => key.length === 1 ? key.toUpperCase() : key, updateDisplay = () => { display.textContent = [...used].join(' + '); display.classList.toggle('hidden', used.size === 0); }, eventHandlers = { keydown: event => { if (event.altKey) used.add(alt); // handle separately because Alt key alone doesn't trigger a key event used.add(translateKey(event.key)); updateDisplay(); }, keyup: event => { setTimeout(() => { if (!event.altKey && used.has(alt)) used.delete(alt); used.delete(translateKey(event.key)); updateDisplay(); }, 500); }, wheel: event => { const label = 'wheel ' + (event.deltaY < 0 ? 'up' : 'down'), wasUsed = used.has(label); if (wasUsed) { if (wheelTimeout) clearTimeout(wheelTimeout); } else { used.add(label); updateDisplay(); } // automatically remove wheelTimeout = setTimeout(() => { used.delete(label); updateDisplay(); wheelTimeout = undefined; }, 500); }, mousemove: event => { mouse.style.top = event.clientY + 'px'; mouse.style.left = event.clientX + 'px'; }, mousedown: () => { mouse.classList.add('down'); }, mouseup: () => { setTimeout(() => { mouse.classList.remove('down'); }, 300); } }; return { toggle: () => { enabled = !enabled; if (enabled) { mouse.hidden = false; for (let [event, handler] of Object.entries(eventHandlers)) document.addEventListener(event, handler); } else { mouse.hidden = true; for (let [event, handler] of Object.entries(eventHandlers)) document.removeEventListener(event, handler); used.clear(); updateDisplay(); } } }; })(); // key bindings document.onkeydown = async (event) => { const arrowUp = 'ArrowUp', arrowDown = 'ArrowDown'; // support Cmd key as alternative on Mac, see https://stackoverflow.com/a/5500536 if (event.ctrlKey || event.metaKey) { switch (event.key) { case 'b': filterSidebar.toggle(); return; case 'k': event.preventDefault(); filterSidebar.open(); typeSelector.focusFilter(); return; case 's': exportOptions.quickSave(event); return; case 'c': exportOptions.copy(event); return; case 'i': event.preventDefault(); controlDisplay.toggle(); return; case 'ArrowLeft': layoutDirection.set('RL', event); return; case 'ArrowRight': layoutDirection.set('LR', event); return; case arrowUp: layoutDirection.set('BT', event); return; case arrowDown: layoutDirection.set('TB', event); return; case '0': output.resetZoomAndPan(); return; } } if (event.altKey) { // naturally triggered by Mac's option key as well // enable moving selected types up and down using arrow keys while holding [Alt] const upOrDown = event.key === arrowUp ? true : event.key === arrowDown ? false : null; if (upOrDown !== null) { typeSelector.focus(); typeSelector.moveSelection(upOrDown); event.preventDefault(); return; } // pulse-animate elements with helping title attributes to point them out if (event.key === 'i') { event.preventDefault(); const pulsing = 'pulsing'; for (let element of document.querySelectorAll('[title],:has(title)')) { element.addEventListener('animationend', () => { element.classList.remove(pulsing); }, { once: true }); element.classList.add(pulsing); } } } }; // rewrite help replacing references to 'Ctrl' with 'Cmd' for Mac users if (/(Mac)/i.test(navigator.userAgent)) { const ctrl = /Ctrl/mg, replace = source => source.replaceAll(ctrl, '⌘'); for (let titled of document.querySelectorAll('[title]')) if (ctrl.test(titled.title)) titled.title = replace(titled.title); for (let titled of document.querySelectorAll('[data-title]')) if (ctrl.test(titled.dataset.title)) titled.dataset.title = replace(titled.dataset.title); for (let element of getById('info').querySelectorAll('*')) { const text = element.innerText || element.textContent; // Get the text content of the element if (ctrl.test(text)) element.innerHTML = replace(text); } } collapse.initToggles(); mermaidExtensions.init({ startOnLoad: false }); // initializes mermaid as well typeSelector.focus(); // focus type filter initially to enable keyboard input await state.restore(); })(); ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/styles.css ================================================ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } body { font-family: system-ui, sans-serif; background: #4e54c8; background-image: linear-gradient(to left, #8f94fb, #4e54c8); } input[type=text] { border-radius: 3px; } button { border-radius: 3px; background-color: #aad; border: none; color: #117; cursor: pointer; } button.icon { font-size: 1em; background-color: transparent; } button:disabled { opacity: 0.5; } [type=checkbox], [type=radio] { cursor: pointer; } [type=checkbox] ~ label, [type=radio] ~ label { cursor: pointer; } fieldset { border-radius: 5px; } select { border: none; border-radius: 3px; background-color: rgba(0, 0, 0, calc(3/16 * 1)); color: whitesmoke; } select option:checked { background-color: rgba(0, 0, 0, calc(3/16 * 1)); color: darkorange; } .flx:not([hidden]) { display: flex; } .flx:not([hidden]).col { flex-direction: column; } .flx:not([hidden]).spaced { justify-content: space-between; } .flx:not([hidden]).gap { gap: 0.5em; } .flx:not([hidden]).aligned { align-items: center; } .flx:not([hidden]) .grow { flex-grow: 1; } .collapse.vertical { max-height: 0; overflow: hidden; transition: max-height ease-in-out 0.5s; } .collapse.vertical.open { max-height: 100vh; } .collapse.horizontal { max-width: 0; padding: 0; margin: 0; transition: all ease-in-out 0.5s; overflow: hidden; } .collapse.horizontal.open { padding: revert; max-width: 100vw; } .toggle, [data-toggles] { cursor: pointer; } .container { position: absolute; inset: 0; margin: 0; } .scndry { font-size: smaller; } .mano-a-borsa { transform: rotate(95deg); cursor: pointer; } .mano-a-borsa:after { content: '🤏'; } .trawl-net { transform: rotate(180deg) translateY(-2px); display: inline-block; } .trawl-net:after { content: '🥅'; } .torch { display: inline-block; } .torch:after { content: '🔦'; } .pulsing { animation: whiteBoxShadowPulse 2s 3; } @keyframes whiteBoxShadowPulse { 0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); } 5% { box-shadow: 0 0 0 15px rgba(255, 255, 255, 0.5); } 50% { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1); } 90% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); } } #content { height: 100%; position: relative; } #filter { max-width: 0; transition: max-width ease-in-out 0.5s; overflow: hidden; background-color: rgba(0, 0, 0, calc(3/16 * 1)); color: whitesmoke; } #filter.open { max-width: 15em; overflow: auto; } #filter.resizing { transition: none; } #filter > * { margin: 0.3em 0.3em 0; } #filter > *:last-child { margin-bottom: 0.3em; } #filter #pre-filter-types { min-width: 3em; } #filter [data-toggles="#info"] .torch { transform: rotate(-90deg); transition: transform 0.5s; } #filter [data-toggles="#info"][aria-expanded=true] .torch { transform: rotate(-255deg); } #filter #info { overflow: auto; background-color: rgba(255, 255, 255, calc(1/16 * 2)); } #filter #info a.toggle { color: whitesmoke; } #filter #info a.toggle img { height: 1em; } #filter #type-select { overflow: auto; } #filter #inheritance { padding: 0.1em 0.75em 0.2em; } #filter #direction [type=radio] { display: none; } #filter #direction [type=radio]:checked + label { background-color: rgba(255, 255, 255, calc(1/16 * 4)); } #filter #direction label { flex-grow: 1; text-align: center; margin: -1em 0 -0.7em; padding-top: 0.2em; } #filter #direction label:first-of-type { margin-left: -0.8em; border-top-left-radius: 5px; border-bottom-left-radius: 5px; } #filter #direction label:last-of-type { margin-right: -0.8em; border-top-right-radius: 5px; border-bottom-right-radius: 5px; } #filter #actions { margin-top: 1em; justify-content: space-between; } #filter #actions #render { font-weight: bold; } #filter #exportOptions { overflow: auto; background-color: rgba(255, 255, 255, calc(1/16 * 2)); } #filter #exportOptions #save { margin-right: 0.5em; } #filter #exportOptions #dimensions fieldset { padding: 0.5em; } #filter #exportOptions #dimensions fieldset .scale-size { margin-left: 0.5em; } #filter #exportOptions #dimensions fieldset .scale-size #scale-size { width: 2.5em; margin: 0 0.2em; } #filter-toggle { padding: 0; border-radius: 0; background-color: #117; color: whitesmoke; } #output { overflow: auto; } #output > svg { cursor: grab; } #output > svg:active { cursor: grabbing; } #output .edgeLabels .edgeTerminals .edgeLabel { color: whitesmoke; } #output .edgeLabels .edgeLabel { border-radius: 3px; } #output .edgeLabels .edgeLabel .edgeLabel[title] { color: darkgoldenrod; } #output path.relation { stroke: whitesmoke; } #output g.nodes > g { cursor: pointer; } #output g.nodes > g > rect { rx: 5px; ry: 5px; } #output g.nodes g.label .nodeLabel[title] { color: darkgoldenrod; } #about { position: absolute; bottom: 2em; right: 2em; align-items: end; } #about #toaster { margin-right: 2.8em; } #about #toaster span { animation: 0.5s ease-in fadeIn; border-radius: 0.5em; padding: 0.5em; background-color: rgba(0, 0, 0, calc(3/16 * 2)); color: whitesmoke; } #about #toaster span.leaving { animation: 1s ease-in-out fadeOut; } #about .build-info { align-items: end; height: 2.3em; border-radius: 7px; background-color: rgba(0, 0, 0, calc(3/16 * 3)); color: whitesmoke; } #about .build-info > * { height: 100%; } #about .build-info #build-info { text-align: right; } #about .build-info #build-info > * { padding: 0 0.5em; } #about .build-info #build-info a { color: whitesmoke; } #about .build-info #build-info a:not(.project) { text-decoration: none; } #about .build-info #build-info a span { display: inline-block; } #pressed-keys { position: fixed; left: 50%; transform: translateX(-50%); font-size: 3em; bottom: 1em; opacity: 1; border-radius: 0.5em; padding: 0.5em; background-color: rgba(0, 0, 0, calc(3/16 * 2)); color: whitesmoke; } #pressed-keys.hidden { transition: opacity 0.5s ease-in-out; opacity: 0; } #mouse { position: fixed; transform: translateX(-50%) translateY(-50%); height: 2em; width: 2em; pointer-events: none; z-index: 9999; border-radius: 1em; border: solid 0.1em yellow; } #mouse.down { background-color: #ff08; } /* hide stuff in print view */ @media print { #filter, #filter-toggle, #about, img, .bubbles { display: none; } } /* ANIMATED BACKGROUND, from https://codepen.io/alvarotrigo/pen/GRvYNax found in https://alvarotrigo.com/blog/animated-backgrounds-css/ */ @keyframes rotateUp { 0% { transform: translateY(0) rotate(0deg); opacity: 1; border-radius: 100%; } 100% { transform: translateY(-150vh) rotate(720deg); opacity: 0; border-radius: 0; } } .bubbles { overflow: hidden; } .bubbles li { position: absolute; display: block; list-style: none; width: 20px; height: 20px; background: rgba(255, 255, 255, 0.2); animation: rotateUp 25s linear infinite; bottom: -150px; } .bubbles li:nth-child(1) { left: 25%; width: 80px; height: 80px; animation-delay: 0s; } .bubbles li:nth-child(2) { left: 10%; width: 20px; height: 20px; animation-delay: 2s; animation-duration: 12s; } .bubbles li:nth-child(3) { left: 70%; width: 20px; height: 20px; animation-delay: 4s; } .bubbles li:nth-child(4) { left: 40%; width: 60px; height: 60px; animation-delay: 0s; animation-duration: 18s; } .bubbles li:nth-child(5) { left: 65%; width: 20px; height: 20px; animation-delay: 0s; } .bubbles li:nth-child(6) { left: 75%; width: 110px; height: 110px; animation-delay: 3s; } .bubbles li:nth-child(7) { left: 35%; width: 150px; height: 150px; animation-delay: 7s; } .bubbles li:nth-child(8) { left: 50%; width: 25px; height: 25px; animation-delay: 15s; animation-duration: 45s; } .bubbles li:nth-child(9) { left: 20%; width: 15px; height: 15px; animation-delay: 2s; animation-duration: 35s; } .bubbles li:nth-child(10) { left: 85%; width: 150px; height: 150px; animation-delay: 0s; animation-duration: 11s; } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/styles.less ================================================ @darkBlue: #117; @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } .clickable() { cursor: pointer; } .useBrightText() { color: whitesmoke; } .colorLabelWithDocs() { color: darkgoldenrod; } .darkenBg(@times: 1) { background-color: rgba(0,0,0, calc(3/16 * @times)); } .brightenBg(@times: 1) { background-color: rgba(255,255,255, calc(1/16 * @times)); } body { font-family: system-ui, sans-serif; background: #4e54c8; background-image: linear-gradient(to left, #8f94fb, #4e54c8); } input[type=text] { border-radius: 3px; } button { border-radius: 3px; background-color: #aad; border: none; color: @darkBlue; .clickable; &.icon { font-size: 1em; background-color: transparent; } &:disabled { opacity: .5; } } [type=checkbox], [type=radio] { .clickable; & ~ label { .clickable; } } fieldset { border-radius: 5px; } select { border: none; border-radius: 3px; .darkenBg; .useBrightText; option:checked { .darkenBg; color: darkorange; } } .flx:not([hidden]) { display: flex; &.col { flex-direction: column; } &.spaced { justify-content: space-between; } &.gap { gap: .5em; } &.aligned { align-items: center; } .grow { flex-grow: 1; } } .collapse { &.vertical { max-height: 0; overflow: hidden; transition: max-height ease-in-out .5s; &.open { max-height: 100vh; } } &.horizontal { max-width: 0; padding: 0; margin: 0; transition: all ease-in-out .5s; overflow: hidden; &.open { padding: revert; max-width: 100vw; } } } .toggle, [data-toggles] { .clickable; } .container { position: absolute; inset: 0; margin: 0; } .scndry { font-size: smaller; } .mano-a-borsa { transform: rotate(95deg); .clickable; &:after { content: '🤏'; } } .trawl-net { transform: rotate(180deg) translateY(-2px); display: inline-block; &:after { content: '🥅'; } } .torch { display: inline-block; &:after { content: '🔦'; } } .pulsing { animation: whiteBoxShadowPulse 2s 3; } @keyframes whiteBoxShadowPulse { 0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); } 5% { box-shadow: 0 0 0 15px rgba(255, 255, 255, 0.5); } 50% { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1); } 90% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); } } #content { height: 100%; position: relative; } #filter { max-width: 0; transition: max-width ease-in-out .5s; overflow: hidden; .darkenBg; .useBrightText; &.open { max-width: 15em; overflow: auto; } &.resizing { transition: none; } > * { margin: .3em .3em 0; &:last-child { margin-bottom: .3em; } } #pre-filter-types { min-width: 3em; } [data-toggles="#info"] { .torch { transform: rotate(-90deg); transition: transform .5s; } &[aria-expanded=true] { .torch { transform: rotate(-255deg); } } } #info { overflow: auto; .brightenBg(2); a.toggle { .useBrightText; img { height: 1em; } } } #type-select { overflow: auto; } #inheritance { padding: .1em .75em .2em; } #direction { [type=radio] { display: none; &:checked + label { .brightenBg(4); } } label { flex-grow: 1; text-align: center; margin: -1em 0 -.7em; padding-top: .2em; &:first-of-type { margin-left: -.8em; border-top-left-radius: 5px; border-bottom-left-radius: 5px; } &:last-of-type { margin-right: -.8em; border-top-right-radius: 5px; border-bottom-right-radius: 5px; } } } #actions { margin-top: 1em; justify-content: space-between; #render { font-weight: bold; } } #exportOptions { overflow: auto; .brightenBg(2); #save { margin-right: .5em; } #dimensions fieldset { padding: .5em; .scale-size { margin-left: .5em; #scale-size { width: 2.5em; margin: 0 .2em; } } } } } #filter-toggle { padding: 0; border-radius: 0; background-color: @darkBlue; .useBrightText; } #output { overflow: auto; > svg { cursor: grab; &:active { cursor: grabbing; } } .edgeLabels { .edgeTerminals .edgeLabel { .useBrightText; } .edgeLabel { border-radius: 3px; .edgeLabel[title] { .colorLabelWithDocs; } } } path.relation { stroke: whitesmoke; } g.nodes { > g { .clickable; > rect { rx: 5px; ry: 5px; } } g.label .nodeLabel[title] { .colorLabelWithDocs; } } } #about { position: absolute; bottom: 2em; right: 2em; align-items: end; @logoWidth: 2.3em; #toaster { margin-right: @logoWidth + .5em; span { animation: .5s ease-in fadeIn; border-radius: .5em; padding: .5em; .darkenBg(2); .useBrightText; &.leaving { animation: 1s ease-in-out fadeOut; } } } .build-info { align-items: end; height: @logoWidth; border-radius: 7px; .darkenBg(3); .useBrightText; > * { height: 100%; } #build-info { text-align: right; > * { padding: 0 .5em; } a { .useBrightText; &:not(.project) { text-decoration: none; } span { display: inline-block; } } } } } #pressed-keys { position: fixed; left: 50%; transform: translateX(-50%); font-size: 3em; bottom: 1em; opacity: 1; border-radius: .5em; padding: .5em; .darkenBg(2); .useBrightText; &.hidden { transition: opacity 0.5s ease-in-out; opacity: 0; } } #mouse { position: fixed; transform: translateX(-50%) translateY(-50%); height: 2em; width: 2em; pointer-events: none; z-index: 9999; border-radius: 1em; border: solid .1em yellow; &.down { background-color: #ff08; } } /* hide stuff in print view */ @media print { #filter, #filter-toggle, #about, img, .bubbles { display: none; } } /* ANIMATED BACKGROUND, from https://codepen.io/alvarotrigo/pen/GRvYNax found in https://alvarotrigo.com/blog/animated-backgrounds-css/ */ @keyframes rotateUp { 0% { transform: translateY(0) rotate(0deg); opacity: 1; border-radius: 100%; } 100% { transform: translateY(-150vh) rotate(720deg); opacity: 0; border-radius: 0; } } .bubbles { overflow: hidden; li { position: absolute; display: block; list-style: none; width: 20px; height: 20px; background: rgba(255, 255, 255, .2); animation: rotateUp 25s linear infinite; bottom: -150px; &:nth-child(1) { left: 25%; width: 80px; height: 80px; animation-delay: 0s; } &:nth-child(2) { left: 10%; width: 20px; height: 20px; animation-delay: 2s; animation-duration: 12s; } &:nth-child(3) { left: 70%; width: 20px; height: 20px; animation-delay: 4s; } &:nth-child(4) { left: 40%; width: 60px; height: 60px; animation-delay: 0s; animation-duration: 18s; } &:nth-child(5) { left: 65%; width: 20px; height: 20px; animation-delay: 0s; } &:nth-child(6) { left: 75%; width: 110px; height: 110px; animation-delay: 3s; } &:nth-child(7) { left: 35%; width: 150px; height: 150px; animation-delay: 7s; } &:nth-child(8) { left: 50%; width: 25px; height: 25px; animation-delay: 15s; animation-duration: 45s; } &:nth-child(9) { left: 20%; width: 15px; height: 15px; animation-delay: 2s; animation-duration: 35s; } &:nth-child(10) { left: 85%; width: 150px; height: 150px; animation-delay: 0s; animation-duration: 11s; } } } ================================================ FILE: ICSharpCode.ILSpyX/MermaidDiagrammer/html/template.html ================================================ {{SourceAssemblyName}} class diagrammer - ILSpy

The type picker is ✜ focused when you open the app. You can just ⌨️ key in the first letter/s of the type you want to start your diagram with and hit [Enter] to render it.

After rendering you can 👆 tap types on the diagram to update your selection and redraw. This allows you to explore the domain along relations.

Don't forget that you can hold [Shift] to ↕ range-select and [Ctrl] to ± add to or subtract from your selection.

Note that the diagram has a 🟈 layout direction - i.e. it depends on how you ⇅ sort selected types using [Alt + Arrow Up|Down].

Changing the type selection or rendering options updates the URL in the location bar. That means you can

  • 🔖 bookmark or 📣 share the URL to your diagram with whoever has access to this diagrammer,
  • access 🕔 earlier diagrams recorded in your 🧾 browser history and
  • ⇥ restore your type selection to the picker from the URL using ⟳ Refresh [F5] if you lose it.

Looking for help with something else?

Stop and spot the tooltips. 🌷 They'll give you more info where necessary. Get a hint for elements with helping tooltips using [Alt + i].

Alternatively, find helpful links to the docs and discussions in the build info

If you find this helpful and want to share your 📺 screen and 🎓 wisdom on how it works with a 🦗 newcomer, try toggling presentation mode using [Ctrl + i].

show inherited
layout direction
png dimensions
built from {{SourceAssemblyName}} v{{SourceAssemblyVersion}} and mermaid.js from CDN 📥 using ICSharpCode.ILSpyX v{{BuilderVersion}} 📜 💬 🌩️
================================================ FILE: ICSharpCode.ILSpyX/PackageReadme.md ================================================ ## About ICSharpCode.ILSpyX is the core cross-platform implemenation of [ILSpy](https://github.com/icsharpcode/ILSpy/) that can be reused to build alternate frontends more easily. ================================================ FILE: ICSharpCode.ILSpyX/PdbProvider/DebugInfoUtils.cs ================================================ // 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.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; #nullable enable namespace ICSharpCode.ILSpyX.PdbProvider { public static class DebugInfoUtils { public static IDebugInfoProvider? LoadSymbols(PEFile module) { try { // try to open portable pdb file/embedded pdb info: if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) { return new PortableDebugInfoProvider(module.FileName, provider, MetadataReaderOptions.Default, pdbFileName); } else { // search for pdb in same directory as dll pdbFileName = Path.Combine( Path.GetDirectoryName(module.FileName)!, Path.GetFileNameWithoutExtension(module.FileName) + ".pdb" ); if (File.Exists(pdbFileName)) { return new MonoCecilDebugInfoProvider(module, pdbFileName); } } } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) { // Ignore PDB load errors } return null; } public static IDebugInfoProvider? FromFile(PEFile module, string pdbFileName) { if (string.IsNullOrEmpty(pdbFileName)) return null; Stream? stream = OpenStream(pdbFileName); if (stream == null) return null; if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) { stream.Position = 0; return new MonoCecilDebugInfoProvider(module, pdbFileName); } else { stream.Position = 0; var provider = MetadataReaderProvider.FromPortablePdbStream(stream); return new PortableDebugInfoProvider(module.FileName, provider, MetadataReaderOptions.Default, pdbFileName); } } const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00"; static readonly byte[] buffer = new byte[LegacyPDBPrefix.Length]; static bool TryOpenPortablePdb(PEFile module, [NotNullWhen(true)] out MetadataReaderProvider? provider, [NotNullWhen(true)] out string? pdbFileName) { provider = null; pdbFileName = null; var reader = module.Reader; foreach (var entry in reader.ReadDebugDirectory()) { if (entry.IsPortableCodeView) { return reader.TryOpenAssociatedPortablePdb(module.FileName, OpenStream, out provider, out pdbFileName); } if (entry.Type == DebugDirectoryEntryType.CodeView) { string pdbDirectory = Path.GetDirectoryName(module.FileName)!; pdbFileName = Path.Combine( pdbDirectory, Path.GetFileNameWithoutExtension(module.FileName) + ".pdb"); Stream? stream = OpenStream(pdbFileName); if (stream != null) { if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) { return false; } stream.Position = 0; provider = MetadataReaderProvider.FromPortablePdbStream(stream); return true; } } } return false; } static Stream? OpenStream(string fileName) { if (!File.Exists(fileName)) return null; var memory = new MemoryStream(); using (var stream = File.OpenRead(fileName)) stream.CopyTo(memory); memory.Position = 0; return memory; } } } ================================================ FILE: ICSharpCode.ILSpyX/PdbProvider/MonoCecilDebugInfoProvider.cs ================================================ // 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.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using Mono.Cecil; using Mono.Cecil.Pdb; using SRM = System.Reflection.Metadata; #nullable enable namespace ICSharpCode.ILSpyX.PdbProvider { public class MonoCecilDebugInfoProvider : IDebugInfoProvider { readonly Dictionary SequencePoints, IList Variables)> debugInfo; public unsafe MonoCecilDebugInfoProvider(PEFile module, string pdbFileName, string? description = null) { if (module == null) { throw new ArgumentNullException(nameof(module)); } if (!module.Reader.IsEntireImageAvailable) { throw new ArgumentException("This provider needs access to the full image!"); } this.SourceFileName = pdbFileName ?? throw new ArgumentNullException(nameof(pdbFileName)); this.Description = description ?? $"Loaded from PDB file: {pdbFileName}"; var image = module.Reader.GetEntireImage(); this.debugInfo = new Dictionary SequencePoints, IList Variables)>(); using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream(image.Pointer, image.Length)) using (var moduleDef = ModuleDefinition.ReadModule(stream)) { moduleDef.ReadSymbols(new PdbReaderProvider().GetSymbolReader(moduleDef, pdbFileName)); foreach (var method in module.Metadata.MethodDefinitions) { var cecilMethod = moduleDef.LookupToken(MetadataTokens.GetToken(method)) as MethodDefinition; var debugInfo = cecilMethod?.DebugInformation; if (debugInfo == null) continue; IList sequencePoints = EmptyList.Instance; if (debugInfo.HasSequencePoints) { sequencePoints = new List(debugInfo.SequencePoints.Count); foreach (var point in debugInfo.SequencePoints) { sequencePoints.Add(new SequencePoint { Offset = point.Offset, StartLine = point.StartLine, StartColumn = point.StartColumn, EndLine = point.EndLine, EndColumn = point.EndColumn, DocumentUrl = point.Document.Url }); } } var variables = new List(); foreach (var scope in debugInfo.GetScopes()) { if (!scope.HasVariables) continue; foreach (var v in scope.Variables) { variables.Add(new Variable(v.Index, v.Name)); } } this.debugInfo.Add(method, (sequencePoints, variables)); } } } public string Description { get; } public string SourceFileName { get; } public IList GetSequencePoints(SRM.MethodDefinitionHandle handle) { if (!debugInfo.TryGetValue(handle, out var info)) { return EmptyList.Instance; } return info.SequencePoints; } public IList GetVariables(SRM.MethodDefinitionHandle handle) { if (!debugInfo.TryGetValue(handle, out var info)) { return EmptyList.Instance; } return info.Variables; } public bool TryGetName(SRM.MethodDefinitionHandle handle, int index, [NotNullWhen(true)] out string? name) { name = null; if (!debugInfo.TryGetValue(handle, out var info)) { return false; } var variable = info.Variables.FirstOrDefault(v => v.Index == index); name = variable.Name; return name != null; } public bool TryGetExtraTypeInfo(SRM.MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo) { // Mono.Cecil's WindowsPDB reader is unable to read tuple element names // and dynamic flags custom debug information. extraTypeInfo = default; return false; } } } ================================================ FILE: ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs ================================================ // 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.CodeAnalysis; using System.IO; using System.Reflection.Metadata; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataFile; #nullable enable namespace ICSharpCode.ILSpyX.PdbProvider { public class PortableDebugInfoProvider : IDebugInfoProvider { string? pdbFileName; string moduleFileName; readonly MetadataReaderProvider provider; MetadataReaderOptions options; bool hasError; public bool IsEmbedded => pdbFileName == null; public PortableDebugInfoProvider(string moduleFileName, MetadataReaderProvider provider, MetadataReaderOptions options = MetadataReaderOptions.Default, string? pdbFileName = null) { this.moduleFileName = moduleFileName ?? throw new ArgumentNullException(nameof(moduleFileName)); this.provider = provider ?? throw new ArgumentNullException(nameof(provider)); this.options = options; this.pdbFileName = pdbFileName; } public string Description { get { if (pdbFileName == null) { if (hasError) return "Error while loading the PDB stream embedded in this assembly"; return "Embedded in this assembly"; } else { if (hasError) return $"Error while loading portable PDB: {pdbFileName}"; return $"Loaded from portable PDB: {pdbFileName}"; } } } public MetadataReader? GetMetadataReader() { try { hasError = false; return provider.GetMetadataReader(); } catch (BadImageFormatException) { hasError = true; return null; } catch (IOException) { hasError = true; return null; } } public string SourceFileName => pdbFileName ?? moduleFileName; public IList GetSequencePoints(MethodDefinitionHandle method) { var metadata = GetMetadataReader(); if (metadata == null) return EmptyList.Instance; var debugInfo = metadata.GetMethodDebugInformation(method); var sequencePoints = new List(); try { foreach (var point in debugInfo.GetSequencePoints()) { string documentFileName; if (!point.Document.IsNil) { var document = metadata.GetDocument(point.Document); documentFileName = metadata.GetString(document.Name); } else { documentFileName = ""; } sequencePoints.Add(new Decompiler.DebugInfo.SequencePoint() { Offset = point.Offset, StartLine = point.StartLine, StartColumn = point.StartColumn, EndLine = point.EndLine, EndColumn = point.EndColumn, DocumentUrl = documentFileName }); } return sequencePoints; } catch (BadImageFormatException) { return EmptyList.Instance; } } public IList GetVariables(MethodDefinitionHandle method) { var metadata = GetMetadataReader(); var variables = new List(); if (metadata == null) return variables; foreach (var h in metadata.GetLocalScopes(method)) { var scope = metadata.GetLocalScope(h); foreach (var v in scope.GetLocalVariables()) { var var = metadata.GetLocalVariable(v); variables.Add(new Variable(var.Index, metadata.GetString(var.Name))); } } return variables; } public bool TryGetName(MethodDefinitionHandle method, int index, [NotNullWhen(true)] out string? name) { var metadata = GetMetadataReader(); name = null; if (metadata == null) return false; foreach (var h in metadata.GetLocalScopes(method)) { var scope = metadata.GetLocalScope(h); foreach (var v in scope.GetLocalVariables()) { var var = metadata.GetLocalVariable(v); if (var.Index == index) { name = metadata.GetString(var.Name); return true; } } } return false; } public bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo) { var metadata = GetMetadataReader(); extraTypeInfo = default; if (metadata == null) return false; LocalVariableHandle localVariableHandle = default; foreach (var h in metadata.GetLocalScopes(method)) { var scope = metadata.GetLocalScope(h); foreach (var v in scope.GetLocalVariables()) { var var = metadata.GetLocalVariable(v); if (var.Index == index) { localVariableHandle = v; break; } } if (!localVariableHandle.IsNil) break; } foreach (var h in metadata.CustomDebugInformation) { var cdi = metadata.GetCustomDebugInformation(h); if (cdi.Parent.IsNil || cdi.Parent.Kind != HandleKind.LocalVariable) continue; if (localVariableHandle != (LocalVariableHandle)cdi.Parent) continue; if (cdi.Value.IsNil || cdi.Kind.IsNil) continue; var kind = metadata.GetGuid(cdi.Kind); if (kind == KnownGuids.TupleElementNames && extraTypeInfo.TupleElementNames is null) { var reader = metadata.GetBlobReader(cdi.Value); var list = new List(); while (reader.RemainingBytes > 0) { // Read a UTF8 null-terminated string int length = reader.IndexOf(0); string s = reader.ReadUTF8(length); // Skip null terminator reader.ReadByte(); list.Add(string.IsNullOrWhiteSpace(s) ? null : s); } extraTypeInfo.TupleElementNames = list.ToArray(); } else if (kind == KnownGuids.DynamicLocalVariables && extraTypeInfo.DynamicFlags is null) { var reader = metadata.GetBlobReader(cdi.Value); extraTypeInfo.DynamicFlags = new bool[reader.Length * 8]; int j = 0; while (reader.RemainingBytes > 0) { int b = reader.ReadByte(); for (int i = 1; i < 0x100; i <<= 1) extraTypeInfo.DynamicFlags[j++] = (b & i) != 0; } } if (extraTypeInfo.TupleElementNames != null && extraTypeInfo.DynamicFlags != null) break; } return extraTypeInfo.TupleElementNames != null || extraTypeInfo.DynamicFlags != null; } public MetadataFile ToMetadataFile() { var kind = IsEmbedded || Path.GetExtension(SourceFileName).Equals(".pdb", StringComparison.OrdinalIgnoreCase) ? MetadataFileKind.ProgramDebugDatabase : MetadataFileKind.Metadata; return new MetadataFile(kind, SourceFileName, provider, options, 0, IsEmbedded); } } } ================================================ FILE: ICSharpCode.ILSpyX/Properties/AssemblyInfo.cs ================================================ #region Using directives using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; #endregion // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] [assembly: AssemblyVersion(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision)] [assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithCommitHash)] [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] ================================================ FILE: ICSharpCode.ILSpyX/Search/AbstractEntitySearchStrategy.cs ================================================ // 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 // 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.Concurrent; using System.IO; namespace ICSharpCode.ILSpyX.Search { using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Abstractions; public abstract class AbstractEntitySearchStrategy : AbstractSearchStrategy { protected readonly ILanguage language; protected readonly ApiVisibility apiVisibility; protected AbstractEntitySearchStrategy(ILanguage language, ApiVisibility apiVisibility, SearchRequest searchRequest, IProducerConsumerCollection resultQueue) : base(searchRequest, resultQueue) { this.language = language; this.apiVisibility = apiVisibility; } protected bool CheckVisibility(IEntity? entity) { if (apiVisibility == ApiVisibility.All) return true; while (entity != null) { if (apiVisibility == ApiVisibility.PublicOnly) { if (!(entity.Accessibility == Accessibility.Public || entity.Accessibility == Accessibility.Protected || entity.Accessibility == Accessibility.ProtectedOrInternal)) return false; } else if (apiVisibility == ApiVisibility.PublicAndInternal) { if (!language.ShowMember(entity)) return false; } entity = entity.DeclaringTypeDefinition; } return true; } protected bool IsInNamespaceOrAssembly(IEntity entity) { if (searchRequest.InAssembly != null) { if (entity.ParentModule?.MetadataFile == null || !(Path.GetFileName(entity.ParentModule.MetadataFile.FileName).Contains(searchRequest.InAssembly, StringComparison.OrdinalIgnoreCase) || entity.ParentModule.FullAssemblyName.Contains(searchRequest.InAssembly, StringComparison.OrdinalIgnoreCase))) { return false; } } if (searchRequest.InNamespace != null) { if (searchRequest.InNamespace.Length == 0) { return entity.Namespace.Length == 0; } else if (!entity.Namespace.Contains(searchRequest.InNamespace, StringComparison.OrdinalIgnoreCase)) { return false; } } return true; } protected void OnFoundResult(IEntity entity) { OnFoundResult(searchRequest.SearchResultFactory.Create(entity)); } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/AbstractSearchStrategy.cs ================================================ // 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 // 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.Concurrent; using System.Text.RegularExpressions; using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Search { public enum SearchMode { TypeAndMember, Type, Member, Method, Field, Property, Event, Literal, Token, Resource, Assembly, Namespace } public struct SearchRequest { public DecompilerSettings DecompilerSettings; public ITreeNodeFactory TreeNodeFactory; public ISearchResultFactory SearchResultFactory; public SearchMode Mode; public AssemblySearchKind AssemblySearchKind; public MemberSearchKind MemberSearchKind; public string[] Keywords; public Regex? RegEx; public bool FullNameSearch; public bool OmitGenerics; public string InNamespace; public string InAssembly; } public abstract class AbstractSearchStrategy { protected readonly string[] searchTerm; protected readonly Regex? regex; protected readonly bool fullNameSearch; protected readonly bool omitGenerics; protected readonly SearchRequest searchRequest; private readonly IProducerConsumerCollection resultQueue; protected AbstractSearchStrategy(SearchRequest request, IProducerConsumerCollection resultQueue) { this.resultQueue = resultQueue; this.searchTerm = request.Keywords; this.regex = request.RegEx; this.searchRequest = request; this.fullNameSearch = request.FullNameSearch; this.omitGenerics = request.OmitGenerics; } public abstract void Search(MetadataFile module, CancellationToken cancellationToken); protected virtual bool IsMatch(string name) { if (regex != null) { return regex.IsMatch(name); } for (int i = 0; i < searchTerm.Length; ++i) { // How to handle overlapping matches? var term = searchTerm[i]; if (string.IsNullOrEmpty(term)) continue; string text = name; switch (term[0]) { case '+': // must contain term = term.Substring(1); goto default; case '-': // should not contain if (term.Length > 1 && text.IndexOf(term.Substring(1), StringComparison.OrdinalIgnoreCase) >= 0) return false; break; case '=': // exact match { var equalCompareLength = text.IndexOf('`'); if (equalCompareLength == -1) equalCompareLength = text.Length; if (term.Length > 1 && String.Compare(term, 1, text, 0, Math.Max(term.Length, equalCompareLength), StringComparison.OrdinalIgnoreCase) != 0) return false; } break; case '~': if (term.Length > 1 && !IsNoncontiguousMatch(text.ToLower(), term.Substring(1).ToLower())) return false; break; default: if (text.IndexOf(term, StringComparison.OrdinalIgnoreCase) < 0) return false; break; } } return true; } bool IsNoncontiguousMatch(string text, string searchTerm) { if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) { return false; } var textLength = text.Length; if (searchTerm.Length > textLength) { return false; } var i = 0; for (int searchIndex = 0; searchIndex < searchTerm.Length;) { while (i != textLength) { if (text[i] == searchTerm[searchIndex]) { // Check if all characters in searchTerm have been matched if (searchTerm.Length == ++searchIndex) return true; i++; break; } i++; } if (i == textLength) return false; } return false; } protected void OnFoundResult(SearchResult result) { resultQueue.TryAdd(result); } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/AssemblySearchStrategy.cs ================================================ // 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 // 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.Collections.Concurrent; using System.IO; using System.Threading; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpyX.Search { public class AssemblySearchStrategy : AbstractSearchStrategy { readonly AssemblySearchKind searchKind; public AssemblySearchStrategy(SearchRequest request, IProducerConsumerCollection resultQueue, AssemblySearchKind searchKind) : base(request, resultQueue) { this.searchKind = searchKind; } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (searchKind == AssemblySearchKind.NameOrFileName) { string? localName = GetNameToMatch(module, AssemblySearchKind.Name); string? filePath = GetNameToMatch(module, AssemblySearchKind.FilePath); if (localName != null && IsMatch(localName)) { OnFoundResult(module); } else if (filePath != null) { string fileName = Path.GetFileName(filePath); if (IsMatch(fileName)) OnFoundResult(module); } return; } string? name = GetNameToMatch(module, searchKind); if (name != null && IsMatch(name)) OnFoundResult(module); } string? GetNameToMatch(MetadataFile module, AssemblySearchKind kind) { switch (kind) { case AssemblySearchKind.FullName: return module.FullName; case AssemblySearchKind.Name: return module.Name; case AssemblySearchKind.FilePath: return module.FileName; } if (!module.IsAssembly) return null; var metadata = module.Metadata; var definition = module.Metadata.GetAssemblyDefinition(); switch (kind) { case AssemblySearchKind.Culture: if (definition.Culture.IsNil) return "neutral"; return metadata.GetString(definition.Culture); case AssemblySearchKind.Version: return definition.Version.ToString(); case AssemblySearchKind.PublicKey: return module.Metadata.GetPublicKeyToken(); case AssemblySearchKind.HashAlgorithm: return definition.HashAlgorithm.ToString(); case AssemblySearchKind.Flags: return definition.Flags.ToString(); } return null; } void OnFoundResult(MetadataFile module) { OnFoundResult(searchRequest.SearchResultFactory.Create(module)); } } public enum AssemblySearchKind { NameOrFileName, Name, FullName, FilePath, Culture, Version, PublicKey, HashAlgorithm, Flags } } ================================================ FILE: ICSharpCode.ILSpyX/Search/CSharpLexer.cs ================================================ // Copyright (c) 2014 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 // 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. #nullable disable using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; namespace ICSharpCode.ILSpyX.Search { class LATextReader : TextReader { List buffer; TextReader reader; public LATextReader(TextReader reader) { this.buffer = new List(); this.reader = reader; } public override int Peek() { return Peek(0); } public override int Read() { int c = Peek(); buffer.RemoveAt(0); return c; } public int Peek(int step) { while (step >= buffer.Count) { buffer.Add(reader.Read()); } if (step < 0) return -1; return buffer[step]; } protected override void Dispose(bool disposing) { if (disposing) reader.Dispose(); base.Dispose(disposing); } } enum TokenKind : byte { EOF, Literal, Identifier, Symbol, } enum LiteralFormat : byte { None, DecimalNumber, HexadecimalNumber, OctalNumber, StringLiteral, VerbatimStringLiteral, CharLiteral } class Literal { internal readonly TokenKind tokenKind; internal readonly LiteralFormat literalFormat; internal readonly object literalValue; internal readonly string val; internal Literal next; public TokenKind TokenKind { get { return tokenKind; } } public LiteralFormat LiteralFormat { get { return literalFormat; } } public object LiteralValue { get { return literalValue; } } public string Value { get { return val; } } public Literal(string val, TokenKind tokenKind) { this.val = val; this.tokenKind = tokenKind; } public Literal(string val, object literalValue, LiteralFormat literalFormat) { this.val = val; this.literalValue = literalValue; this.literalFormat = literalFormat; this.tokenKind = TokenKind.Literal; } } internal abstract class AbstractLexer : IDisposable { LATextReader reader; int col = 1; int line = 1; protected Literal lastToken = null; protected Literal curToken = null; protected Literal peekToken = null; protected StringBuilder sb = new StringBuilder(); // used for the original value of strings (with escape sequences). protected StringBuilder originalValue = new StringBuilder(); protected int Line { get { return line; } } protected int Col { get { return col; } } protected bool recordRead = false; protected StringBuilder recordedText = new StringBuilder(); protected int ReaderRead() { int val = reader.Read(); if (recordRead && val >= 0) recordedText.Append((char)val); if ((val == '\r' && reader.Peek() != '\n') || val == '\n') { ++line; col = 1; LineBreak(); } else if (val >= 0) { col++; } return val; } protected int ReaderPeek() { return reader.Peek(); } protected int ReaderPeek(int step) { return reader.Peek(step); } protected void ReaderSkip(int steps) { for (int i = 0; i < steps; i++) { ReaderRead(); } } protected string ReaderPeekString(int length) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < length; i++) { int peek = ReaderPeek(i); if (peek != -1) builder.Append((char)peek); } return builder.ToString(); } /// /// The current Token. /// public Literal Token { get { return lastToken; } } /// /// The next Token (The after call) . /// public Literal LookAhead { get { return curToken; } } /// /// Constructor for the abstract lexer class. /// protected AbstractLexer(TextReader reader) { this.reader = new LATextReader(reader); } #region System.IDisposable interface implementation public virtual void Dispose() { reader.Close(); reader = null; lastToken = curToken = peekToken = null; sb = originalValue = null; } #endregion /// /// Must be called before a peek operation. /// public void StartPeek() { peekToken = curToken; } /// /// Gives back the next token. A second call to Peek() gives the next token after the last call for Peek() and so on. /// /// An object. public Literal Peek() { // Debug.WriteLine("Call to Peek"); if (peekToken.next == null) { peekToken.next = Next(); } peekToken = peekToken.next; return peekToken; } /// /// Reads the next token and gives it back. /// /// An object. public virtual Literal NextToken() { if (curToken == null) { curToken = Next(); // Debug.WriteLine(ICSharpCode.NRefactory.Parser.CSharp.Tokens.GetTokenString(curToken.kind) + " -- " + curToken.val + "(" + curToken.kind + ")"); return curToken; } lastToken = curToken; if (curToken.next == null) { curToken.next = Next(); } curToken = curToken.next; // Debug.WriteLine(ICSharpCode.NRefactory.Parser.CSharp.Tokens.GetTokenString(curToken.kind) + " -- " + curToken.val + "(" + curToken.kind + ")"); return curToken; } protected abstract Literal Next(); protected static bool IsIdentifierPart(int ch) { if (ch == 95) return true; // 95 = '_' if (ch == -1) return false; return char.IsLetterOrDigit((char)ch); // accept unicode letters } protected static bool IsHex(char digit) { return Char.IsDigit(digit) || ('A' <= digit && digit <= 'F') || ('a' <= digit && digit <= 'f'); } protected int GetHexNumber(char digit) { if (Char.IsDigit(digit)) { return digit - '0'; } if ('A' <= digit && digit <= 'F') { return digit - 'A' + 0xA; } if ('a' <= digit && digit <= 'f') { return digit - 'a' + 0xA; } return 0; } protected void LineBreak() { } protected bool HandleLineEnd(char ch) { // Handle MS-DOS or MacOS line ends. if (ch == '\r') { if (reader.Peek() == '\n') { // MS-DOS line end '\r\n' ReaderRead(); // LineBreak (); called by ReaderRead (); return true; } else { // assume MacOS line end which is '\r' LineBreak(); return true; } } if (ch == '\n') { LineBreak(); return true; } return false; } protected void SkipToEndOfLine() { int nextChar; while ((nextChar = reader.Read()) != -1) { if (nextChar == '\r') { if (reader.Peek() == '\n') reader.Read(); nextChar = '\n'; } if (nextChar == '\n') { ++line; col = 1; break; } } } protected string ReadToEndOfLine() { sb.Length = 0; int nextChar; while ((nextChar = reader.Read()) != -1) { char ch = (char)nextChar; if (nextChar == '\r') { if (reader.Peek() == '\n') reader.Read(); nextChar = '\n'; } // Return read string, if EOL is reached if (nextChar == '\n') { ++line; col = 1; return sb.ToString(); } sb.Append(ch); } // Got EOF before EOL string retStr = sb.ToString(); col += retStr.Length; return retStr; } } internal sealed class Lexer : AbstractLexer { public Lexer(TextReader reader) : base(reader) { } protected override Literal Next() { char ch; while (true) { int nextChar = ReaderRead(); if (nextChar == -1) break; Literal token = null; switch (nextChar) { case ' ': case '\t': continue; case '\r': case '\n': HandleLineEnd((char)nextChar); continue; case '"': token = ReadString(); break; case '\'': token = ReadChar(); break; case '@': int next = ReaderRead(); if (next == -1) { Error(Line, Col, String.Format("EOF after @")); continue; } else { int x = Col - 1; int y = Line; ch = (char)next; if (ch == '"') { token = ReadVerbatimString(); } else if (Char.IsLetterOrDigit(ch) || ch == '_') { string s = ReadIdent(ch, out _); return new Literal(s, TokenKind.Identifier); } else { HandleLineEnd(ch); Error(y, x, String.Format("Unexpected char in Lexer.Next() : {0}", ch)); continue; } } break; default: // non-ws chars are handled here ch = (char)nextChar; if (Char.IsLetter(ch) || ch == '_' || ch == '\\') { int x = Col - 1; // Col was incremented above, but we want the start of the identifier int y = Line; string s = ReadIdent(ch, out _); return new Literal(s, TokenKind.Identifier); } else if (Char.IsDigit(ch)) { token = ReadDigit(ch, Col - 1); } break; } // try error recovery (token = null -> continue with next char) if (token != null) { return token; } } return new Literal(null, TokenKind.EOF); } // The C# compiler has a fixed size length therefore we'll use a fixed size char array for identifiers // it's also faster than using a string builder. const int MAX_IDENTIFIER_LENGTH = 512; char[] identBuffer = new char[MAX_IDENTIFIER_LENGTH]; string ReadIdent(char ch, out bool canBeKeyword) { int peek; int curPos = 0; canBeKeyword = true; while (true) { if (ch == '\\') { peek = ReaderPeek(); if (peek != 'u' && peek != 'U') { Error(Line, Col, "Identifiers can only contain unicode escape sequences"); } canBeKeyword = false; string surrogatePair; ReadEscapeSequence(out ch, out surrogatePair); if (surrogatePair != null) { if (!char.IsLetterOrDigit(surrogatePair, 0)) { Error(Line, Col, "Unicode escape sequences in identifiers cannot be used to represent characters that are invalid in identifiers"); } for (int i = 0; i < surrogatePair.Length - 1; i++) { if (curPos < MAX_IDENTIFIER_LENGTH) { identBuffer[curPos++] = surrogatePair[i]; } } ch = surrogatePair[surrogatePair.Length - 1]; } else { if (!IsIdentifierPart(ch)) { Error(Line, Col, "Unicode escape sequences in identifiers cannot be used to represent characters that are invalid in identifiers"); } } } if (curPos < MAX_IDENTIFIER_LENGTH) { if (ch != '\0') // only add character, if it is valid // prevents \ from being added identBuffer[curPos++] = ch; } else { Error(Line, Col, String.Format("Identifier too long")); while (IsIdentifierPart(ReaderPeek())) { ReaderRead(); } break; } peek = ReaderPeek(); if (IsIdentifierPart(peek) || peek == '\\') { ch = (char)ReaderRead(); } else { break; } } return new String(identBuffer, 0, curPos); } Literal ReadDigit(char ch, int x) { unchecked { // prevent exception when ReaderPeek() = -1 is cast to char int y = Line; sb.Length = 0; sb.Append(ch); string prefix = null; string suffix = null; bool ishex = false; bool isunsigned = false; bool islong = false; bool isfloat = false; bool isdouble = false; bool isdecimal = false; char peek = (char)ReaderPeek(); if (ch == '.') { isdouble = true; while (Char.IsDigit((char)ReaderPeek())) { // read decimal digits beyond the dot sb.Append((char)ReaderRead()); } peek = (char)ReaderPeek(); } else if (ch == '0' && (peek == 'x' || peek == 'X')) { ReaderRead(); // skip 'x' sb.Length = 0; // Remove '0' from 0x prefix from the stringvalue while (IsHex((char)ReaderPeek())) { sb.Append((char)ReaderRead()); } if (sb.Length == 0) { sb.Append('0'); // dummy value to prevent exception Error(y, x, "Invalid hexadecimal integer literal"); } ishex = true; prefix = "0x"; peek = (char)ReaderPeek(); } else { while (Char.IsDigit((char)ReaderPeek())) { sb.Append((char)ReaderRead()); } peek = (char)ReaderPeek(); } Literal nextToken = null; // if we accidently read a 'dot' if (peek == '.') { // read floating point number ReaderRead(); peek = (char)ReaderPeek(); if (!Char.IsDigit(peek)) { nextToken = new Literal(".", TokenKind.Symbol); peek = '.'; } else { isdouble = true; // double is default if (ishex) { Error(y, x, "No hexadecimal floating point values allowed"); } sb.Append('.'); while (Char.IsDigit((char)ReaderPeek())) { // read decimal digits beyond the dot sb.Append((char)ReaderRead()); } peek = (char)ReaderPeek(); } } if (peek == 'e' || peek == 'E') { // read exponent isdouble = true; sb.Append((char)ReaderRead()); peek = (char)ReaderPeek(); if (peek == '-' || peek == '+') { sb.Append((char)ReaderRead()); } while (Char.IsDigit((char)ReaderPeek())) { // read exponent value sb.Append((char)ReaderRead()); } isunsigned = true; peek = (char)ReaderPeek(); } if (peek == 'f' || peek == 'F') { // float value ReaderRead(); suffix = "f"; isfloat = true; } else if (peek == 'd' || peek == 'D') { // double type suffix (obsolete, double is default) ReaderRead(); suffix = "d"; isdouble = true; } else if (peek == 'm' || peek == 'M') { // decimal value ReaderRead(); suffix = "m"; isdecimal = true; } else if (!isdouble) { if (peek == 'u' || peek == 'U') { ReaderRead(); suffix = "u"; isunsigned = true; peek = (char)ReaderPeek(); } if (peek == 'l' || peek == 'L') { ReaderRead(); peek = (char)ReaderPeek(); islong = true; if (!isunsigned && (peek == 'u' || peek == 'U')) { ReaderRead(); suffix = "Lu"; isunsigned = true; } else { suffix = isunsigned ? "uL" : "L"; } } } string digit = sb.ToString(); string stringValue = prefix + digit + suffix; if (isfloat) { float num; if (float.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { return new Literal(stringValue, num, LiteralFormat.DecimalNumber); } else { Error(y, x, String.Format("Can't parse float {0}", digit)); return new Literal(stringValue, 0f, LiteralFormat.DecimalNumber); } } if (isdecimal) { decimal num; if (decimal.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { return new Literal(stringValue, num, LiteralFormat.DecimalNumber); } else { Error(y, x, String.Format("Can't parse decimal {0}", digit)); return new Literal(stringValue, 0m, LiteralFormat.DecimalNumber); } } if (isdouble) { double num; if (double.TryParse(digit, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) { return new Literal(stringValue, num, LiteralFormat.DecimalNumber); } else { Error(y, x, String.Format("Can't parse double {0}", digit)); return new Literal(stringValue, 0d, LiteralFormat.DecimalNumber); } } // Try to determine a parsable value using ranges. ulong result; if (ishex) { if (!ulong.TryParse(digit, NumberStyles.HexNumber, null, out result)) { Error(y, x, String.Format("Can't parse hexadecimal constant {0}", digit)); return new Literal(stringValue.ToString(), 0, LiteralFormat.HexadecimalNumber); } } else { if (!ulong.TryParse(digit, NumberStyles.Integer, null, out result)) { Error(y, x, String.Format("Can't parse integral constant {0}", digit)); return new Literal(stringValue.ToString(), 0, LiteralFormat.DecimalNumber); } } if (result > long.MaxValue) { islong = true; isunsigned = true; } else if (result > uint.MaxValue) { islong = true; } else if (islong == false && result > int.MaxValue) { isunsigned = true; } Literal token; LiteralFormat literalFormat = ishex ? LiteralFormat.HexadecimalNumber : LiteralFormat.DecimalNumber; if (islong) { if (isunsigned) { ulong num; if (ulong.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { token = new Literal(stringValue, num, literalFormat); } else { Error(y, x, String.Format("Can't parse unsigned long {0}", digit)); token = new Literal(stringValue, 0UL, literalFormat); } } else { long num; if (long.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { token = new Literal(stringValue, num, literalFormat); } else { Error(y, x, String.Format("Can't parse long {0}", digit)); token = new Literal(stringValue, 0L, literalFormat); } } } else { if (isunsigned) { uint num; if (uint.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { token = new Literal(stringValue, num, literalFormat); } else { Error(y, x, String.Format("Can't parse unsigned int {0}", digit)); token = new Literal(stringValue, (uint)0, literalFormat); } } else { int num; if (int.TryParse(digit, ishex ? NumberStyles.HexNumber : NumberStyles.Number, CultureInfo.InvariantCulture, out num)) { token = new Literal(stringValue, num, literalFormat); } else { Error(y, x, String.Format("Can't parse int {0}", digit)); token = new Literal(stringValue, 0, literalFormat); } } } token.next = nextToken; return token; } } Literal ReadString() { int x = Col - 1; int y = Line; sb.Length = 0; originalValue.Length = 0; originalValue.Append('"'); bool doneNormally = false; int nextChar; while ((nextChar = ReaderRead()) != -1) { char ch = (char)nextChar; if (ch == '"') { doneNormally = true; originalValue.Append('"'); break; } if (ch == '\\') { originalValue.Append('\\'); string surrogatePair; originalValue.Append(ReadEscapeSequence(out ch, out surrogatePair)); if (surrogatePair != null) { sb.Append(surrogatePair); } else { sb.Append(ch); } } else if (HandleLineEnd(ch)) { // call HandleLineEnd to ensure line numbers are still correct after the error Error(y, x, "No new line is allowed inside a string literal"); break; } else { originalValue.Append(ch); sb.Append(ch); } } if (!doneNormally) { Error(y, x, "End of file reached inside string literal"); } return new Literal(originalValue.ToString(), sb.ToString(), LiteralFormat.StringLiteral); } Literal ReadVerbatimString() { sb.Length = 0; originalValue.Length = 0; originalValue.Append("@\""); int nextChar; while ((nextChar = ReaderRead()) != -1) { char ch = (char)nextChar; if (ch == '"') { if (ReaderPeek() != '"') { originalValue.Append('"'); break; } originalValue.Append("\"\""); sb.Append('"'); ReaderRead(); } else if (HandleLineEnd(ch)) { sb.Append("\r\n"); originalValue.Append("\r\n"); } else { sb.Append(ch); originalValue.Append(ch); } } if (nextChar == -1) { Error(Line, Col, "End of file reached inside verbatim string literal"); } return new Literal(originalValue.ToString(), sb.ToString(), LiteralFormat.VerbatimStringLiteral); } readonly char[] escapeSequenceBuffer = new char[12]; /// /// reads an escape sequence /// /// The character represented by the escape sequence, /// or '\0' if there was an error or the escape sequence represents a character that /// can be represented only be a suggorate pair /// Null, except when the character represented /// by the escape sequence can only be represented by a surrogate pair (then the string /// contains the surrogate pair) /// The escape sequence string ReadEscapeSequence(out char ch, out string surrogatePair) { surrogatePair = null; int nextChar = ReaderRead(); if (nextChar == -1) { Error(Line, Col, "End of file reached inside escape sequence"); ch = '\0'; return String.Empty; } int number; char c = (char)nextChar; int curPos = 1; escapeSequenceBuffer[0] = c; switch (c) { case '\'': ch = '\''; break; case '\"': ch = '\"'; break; case '\\': ch = '\\'; break; case '0': ch = '\0'; break; case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'v': ch = '\v'; break; case 'u': case 'x': // 16 bit unicode character c = (char)ReaderRead(); number = GetHexNumber(c); escapeSequenceBuffer[curPos++] = c; if (number < 0) { Error(Line, Col - 1, String.Format("Invalid char in literal : {0}", c)); } for (int i = 0; i < 3; ++i) { if (IsHex((char)ReaderPeek())) { c = (char)ReaderRead(); int idx = GetHexNumber(c); escapeSequenceBuffer[curPos++] = c; number = 16 * number + idx; } else { break; } } ch = (char)number; break; case 'U': // 32 bit unicode character number = 0; for (int i = 0; i < 8; ++i) { if (IsHex((char)ReaderPeek())) { c = (char)ReaderRead(); int idx = GetHexNumber(c); escapeSequenceBuffer[curPos++] = c; number = 16 * number + idx; } else { Error(Line, Col - 1, String.Format("Invalid char in literal : {0}", (char)ReaderPeek())); break; } } if (number > 0xffff) { ch = '\0'; surrogatePair = char.ConvertFromUtf32(number); } else { ch = (char)number; } break; default: Error(Line, Col, String.Format("Unexpected escape sequence : {0}", c)); ch = '\0'; break; } return new String(escapeSequenceBuffer, 0, curPos); } Literal ReadChar() { int x = Col - 1; int y = Line; int nextChar = ReaderRead(); if (nextChar == -1 || HandleLineEnd((char)nextChar)) { return null; } char ch = (char)nextChar; char chValue = ch; string escapeSequence = String.Empty; if (ch == '\\') { string surrogatePair; escapeSequence = ReadEscapeSequence(out chValue, out surrogatePair); if (surrogatePair != null) { Error(y, x, "The unicode character must be represented by a surrogate pair and does not fit into a System.Char"); } } unchecked { if ((char)ReaderRead() != '\'') { Error(y, x, "Char not terminated"); } } return new Literal("'" + ch + escapeSequence + "'", chValue, LiteralFormat.CharLiteral); } void Error(int y, int x, string message) { } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/LiteralSearchStrategy.cs ================================================ // 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 // 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.Concurrent; using System.Diagnostics; using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpyX.Abstractions; using ILOpCode = System.Reflection.Metadata.ILOpCode; namespace ICSharpCode.ILSpyX.Search { public class LiteralSearchStrategy : AbstractEntitySearchStrategy { readonly TypeCode searchTermLiteralType = TypeCode.Empty; readonly object? searchTermLiteralValue; public LiteralSearchStrategy(ILanguage language, ApiVisibility apiVisibility, SearchRequest request, IProducerConsumerCollection resultQueue) : base(language, apiVisibility, request, resultQueue) { var terms = request.Keywords; if (terms.Length == 1) { var lexer = new Lexer(new LATextReader(new System.IO.StringReader(terms[0]))); var value = lexer.NextToken(); var following = lexer.NextToken(); if (value != null && value.LiteralValue != null && following != null && following.TokenKind == TokenKind.EOF) { TypeCode valueType = Type.GetTypeCode(value.LiteralValue.GetType()); switch (valueType) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: searchTermLiteralType = TypeCode.Int64; searchTermLiteralValue = CSharpPrimitiveCast.Cast(TypeCode.Int64, value.LiteralValue, false); break; case TypeCode.Single: case TypeCode.Double: case TypeCode.String: searchTermLiteralType = valueType; searchTermLiteralValue = value.LiteralValue; break; } } } // Note: if searchTermLiteralType remains TypeCode.Empty, we'll do a substring search via base.IsMatch } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; var typeSystem = module.GetTypeSystemWithDecompilerSettingsOrNull(searchRequest.DecompilerSettings); if (typeSystem == null) return; foreach (var handle in metadata.MethodDefinitions) { cancellationToken.ThrowIfCancellationRequested(); var md = metadata.GetMethodDefinition(handle); if (!md.HasBody() || !MethodIsLiteralMatch(module, md)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); var result = method.AccessorOwner ?? method; if (!CheckVisibility(result) || !IsInNamespaceOrAssembly(result)) continue; OnFoundResult(result); } foreach (var handle in metadata.FieldDefinitions) { cancellationToken.ThrowIfCancellationRequested(); var fd = metadata.GetFieldDefinition(handle); if (!fd.HasFlag(System.Reflection.FieldAttributes.Literal)) continue; var constantHandle = fd.GetDefaultValue(); if (constantHandle.IsNil) continue; var constant = metadata.GetConstant(constantHandle); var blob = metadata.GetBlobReader(constant.Value); if (!IsLiteralMatch(metadata, blob.ReadConstant(constant.TypeCode))) continue; IField field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(field) || !IsInNamespaceOrAssembly(field)) continue; OnFoundResult(field); } } bool IsLiteralMatch(MetadataReader metadata, object? val) { if (val == null) return false; switch (searchTermLiteralType) { case TypeCode.Int64: TypeCode tc = Type.GetTypeCode(val.GetType()); if (tc >= TypeCode.SByte && tc <= TypeCode.UInt64) return CSharpPrimitiveCast.Cast(TypeCode.Int64, val, false).Equals(searchTermLiteralValue); else return false; case TypeCode.Single: case TypeCode.Double: case TypeCode.String: Debug.Assert(searchTermLiteralValue != null); return searchTermLiteralValue.Equals(val); default: // substring search with searchTerm string? valAsString = val.ToString(); return valAsString != null && IsMatch(valAsString); } } bool MethodIsLiteralMatch(MetadataFile module, MethodDefinition methodDefinition) { var blob = module.GetMethodBody(methodDefinition.RelativeVirtualAddress).GetILReader(); if (searchTermLiteralType == TypeCode.Int64) { Debug.Assert(searchTermLiteralValue != null); long val = (long)searchTermLiteralValue; while (blob.RemainingBytes > 0) { ILOpCode code; switch (code = ILParser.DecodeOpCode(ref blob)) { case ILOpCode.Ldc_i8: if (val == blob.ReadInt64()) return true; break; case ILOpCode.Ldc_i4: if (val == blob.ReadInt32()) return true; break; case ILOpCode.Ldc_i4_s: if (val == blob.ReadSByte()) return true; break; case ILOpCode.Ldc_i4_m1: if (val == -1) return true; break; case ILOpCode.Ldc_i4_0: if (val == 0) return true; break; case ILOpCode.Ldc_i4_1: if (val == 1) return true; break; case ILOpCode.Ldc_i4_2: if (val == 2) return true; break; case ILOpCode.Ldc_i4_3: if (val == 3) return true; break; case ILOpCode.Ldc_i4_4: if (val == 4) return true; break; case ILOpCode.Ldc_i4_5: if (val == 5) return true; break; case ILOpCode.Ldc_i4_6: if (val == 6) return true; break; case ILOpCode.Ldc_i4_7: if (val == 7) return true; break; case ILOpCode.Ldc_i4_8: if (val == 8) return true; break; default: ILParser.SkipOperand(ref blob, code); break; } } } else if (searchTermLiteralType != TypeCode.Empty) { Debug.Assert(searchTermLiteralValue != null); ILOpCode expectedCode; switch (searchTermLiteralType) { case TypeCode.Single: expectedCode = ILOpCode.Ldc_r4; break; case TypeCode.Double: expectedCode = ILOpCode.Ldc_r8; break; case TypeCode.String: expectedCode = ILOpCode.Ldstr; break; default: throw new InvalidOperationException(); } while (blob.RemainingBytes > 0) { var code = ILParser.DecodeOpCode(ref blob); if (code != expectedCode) { ILParser.SkipOperand(ref blob, code); continue; } switch (code) { case ILOpCode.Ldc_r4: if ((float)searchTermLiteralValue == blob.ReadSingle()) return true; break; case ILOpCode.Ldc_r8: if ((double)searchTermLiteralValue == blob.ReadDouble()) return true; break; case ILOpCode.Ldstr: if ((string)searchTermLiteralValue == ILParser.DecodeUserString(ref blob, module.Metadata)) return true; break; } } } else { while (blob.RemainingBytes > 0) { var code = ILParser.DecodeOpCode(ref blob); if (code != ILOpCode.Ldstr) { ILParser.SkipOperand(ref blob, code); continue; } if (IsMatch(ILParser.DecodeUserString(ref blob, module.Metadata))) return true; } } return false; } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/MemberSearchStrategy.cs ================================================ // 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 // 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.Collections.Concurrent; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Search { public class MemberSearchStrategy : AbstractEntitySearchStrategy { readonly MemberSearchKind searchKind; public MemberSearchStrategy(ILanguage language, ApiVisibility apiVisibility, SearchRequest searchRequest, IProducerConsumerCollection resultQueue, MemberSearchKind searchKind = MemberSearchKind.All) : base(language, apiVisibility, searchRequest, resultQueue) { this.searchKind = searchKind; } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var metadata = module.Metadata; var typeSystem = module.GetTypeSystemWithDecompilerSettingsOrNull(searchRequest.DecompilerSettings); if (typeSystem == null) return; if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { foreach (var handle in metadata.TypeDefinitions) { cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(type) || !IsInNamespaceOrAssembly(type)) continue; OnFoundResult(type); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) { foreach (var handle in metadata.MethodDefinitions) { cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(method) || !IsInNamespaceOrAssembly(method)) continue; OnFoundResult(method); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Field) { foreach (var handle in metadata.FieldDefinitions) { cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(field) || !IsInNamespaceOrAssembly(field)) continue; OnFoundResult(field); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Property) { foreach (var handle in metadata.PropertyDefinitions) { cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(property) || !IsInNamespaceOrAssembly(property)) continue; OnFoundResult(property); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Event) { foreach (var handle in metadata.EventDefinitions) { cancellationToken.ThrowIfCancellationRequested(); string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (!IsMatch(languageSpecificName)) continue; var @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); if (!CheckVisibility(@event) || !IsInNamespaceOrAssembly(@event)) continue; OnFoundResult(@event); } } } } public enum MemberSearchKind { All, Type, Member, Field, Property, Event, Method } } ================================================ FILE: ICSharpCode.ILSpyX/Search/MetadataTokenSearchStrategy.cs ================================================ // 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 // 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.Collections.Concurrent; using System.Globalization; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Search { public class MetadataTokenSearchStrategy : AbstractEntitySearchStrategy { readonly EntityHandle searchTermToken; public MetadataTokenSearchStrategy(ILanguage language, ApiVisibility apiVisibility, SearchRequest request, IProducerConsumerCollection resultQueue) : base(language, apiVisibility, request, resultQueue) { var terms = request.Keywords; if (terms.Length == 1) { int.TryParse(terms[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var token); searchTermToken = MetadataTokenHelpers.EntityHandleOrNil(token); } } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (searchTermToken.IsNil) return; var typeSystem = module.GetTypeSystemWithDecompilerSettingsOrNull(searchRequest.DecompilerSettings); if (typeSystem == null) return; var metadataModule = (MetadataModule)typeSystem.MainModule; int row = module.Metadata.GetRowNumber(searchTermToken); switch (searchTermToken.Kind) { case HandleKind.TypeDefinition: if (row < 1 || row > module.Metadata.TypeDefinitions.Count) break; var type = metadataModule.GetDefinition((TypeDefinitionHandle)searchTermToken); if (!CheckVisibility(type) || !IsInNamespaceOrAssembly(type)) break; OnFoundResult(type); break; case HandleKind.MethodDefinition: if (row < 1 || row > module.Metadata.MethodDefinitions.Count) break; var method = metadataModule.GetDefinition((MethodDefinitionHandle)searchTermToken); if (!CheckVisibility(method) || !IsInNamespaceOrAssembly(method)) break; OnFoundResult(method); break; case HandleKind.FieldDefinition: if (row < 1 || row > module.Metadata.FieldDefinitions.Count) break; var field = metadataModule.GetDefinition((FieldDefinitionHandle)searchTermToken); if (!CheckVisibility(field) || !IsInNamespaceOrAssembly(field)) break; OnFoundResult(field); break; case HandleKind.PropertyDefinition: if (row < 1 || row > module.Metadata.PropertyDefinitions.Count) break; var property = metadataModule.GetDefinition((PropertyDefinitionHandle)searchTermToken); if (!CheckVisibility(property) || !IsInNamespaceOrAssembly(property)) break; OnFoundResult(property); break; case HandleKind.EventDefinition: if (row < 1 || row > module.Metadata.EventDefinitions.Count) break; var @event = metadataModule.GetDefinition((EventDefinitionHandle)searchTermToken); if (!CheckVisibility(@event) || !IsInNamespaceOrAssembly(@event)) break; OnFoundResult(@event); break; } } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/NamespaceSearchStrategy.cs ================================================ // 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 // 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.Collections.Concurrent; using System.Linq; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpyX.Search { public class NamespaceSearchStrategy : AbstractSearchStrategy { public NamespaceSearchStrategy(SearchRequest request, IProducerConsumerCollection resultQueue) : base(request, resultQueue) { } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var typeSystem = module.GetTypeSystemWithDecompilerSettingsOrNull(searchRequest.DecompilerSettings); if (typeSystem == null) return; var root = ((MetadataModule)typeSystem.MainModule).RootNamespace; Search(module, root); } private void Search(MetadataFile module, INamespace ns) { if (ns.Types.Any()) { if (IsMatch(ns.FullName.Length == 0 ? "-" : ns.FullName)) OnFoundResult(module, ns); } foreach (var child in ns.ChildNamespaces) Search(module, child); } void OnFoundResult(MetadataFile module, INamespace ns) { OnFoundResult(searchRequest.SearchResultFactory.Create(module, ns)); } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/ResourceSearchStrategy.cs ================================================ // 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 // 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.Collections.Concurrent; using System.Reflection; using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Search { public class ResourceSearchStrategy : AbstractSearchStrategy { protected readonly bool searchInside; protected readonly ApiVisibility apiVisibility; protected readonly ITreeNodeFactory treeNodeFactory; public ResourceSearchStrategy(ApiVisibility apiVisibility, SearchRequest request, IProducerConsumerCollection resultQueue) : base(request, resultQueue) { this.treeNodeFactory = request.TreeNodeFactory; this.apiVisibility = apiVisibility; this.searchInside = true; } protected bool CheckVisibility(Resource resource) { if (apiVisibility == ApiVisibility.All) return true; if (apiVisibility == ApiVisibility.PublicOnly && (resource.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private) return false; return true; } public override void Search(MetadataFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var resourcesNode = treeNodeFactory.CreateResourcesList(module); foreach (Resource resource in module.Resources) Search(module, resource, resourcesNode, treeNodeFactory.Create(resource), cancellationToken); } void Search(MetadataFile module, Resource resource, ITreeNode parent, ITreeNode node, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (node is IResourcesFileTreeNode treeNode) { if (!CheckVisibility(treeNode.Resource)) return; resource = treeNode.Resource; } if (node.Text is string s && IsMatch(s)) OnFoundResult(module, resource, node, parent); if (!searchInside) return; node.EnsureLazyChildren(); foreach (var child in node.Children) Search(module, resource, node, child, cancellationToken); } void OnFoundResult(MetadataFile module, Resource resource, ITreeNode node, ITreeNode parent) { OnFoundResult(searchRequest.SearchResultFactory.Create(module, resource, node, parent)); } } } ================================================ FILE: ICSharpCode.ILSpyX/Search/SearchResult.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpyX.Search { public interface ISearchResultFactory { MemberSearchResult Create(IEntity entity); ResourceSearchResult Create(MetadataFile module, Resource resource, ITreeNode node, ITreeNode parent); AssemblySearchResult Create(MetadataFile module); NamespaceSearchResult Create(MetadataFile module, INamespace @namespace); } public class SearchResult { public static readonly IComparer ComparerByName = new SearchResultNameComparer(); public static readonly IComparer ComparerByFitness = new SearchResultFitnessComparer(); public virtual object? Reference => null; public float Fitness { get; set; } public required string Name { get; set; } public required string Location { get; set; } public required string Assembly { get; set; } public object? ToolTip { get; set; } public required object Image { get; set; } public required object LocationImage { get; set; } public required object AssemblyImage { get; set; } public override string ToString() { return Name; } class SearchResultNameComparer : IComparer { public int Compare(SearchResult? x, SearchResult? y) { return StringComparer.Ordinal.Compare(x?.Name ?? "", y?.Name ?? ""); } } class SearchResultFitnessComparer : IComparer { public int Compare(SearchResult? x, SearchResult? y) { //elements with higher Fitness come first return Comparer.Default.Compare(y?.Fitness ?? 0, x?.Fitness ?? 0); } } } public class MemberSearchResult : SearchResult { #nullable disable public IEntity Member { get; set; } public override object Reference => Member; #nullable enable } public class ResourceSearchResult : SearchResult { #nullable disable public Resource Resource { get; set; } #nullable enable public override object Reference => ValueTuple.Create(Resource, Name); } public class AssemblySearchResult : SearchResult { #nullable disable public MetadataFile Module { get; set; } public override object Reference => Module; #nullable enable } public class NamespaceSearchResult : SearchResult { #nullable disable public INamespace Namespace { get; set; } public override object Reference => Namespace; #nullable enable } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs ================================================ // Copyright (c) 2024 Tom Englert // // 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.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Xml.Linq; namespace ICSharpCode.ILSpyX.Settings { public class DecompilerSettings : Decompiler.DecompilerSettings, ISettingsSection { static readonly PropertyInfo[] properties = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false) .ToArray(); public XName SectionName => "DecompilerSettings"; public XElement SaveToXml() { var section = new XElement(SectionName); foreach (var p in properties) { section.SetAttributeValue(p.Name, p.GetValue(this)); } return section; } public void LoadFromXml(XElement section) { foreach (var p in properties) { var value = (bool?)section.Attribute(p.Name); if (value.HasValue) p.SetValue(this, value.Value); } } public override DecompilerSettings Clone() { return (DecompilerSettings)base.Clone(); } public static bool IsKnownOption(string name, [NotNullWhen(true)] out PropertyInfo? property) { property = null; foreach (var item in properties) { if (item.Name != name) continue; property = item; return true; } return false; } } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/DefaultSettingsFilePathProvider.cs ================================================ // Copyright (c) 2022 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 // 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; namespace ICSharpCode.ILSpyX.Settings { /// /// Used in scenarios where the user passes a path and that is to be used, eg ilspycmd parameter /// public class DefaultSettingsFilePathProvider : ISettingsFilePathProvider { private readonly string _providedPath; public DefaultSettingsFilePathProvider(string providedPath) { _providedPath = providedPath; } public string GetSettingsFilePath() { return _providedPath; } } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/ILSpySettings.cs ================================================ // 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 // 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.IO; using System.Threading; using System.Xml; using System.Xml.Linq; namespace ICSharpCode.ILSpyX.Settings { /// /// Manages IL Spy settings. /// public class ILSpySettings : ISettingsProvider { /// /// This settings file path provider determines where to load settings file from, includes filename /// public static ISettingsFilePathProvider? SettingsFilePathProvider { get; set; } XElement root; ILSpySettings(XElement? root = null) { this.root = root ?? new XElement("ILSpy"); } public XElement this[XName section] { get { return root.Element(section) ?? new XElement(section); } } /// /// Loads the settings file from disk. /// /// /// An instance used to access the loaded settings. /// public static ILSpySettings Load() { using (new MutexProtector(ConfigFileMutex)) { try { return new ILSpySettings(LoadFile(GetConfigFile()).Root); } catch (IOException) { return new ILSpySettings(); } catch (XmlException) { return new ILSpySettings(); } } } static XDocument LoadFile(string fileName) { return XDocument.Load(fileName, LoadOptions.None); } /// /// Saves a setting section. /// public void SaveSettings(XElement section) { Update(rootElement => { XElement? existingElement = rootElement.Element(section.Name); if (existingElement != null) existingElement.ReplaceWith(section); else rootElement.Add(section); }); } /// /// Updates the saved settings. /// We always reload the file on updates to ensure we aren't overwriting unrelated changes performed /// by another ILSpy instance. /// public void Update(Action action) { using (new MutexProtector(ConfigFileMutex)) { string config = GetConfigFile(); XDocument doc; try { doc = LoadFile(config); } catch (IOException) { // ensure the directory exists Directory.CreateDirectory(Path.GetDirectoryName(config)!); doc = new XDocument(new XElement("ILSpy")); } catch (XmlException) { doc = new XDocument(new XElement("ILSpy")); } doc.Root!.SetAttributeValue("version", DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision); action(doc.Root); doc.Save(config, SaveOptions.None); this.root = doc.Root; } } static string GetConfigFile() { if (null != SettingsFilePathProvider) return SettingsFilePathProvider.GetSettingsFilePath(); throw new ArgumentNullException(nameof(SettingsFilePathProvider)); // return "ILSpy.xml"; } const string ConfigFileMutex = "01A91708-49D1-410D-B8EB-4DE2662B3971"; /// /// Helper class for serializing access to the config file when multiple ILSpy instances are running. /// sealed class MutexProtector : IDisposable { readonly Mutex mutex; public MutexProtector(string name) { this.mutex = new Mutex(true, name, out bool createdNew); if (createdNew) return; try { mutex.WaitOne(); } catch (AbandonedMutexException) { } } public void Dispose() { mutex.ReleaseMutex(); mutex.Dispose(); } } } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/ISettingsFilePathProvider.cs ================================================ // Copyright (c) 2022 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 // 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. namespace ICSharpCode.ILSpyX.Settings { public interface ISettingsFilePathProvider { string GetSettingsFilePath(); } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/ISettingsProvider.cs ================================================ // 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 // 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.Xml.Linq; namespace ICSharpCode.ILSpyX.Settings { public interface ISettingsProvider { XElement this[XName section] { get; } void Update(Action action); void SaveSettings(XElement section); } } ================================================ FILE: ICSharpCode.ILSpyX/Settings/SettingsServiceBase.cs ================================================ // Copyright (c) 2024 Tom Englert // // 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.Concurrent; using System.ComponentModel; using System.Xml.Linq; namespace ICSharpCode.ILSpyX.Settings { public interface IChildSettings { ISettingsSection Parent { get; } } public interface ISettingsSection : INotifyPropertyChanged { XName SectionName { get; } void LoadFromXml(XElement section); XElement SaveToXml(); } public class SettingsServiceBase(ISettingsProvider spySettings) { protected readonly ConcurrentDictionary sections = new(); protected ISettingsProvider SpySettings { get; set; } = spySettings; public T GetSettings() where T : ISettingsSection, new() { return (T)sections.GetOrAdd(typeof(T), _ => { T section = new T(); var sectionElement = SpySettings[section.SectionName]; section.LoadFromXml(sectionElement); section.PropertyChanged += Section_PropertyChanged; return section; }); } protected static void SaveSection(ISettingsSection section, XElement root) { var element = section.SaveToXml(); var existingElement = root.Element(section.SectionName); if (existingElement != null) existingElement.ReplaceWith(element); else root.Add(element); } protected virtual void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e) { } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/FlatListTreeNode.cs ================================================ // Copyright (c) 2020 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 // 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; #nullable disable namespace ICSharpCode.ILSpyX.TreeView { // This part of SharpTreeNode controls the 'flat list' data structure, which emulates // a big flat list containing the whole tree; allowing access by visible index. partial class SharpTreeNode { /// The parent in the flat list internal SharpTreeNode listParent; /// Left/right nodes in the flat list SharpTreeNode left, right; internal TreeFlattener treeFlattener; /// Subtree height in the flat list tree byte height = 1; /// Length in the flat list, including children (children within the flat list). -1 = invalidated int totalListLength = -1; int Balance { get { return Height(right) - Height(left); } } static int Height(SharpTreeNode node) { return node != null ? node.height : 0; } internal SharpTreeNode GetListRoot() { SharpTreeNode node = this; while (node.listParent != null) node = node.listParent; return node; } #region Debugging [Conditional("DEBUG")] void CheckRootInvariants() { GetListRoot().CheckInvariants(); } [Conditional("DATACONSISTENCYCHECK")] void CheckInvariants() { Debug.Assert(left == null || left.listParent == this); Debug.Assert(right == null || right.listParent == this); Debug.Assert(height == 1 + Math.Max(Height(left), Height(right))); Debug.Assert(Math.Abs(this.Balance) <= 1); Debug.Assert(totalListLength == -1 || totalListLength == (left != null ? left.totalListLength : 0) + (isVisible ? 1 : 0) + (right != null ? right.totalListLength : 0)); if (left != null) left.CheckInvariants(); if (right != null) right.CheckInvariants(); } [Conditional("DEBUG")] static void DumpTree(SharpTreeNode node) { node.GetListRoot().DumpTree(); } [Conditional("DEBUG")] void DumpTree() { Debug.Indent(); if (left != null) left.DumpTree(); Debug.Unindent(); Debug.WriteLine("{0}, totalListLength={1}, height={2}, Balance={3}, isVisible={4}", ToString(), totalListLength, height, Balance, isVisible); Debug.Indent(); if (right != null) right.DumpTree(); Debug.Unindent(); } #endregion #region GetNodeByVisibleIndex / GetVisibleIndexForNode internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index) { root.GetTotalListLength(); // ensure all list lengths are calculated Debug.Assert(index >= 0); Debug.Assert(index < root.totalListLength); SharpTreeNode node = root; while (true) { if (node.left != null && index < node.left.totalListLength) { node = node.left; } else { if (node.left != null) { index -= node.left.totalListLength; } if (node.isVisible) { if (index == 0) return node; index--; } node = node.right; } } } internal static int GetVisibleIndexForNode(SharpTreeNode node) { int index = node.left != null ? node.left.GetTotalListLength() : 0; while (node.listParent != null) { if (node == node.listParent.right) { if (node.listParent.left != null) index += node.listParent.left.GetTotalListLength(); if (node.listParent.isVisible) index++; } node = node.listParent; } return index; } #endregion #region Balancing /// /// Balances the subtree rooted in and recomputes the 'height' field. /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value. /// /// The new root node static SharpTreeNode Rebalance(SharpTreeNode node) { Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1); Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1); // Keep looping until it's balanced. Not sure if this is stricly required; this is based on // the Rope code where node merging made this necessary. while (Math.Abs(node.Balance) > 1) { // AVL balancing // note: because we don't care about the identity of concat nodes, this works a little different than usual // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged if (node.Balance > 1) { if (node.right.Balance < 0) { node.right = node.right.RotateRight(); } node = node.RotateLeft(); // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that. node.left = Rebalance(node.left); } else if (node.Balance < -1) { if (node.left.Balance > 0) { node.left = node.left.RotateLeft(); } node = node.RotateRight(); // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that. node.right = Rebalance(node.right); } } Debug.Assert(Math.Abs(node.Balance) <= 1); node.height = (byte)(1 + Math.Max(Height(node.left), Height(node.right))); node.totalListLength = -1; // mark for recalculation // since balancing checks the whole tree up to the root, the whole path will get marked as invalid return node; } internal int GetTotalListLength() { if (totalListLength >= 0) return totalListLength; int length = (isVisible ? 1 : 0); if (left != null) { length += left.GetTotalListLength(); } if (right != null) { length += right.GetTotalListLength(); } return totalListLength = length; } SharpTreeNode RotateLeft() { /* Rotate tree to the left * * this right * / \ / \ * A right ===> this C * / \ / \ * B C A B */ SharpTreeNode b = right.left; SharpTreeNode newTop = right; if (b != null) b.listParent = this; this.right = b; newTop.left = this; newTop.listParent = this.listParent; this.listParent = newTop; // rebalance the 'this' node - this is necessary in some bulk insertion cases: newTop.left = Rebalance(this); return newTop; } SharpTreeNode RotateRight() { /* Rotate tree to the right * * this left * / \ / \ * left C ===> A this * / \ / \ * A B B C */ SharpTreeNode b = left.right; SharpTreeNode newTop = left; if (b != null) b.listParent = this; this.left = b; newTop.right = this; newTop.listParent = this.listParent; this.listParent = newTop; newTop.right = Rebalance(this); return newTop; } static void RebalanceUntilRoot(SharpTreeNode pos) { while (pos.listParent != null) { if (pos == pos.listParent.left) { pos = pos.listParent.left = Rebalance(pos); } else { Debug.Assert(pos == pos.listParent.right); pos = pos.listParent.right = Rebalance(pos); } pos = pos.listParent; } SharpTreeNode newRoot = Rebalance(pos); if (newRoot != pos && pos.treeFlattener != null) { Debug.Assert(newRoot.treeFlattener == null); newRoot.treeFlattener = pos.treeFlattener; pos.treeFlattener = null; newRoot.treeFlattener.root = newRoot; } Debug.Assert(newRoot.listParent == null); newRoot.CheckInvariants(); } #endregion #region Insertion static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode) { // newNode might be the model root of a whole subtree, so go to the list root of that subtree: newNode = newNode.GetListRoot(); if (pos.right == null) { pos.right = newNode; newNode.listParent = pos; } else { // insert before pos.right's leftmost: pos = pos.right; while (pos.left != null) pos = pos.left; Debug.Assert(pos.left == null); pos.left = newNode; newNode.listParent = pos; } RebalanceUntilRoot(pos); } #endregion #region Removal void RemoveNodes(SharpTreeNode start, SharpTreeNode end) { // Removes all nodes from start to end (inclusive) // All removed nodes will be reorganized in a separate tree, do not delete // regions that don't belong together in the tree model! List removedSubtrees = new List(); SharpTreeNode oldPos; SharpTreeNode pos = start; do { // recalculate the endAncestors every time, because the tree might have been rebalanced HashSet endAncestors = new HashSet(); for (SharpTreeNode tmp = end; tmp != null; tmp = tmp.listParent) endAncestors.Add(tmp); removedSubtrees.Add(pos); if (!endAncestors.Contains(pos)) { // we can remove pos' right subtree in a single step: if (pos.right != null) { removedSubtrees.Add(pos.right); pos.right.listParent = null; pos.right = null; } } SharpTreeNode succ = pos.Successor(); DeleteNode(pos); // this will also rebalance out the deletion of the right subtree oldPos = pos; pos = succ; } while (oldPos != end); // merge back together the removed subtrees: SharpTreeNode removed = removedSubtrees[0]; for (int i = 1; i < removedSubtrees.Count; i++) { removed = ConcatTrees(removed, removedSubtrees[i]); } } static SharpTreeNode ConcatTrees(SharpTreeNode first, SharpTreeNode second) { SharpTreeNode tmp = first; while (tmp.right != null) tmp = tmp.right; InsertNodeAfter(tmp, second); return tmp.GetListRoot(); } SharpTreeNode Successor() { if (right != null) { SharpTreeNode node = right; while (node.left != null) node = node.left; return node; } else { SharpTreeNode node = this; SharpTreeNode oldNode; do { oldNode = node; node = node.listParent; // loop while we are on the way up from the right part } while (node != null && node.right == oldNode); return node; } } static void DeleteNode(SharpTreeNode node) { SharpTreeNode balancingNode; if (node.left == null) { balancingNode = node.listParent; node.ReplaceWith(node.right); node.right = null; } else if (node.right == null) { balancingNode = node.listParent; node.ReplaceWith(node.left); node.left = null; } else { SharpTreeNode tmp = node.right; while (tmp.left != null) tmp = tmp.left; // First replace tmp with tmp.right balancingNode = tmp.listParent; tmp.ReplaceWith(tmp.right); tmp.right = null; Debug.Assert(tmp.left == null); Debug.Assert(tmp.listParent == null); // Now move node's children to tmp: tmp.left = node.left; node.left = null; tmp.right = node.right; node.right = null; if (tmp.left != null) tmp.left.listParent = tmp; if (tmp.right != null) tmp.right.listParent = tmp; // Then replace node with tmp node.ReplaceWith(tmp); if (balancingNode == node) balancingNode = tmp; } Debug.Assert(node.listParent == null); Debug.Assert(node.left == null); Debug.Assert(node.right == null); node.height = 1; node.totalListLength = -1; if (balancingNode != null) RebalanceUntilRoot(balancingNode); } void ReplaceWith(SharpTreeNode node) { if (listParent != null) { if (listParent.left == this) { listParent.left = node; } else { Debug.Assert(listParent.right == this); listParent.right = node; } if (node != null) node.listParent = listParent; listParent = null; } else { // this was a root node Debug.Assert(node != null); // cannot delete the only node in the tree node.listParent = null; if (treeFlattener != null) { Debug.Assert(node.treeFlattener == null); node.treeFlattener = this.treeFlattener; this.treeFlattener = null; node.treeFlattener.root = node; } } } #endregion } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/IPlatformDataObject.cs ================================================ namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { public interface IPlatformDataObject { bool GetDataPresent(string format); object GetData(string format); void SetData(string format, object data); object UnderlyingDataObject { get; } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/IPlatformDragDrop.cs ================================================ namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { public interface IPlatformDragDrop { XPlatDragDropEffects DoDragDrop(object dragSource, IPlatformDataObject data, XPlatDragDropEffects allowedEffects); } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/IPlatformDragEventArgs.cs ================================================ namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { public interface IPlatformDragEventArgs { XPlatDragDropEffects Effects { get; set; } IPlatformDataObject Data { get; } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/IPlatformRoutedEventArgs.cs ================================================ namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { public interface IPlatformRoutedEventArgs { bool Handled { get; set; } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/ITreeNodeImagesProvider.cs ================================================ namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { public interface ITreeNodeImagesProvider { object Assembly { get; } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/PlatformAbstractions/XPlatDragDropEffects.cs ================================================ using System; namespace ICSharpCode.ILSpyX.TreeView.PlatformAbstractions { // // Summary: // Specifies the effects of a drag-and-drop operation. [Flags] public enum XPlatDragDropEffects { // // Summary: // Scrolling is about to start or is currently occurring in the drop target. Scroll = int.MinValue, // // Summary: // The data is copied, removed from the drag source, and scrolled in the drop target. All = -2147483645, // // Summary: // The drop target does not accept the data. None = 0, // // Summary: // The data is copied to the drop target. Copy = 1, // // Summary: // The data from the drag source is moved to the drop target. Move = 2, // // Summary: // The data from the drag source is linked to the drop target. Link = 4 } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs ================================================ // Copyright (c) 2020 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 // 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.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpyX.TreeView { public partial class SharpTreeNode : INotifyPropertyChanged { [AllowNull] protected static ITreeNodeImagesProvider ImagesProvider { get; private set; } public static void SetImagesProvider(ITreeNodeImagesProvider provider) => ImagesProvider = provider; SharpTreeNodeCollection? modelChildren; internal SharpTreeNode? modelParent; bool isVisible = true; void UpdateIsVisible(bool parentIsVisible, bool updateFlattener) { bool newIsVisible = parentIsVisible && !isHidden; if (isVisible != newIsVisible) { isVisible = newIsVisible; // invalidate the augmented data SharpTreeNode node = this; while (node != null && node.totalListLength >= 0) { node.totalListLength = -1; node = node.listParent; } // Remember the removed nodes: List? removedNodes = null; if (updateFlattener && !newIsVisible) { removedNodes = VisibleDescendantsAndSelf().ToList(); } // also update the model children: UpdateChildIsVisible(false); // Validate our invariants: if (updateFlattener) CheckRootInvariants(); // Tell the flattener about the removed nodes: if (removedNodes != null) { var flattener = GetListRoot().treeFlattener; if (flattener != null) { flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes); foreach (var n in removedNodes) n.OnIsVisibleChanged(); } } // Tell the flattener about the new nodes: if (updateFlattener && newIsVisible) { var flattener = GetListRoot().treeFlattener; if (flattener != null) { flattener.NodesInserted(GetVisibleIndexForNode(this), VisibleDescendantsAndSelf()); foreach (var n in VisibleDescendantsAndSelf()) n.OnIsVisibleChanged(); } } } } protected virtual void OnIsVisibleChanged() { } void UpdateChildIsVisible(bool updateFlattener) { if (modelChildren != null && modelChildren.Count > 0) { bool showChildren = isVisible && isExpanded; foreach (SharpTreeNode child in modelChildren) { child.UpdateIsVisible(showChildren, updateFlattener); } } } #region Main public SharpTreeNode() { } public SharpTreeNodeCollection Children { get { if (modelChildren == null) modelChildren = new SharpTreeNodeCollection(this); return modelChildren; } } public SharpTreeNode? Parent { get { return modelParent; } } public virtual object? Text { get { return null; } } public virtual object? NavigationText { get { return Text; } } public virtual object? Icon { get { return null; } } public virtual object? ToolTip { get { return null; } } public virtual object? Background { get { return null; } } public virtual object? Foreground { get { return null; } } public int Level { get { return Parent != null ? Parent.Level + 1 : 0; } } public bool IsRoot { get { return Parent == null; } } bool isHidden; public bool IsHidden { get { return isHidden; } set { if (isHidden != value) { isHidden = value; if (modelParent != null) UpdateIsVisible(modelParent.isVisible && modelParent.isExpanded, true); RaisePropertyChanged(nameof(IsHidden)); if (Parent != null) Parent.RaisePropertyChanged(nameof(ShowExpander)); } } } /// /// Return true when this node is not hidden and when all parent nodes are expanded and not hidden. /// public bool IsVisible { get { return isVisible; } } bool isSelected; public bool IsSelected { get { return isSelected; } set { if (isSelected != value) { isSelected = value; RaisePropertyChanged(nameof(IsSelected)); } } } #endregion #region OnParentChanged / OnChildrenChanged public virtual void OnParentChanged() { } public virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (SharpTreeNode node in e.OldItems) { Debug.Assert(node.modelParent == this); node.modelParent = null; node.OnParentChanged(); Debug.WriteLine("Removing {0} from {1}", node, this); SharpTreeNode removeEnd = node; while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0) removeEnd = removeEnd.modelChildren.Last(); List? removedNodes = null; int visibleIndexOfRemoval = 0; if (node.isVisible) { visibleIndexOfRemoval = GetVisibleIndexForNode(node); removedNodes = node.VisibleDescendantsAndSelf().ToList(); } RemoveNodes(node, removeEnd); if (removedNodes != null) { var flattener = GetListRoot().treeFlattener; if (flattener != null) { flattener.NodesRemoved(visibleIndexOfRemoval, removedNodes); } } } } if (e.NewItems != null) { SharpTreeNode? insertionPos; if (e.NewStartingIndex == 0) insertionPos = null; else insertionPos = modelChildren?[e.NewStartingIndex - 1]; foreach (SharpTreeNode node in e.NewItems) { Debug.Assert(node.modelParent == null); node.modelParent = this; node.OnParentChanged(); node.UpdateIsVisible(isVisible && isExpanded, false); //Debug.WriteLine("Inserting {0} after {1}", node, insertionPos); while (insertionPos != null && insertionPos.modelChildren != null && insertionPos.modelChildren.Count > 0) { insertionPos = insertionPos.modelChildren.Last(); } InsertNodeAfter(insertionPos ?? this, node); insertionPos = node; if (node.isVisible) { var flattener = GetListRoot().treeFlattener; if (flattener != null) { flattener.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf()); } } } } RaisePropertyChanged(nameof(ShowExpander)); RaiseIsLastChangedIfNeeded(e); } #endregion #region Expanding / LazyLoading public virtual object? ExpandedIcon { get { return Icon; } } public virtual bool ShowExpander { get { return LazyLoading || Children.Any(c => !c.isHidden); } } bool isExpanded; public bool IsExpanded { get { return isExpanded; } set { if (isExpanded != value) { isExpanded = value; if (isExpanded) { EnsureLazyChildren(); OnExpanding(); } else { OnCollapsing(); } UpdateChildIsVisible(true); RaisePropertyChanged(nameof(IsExpanded)); } } } protected virtual void OnExpanding() { } protected virtual void OnCollapsing() { } bool lazyLoading; public bool LazyLoading { get { return lazyLoading; } set { lazyLoading = value; if (lazyLoading) { IsExpanded = false; if (canExpandRecursively) { canExpandRecursively = false; RaisePropertyChanged(nameof(CanExpandRecursively)); } } RaisePropertyChanged(nameof(LazyLoading)); RaisePropertyChanged(nameof(ShowExpander)); } } bool canExpandRecursively = true; /// /// Gets whether this node can be expanded recursively. /// If not overridden, this property returns false if the node is using lazy-loading, and true otherwise. /// public virtual bool CanExpandRecursively { get { return canExpandRecursively; } } public virtual bool ShowIcon { get { return Icon != null; } } protected virtual void LoadChildren() { throw new NotSupportedException(GetType().Name + " does not support lazy loading"); } /// /// Ensures the children were initialized (loads children if lazy loading is enabled) /// public void EnsureLazyChildren() { if (LazyLoading) { LazyLoading = false; LoadChildren(); } } #endregion #region Ancestors / Descendants public IEnumerable Descendants() { return TreeTraversal.PreOrder(this.Children, n => n.Children); } public IEnumerable DescendantsAndSelf() { return TreeTraversal.PreOrder(this, n => n.Children); } internal IEnumerable VisibleDescendants() { return TreeTraversal.PreOrder(this.Children.Where(c => c.isVisible), n => n.Children.Where(c => c.isVisible)); } public IEnumerable VisibleDescendantsAndSelf() { return TreeTraversal.PreOrder(this, n => n.Children.Where(c => c.isVisible)); } public IEnumerable Ancestors() { for (SharpTreeNode? n = this.Parent; n != null; n = n.Parent) yield return n; } public IEnumerable AncestorsAndSelf() { for (SharpTreeNode? n = this; n != null; n = n.Parent) yield return n; } #endregion #region Editing public virtual bool IsEditable { get { return false; } } bool isEditing; public bool IsEditing { get { return isEditing; } set { if (isEditing != value) { isEditing = value; RaisePropertyChanged(nameof(IsEditing)); } } } public virtual string? LoadEditText() { return null; } public virtual bool SaveEditText(string value) { return true; } #endregion #region Checkboxes public virtual bool IsCheckable { get { return false; } } bool? isChecked; public bool? IsChecked { get { return isChecked; } set { SetIsChecked(value, true); } } void SetIsChecked(bool? value, bool update) { if (isChecked != value) { isChecked = value; if (update) { if (IsChecked != null) { foreach (var child in Descendants()) { if (child.IsCheckable) { child.SetIsChecked(IsChecked, false); } } } foreach (var parent in Ancestors()) { if (parent.IsCheckable) { if (!parent.TryValueForIsChecked(true)) { if (!parent.TryValueForIsChecked(false)) { parent.SetIsChecked(null, false); } } } } } RaisePropertyChanged(nameof(IsChecked)); } } bool TryValueForIsChecked(bool? value) { if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) { SetIsChecked(value, false); return true; } return false; } #endregion #region Cut / Copy / Paste / Delete public bool IsCut { get { return false; } } /* static List cuttedNodes = new List(); static IDataObject cuttedData; static EventHandler requerySuggestedHandler; // for weak event static void StartCuttedDataWatcher() { requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested); CommandManager.RequerySuggested += requerySuggestedHandler; } static void CommandManager_RequerySuggested(object sender, EventArgs e) { if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) { ClearCuttedData(); } } static void ClearCuttedData() { foreach (var node in cuttedNodes) { node.IsCut = false; } cuttedNodes.Clear(); cuttedData = null; } //static public IEnumerable PurifyNodes(IEnumerable nodes) //{ // var list = nodes.ToList(); // var array = list.ToArray(); // foreach (var node1 in array) { // foreach (var node2 in array) { // if (node1.Descendants().Contains(node2)) { // list.Remove(node2); // } // } // } // return list; //} bool isCut; public bool IsCut { get { return isCut; } private set { isCut = value; RaisePropertyChanged("IsCut"); } } internal bool InternalCanCut() { return InternalCanCopy() && InternalCanDelete(); } internal void InternalCut() { ClearCuttedData(); cuttedData = Copy(ActiveNodesArray); Clipboard.SetDataObject(cuttedData); foreach (var node in ActiveNodes) { node.IsCut = true; cuttedNodes.Add(node); } } internal bool InternalCanCopy() { return CanCopy(ActiveNodesArray); } internal void InternalCopy() { Clipboard.SetDataObject(Copy(ActiveNodesArray)); } internal bool InternalCanPaste() { return CanPaste(Clipboard.GetDataObject()); } internal void InternalPaste() { Paste(Clipboard.GetDataObject()); if (cuttedData != null) { DeleteCore(cuttedNodes.ToArray()); ClearCuttedData(); } } */ public virtual bool CanDelete() { return false; } public virtual void Delete() { throw new NotSupportedException(GetType().Name + " does not support deletion"); } public virtual void DeleteCore() { throw new NotSupportedException(GetType().Name + " does not support deletion"); } public virtual IPlatformDataObject Copy(SharpTreeNode[] nodes) { throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop"); } /* public virtual bool CanCopy(SharpTreeNode[] nodes) { return false; } public virtual bool CanPaste(IDataObject data) { return false; } public virtual void Paste(IDataObject data) { EnsureLazyChildren(); Drop(data, Children.Count, DropEffect.Copy); } */ #endregion #region Drag and Drop public virtual bool CanDrag(SharpTreeNode[] nodes) { return false; } public virtual void StartDrag(object dragSource, SharpTreeNode[] nodes, IPlatformDragDrop dragdropManager) { XPlatDragDropEffects effects = XPlatDragDropEffects.All; if (!nodes.All(n => n.CanDelete())) effects &= ~XPlatDragDropEffects.Move; XPlatDragDropEffects result = dragdropManager.DoDragDrop(dragSource, Copy(nodes), effects); if (result == XPlatDragDropEffects.Move) { foreach (SharpTreeNode node in nodes) node.DeleteCore(); } } public virtual bool CanDrop(IPlatformDragEventArgs e, int index) { return false; } public void InternalDrop(IPlatformDragEventArgs e, int index) { if (LazyLoading) { EnsureLazyChildren(); index = Children.Count; } Drop(e, index); } public virtual void Drop(IPlatformDragEventArgs e, int index) { throw new NotSupportedException(GetType().Name + " does not support Drop()"); } #endregion #region IsLast (for TreeView lines) public bool IsLast { get { return Parent == null || Parent.Children.Count == 0 || Parent.Children[^1] == this; } } void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewStartingIndex == Children.Count - 1) { if (Children.Count > 1) { Children[Children.Count - 2].RaisePropertyChanged(nameof(IsLast)); } Children[Children.Count - 1].RaisePropertyChanged(nameof(IsLast)); } break; case NotifyCollectionChangedAction.Remove: if (e.OldStartingIndex == Children.Count) { if (Children.Count > 0) { Children[Children.Count - 1].RaisePropertyChanged(nameof(IsLast)); } } break; } } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler? PropertyChanged; public void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion /// /// Gets called when the item is double-clicked. /// public virtual void ActivateItem(IPlatformRoutedEventArgs e) { } /// /// Gets called when the item is clicked with the middle mouse button. /// public virtual void ActivateItemSecondary(IPlatformRoutedEventArgs e) { } public override string? ToString() { // used for keyboard navigation object? text = this.Text; return text != null ? text.ToString() : string.Empty; } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/SharpTreeNodeCollection.cs ================================================ // Copyright (c) 2020 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 // 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.Collections.Specialized; using System.Diagnostics; using System.Linq; #nullable disable namespace ICSharpCode.ILSpyX.TreeView { /// /// Collection that validates that inserted nodes do not have another parent. /// public sealed class SharpTreeNodeCollection : IList, INotifyCollectionChanged { readonly SharpTreeNode parent; List list = new List(); bool isRaisingEvent; public SharpTreeNodeCollection(SharpTreeNode parent) { this.parent = parent; } public event NotifyCollectionChangedEventHandler CollectionChanged; void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { Debug.Assert(!isRaisingEvent); isRaisingEvent = true; try { parent.OnChildrenChanged(e); CollectionChanged?.Invoke(this, e); } finally { isRaisingEvent = false; } } void ThrowOnReentrancy() { if (isRaisingEvent) throw new InvalidOperationException(); } void ThrowIfValueIsNullOrHasParent(SharpTreeNode node) { if (node == null) throw new ArgumentNullException("node"); if (node.modelParent != null) throw new ArgumentException("The node already has a parent", "node"); } public SharpTreeNode this[int index] { get { return list[index]; } set { ThrowOnReentrancy(); var oldItem = list[index]; if (oldItem == value) return; ThrowIfValueIsNullOrHasParent(value); list[index] = value; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index)); } } public int Count { get { return list.Count; } } bool ICollection.IsReadOnly { get { return false; } } public int IndexOf(SharpTreeNode node) { if (node == null || node.modelParent != parent) return -1; else return list.IndexOf(node); } public void Insert(int index, SharpTreeNode node) { ThrowOnReentrancy(); ThrowIfValueIsNullOrHasParent(node); list.Insert(index, node); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index)); } public void InsertRange(int index, IEnumerable nodes) { if (nodes == null) throw new ArgumentNullException("nodes"); ThrowOnReentrancy(); List newNodes = nodes.ToList(); if (newNodes.Count == 0) return; foreach (SharpTreeNode node in newNodes) { ThrowIfValueIsNullOrHasParent(node); } list.InsertRange(index, newNodes); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newNodes, index)); } public void RemoveAt(int index) { ThrowOnReentrancy(); var oldItem = list[index]; list.RemoveAt(index); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index)); } public void RemoveRange(int index, int count) { ThrowOnReentrancy(); if (count == 0) return; var oldItems = list.GetRange(index, count); list.RemoveRange(index, count); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, index)); } public void Add(SharpTreeNode node) { ThrowOnReentrancy(); ThrowIfValueIsNullOrHasParent(node); list.Add(node); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1)); } public void AddRange(IEnumerable nodes) { InsertRange(this.Count, nodes); } public void Clear() { ThrowOnReentrancy(); var oldList = list; list = new List(); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldList, 0)); } public bool Contains(SharpTreeNode node) { return IndexOf(node) >= 0; } public void CopyTo(SharpTreeNode[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } public bool Remove(SharpTreeNode item) { int pos = IndexOf(item); if (pos >= 0) { RemoveAt(pos); return true; } else { return false; } } public IEnumerator GetEnumerator() { return list.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return list.GetEnumerator(); } public void RemoveAll(Predicate match) { if (match == null) throw new ArgumentNullException("match"); ThrowOnReentrancy(); int firstToRemove = 0; for (int i = 0; i < list.Count; i++) { bool removeNode; isRaisingEvent = true; try { removeNode = match(list[i]); } finally { isRaisingEvent = false; } if (!removeNode) { if (firstToRemove < i) { RemoveRange(firstToRemove, i - firstToRemove); i = firstToRemove - 1; } else { firstToRemove = i + 1; } Debug.Assert(firstToRemove == i + 1); } } if (firstToRemove < list.Count) { RemoveRange(firstToRemove, list.Count - firstToRemove); } } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/TreeFlattener.cs ================================================ // Copyright (c) 2020 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 // 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; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; #nullable disable namespace ICSharpCode.ILSpyX.TreeView { public sealed class TreeFlattener : IList, INotifyCollectionChanged { /// /// The root node of the flat list tree. /// Tjis is not necessarily the root of the model! /// internal SharpTreeNode root; readonly bool includeRoot; readonly object syncRoot = new object(); public TreeFlattener(SharpTreeNode modelRoot, bool includeRoot) { this.root = modelRoot; while (root.listParent != null) root = root.listParent; root.treeFlattener = this; this.includeRoot = includeRoot; } public event NotifyCollectionChangedEventHandler CollectionChanged; public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e) { CollectionChanged?.Invoke(this, e); } public void NodesInserted(int index, IEnumerable nodes) { if (!includeRoot) index--; foreach (SharpTreeNode node in nodes) { RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index++)); } } public void NodesRemoved(int index, IEnumerable nodes) { if (!includeRoot) index--; foreach (SharpTreeNode node in nodes) { RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, node, index)); } } public void Stop() { Debug.Assert(root.treeFlattener == this); root.treeFlattener = null; } public object this[int index] { get { if (index < 0 || index >= this.Count) throw new ArgumentOutOfRangeException(); return SharpTreeNode.GetNodeByVisibleIndex(root, includeRoot ? index : index + 1); } set { throw new NotSupportedException(); } } public int Count { get { return includeRoot ? root.GetTotalListLength() : root.GetTotalListLength() - 1; } } public int IndexOf(object item) { SharpTreeNode node = item as SharpTreeNode; if (node != null && node.IsVisible && node.GetListRoot() == root) { if (includeRoot) return SharpTreeNode.GetVisibleIndexForNode(node); else return SharpTreeNode.GetVisibleIndexForNode(node) - 1; } else { return -1; } } bool IList.IsReadOnly { get { return true; } } bool IList.IsFixedSize { get { return false; } } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { return syncRoot; } } void IList.Insert(int index, object item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } int IList.Add(object item) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } public bool Contains(object item) { return IndexOf(item) >= 0; } public void CopyTo(Array array, int arrayIndex) { foreach (object item in this) array.SetValue(item, arrayIndex++); } void IList.Remove(object item) { throw new NotSupportedException(); } public IEnumerator GetEnumerator() { for (int i = 0; i < this.Count; i++) { yield return this[i]; } } } } ================================================ FILE: ICSharpCode.ILSpyX/TreeView/TreeTraversal.cs ================================================ // Copyright (c) 2020 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 // 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; namespace ICSharpCode.ILSpyX.TreeView { /// /// Static helper methods for traversing trees. /// static class TreeTraversal { /// /// Converts a tree data structure into a flat list by traversing it in pre-order. /// /// The root element of the tree. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in pre-order. public static IEnumerable PreOrder(T root, Func> recursion) { return PreOrder(new T[] { root }, recursion); } /// /// Converts a tree data structure into a flat list by traversing it in pre-order. /// /// The root elements of the forest. /// The function that gets the children of an element. /// Iterator that enumerates the tree structure in pre-order. public static IEnumerable PreOrder(IEnumerable input, Func> recursion) { Stack> stack = new Stack>(); try { stack.Push(input.GetEnumerator()); while (stack.Count > 0) { while (stack.Peek().MoveNext()) { T element = stack.Peek().Current; yield return element; IEnumerable children = recursion(element); if (children != null) { stack.Push(children.GetEnumerator()); } } stack.Pop().Dispose(); } } finally { while (stack.Count > 0) { stack.Pop().Dispose(); } } } } } ================================================ FILE: ICSharpCode.ILSpyX/packages.lock.json ================================================ { "version": 2, "dependencies": { "net10.0": { "K4os.Compression.LZ4": { "type": "Direct", "requested": "[1.3.8, )", "resolved": "1.3.8", "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g==" }, "Microsoft.Sbom.Targets": { "type": "Direct", "requested": "[4.1.5, )", "resolved": "4.1.5", "contentHash": "i5z+cNu/cOcdO0AgFB8aXk8w6In2H+haaDfSgd9ImvQIK+rSHavHZIogVoAZLL8jLwYx4bAcs5b7EyuMMG4mQQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[10.0.201, )", "resolved": "10.0.201", "contentHash": "qxYAmO4ktzd9L+HMdnqWucxpu7bI9undPyACXOMqPyhaiMtbpbYL/n0ACyWIJlbyEJrXFwxiOaBOSasLtDvsCg==", "dependencies": { "Microsoft.Build.Tasks.Git": "10.0.201", "Microsoft.SourceLink.Common": "10.0.201", "System.IO.Hashing": "10.0.5" } }, "Mono.Cecil": { "type": "Direct", "requested": "[0.11.6, )", "resolved": "0.11.6", "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA==" }, "System.Composition.AttributedModel": { "type": "Direct", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "Vgb7wwB7ya+lbcwccXHZPSJxeKR7tCkWLgFXO9Wcgbu/NgO5DvNAIHtEkXaEESkcvXdD1iqp2JBcLWGT/xDxEw==" }, "System.Reflection.Metadata": { "type": "Direct", "requested": "[10.0.5, )", "resolved": "10.0.5", "contentHash": "fEAXJCtauNLYr5ESg3t6HE2Av6urWdJdymxZbuSt/DDqhtNtLtUtXTEpKbp0vkTdyBJwmaha8d2454vSzS/lcQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Direct", "requested": "[6.1.2, )", "resolved": "6.1.2", "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "TomsToolbox.Composition.Analyzer": { "type": "Direct", "requested": "[2.22.2, )", "resolved": "2.22.2", "contentHash": "7gYo8ZR2eq3XkrilvUpLbTypeZy6IlD5FB8jah0YPhMOmDGhya4jJ3kfDMTTRt5m258Ou78P69mHMkG6DKZXsg==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "10.0.201", "contentHash": "DMYBnrFZvLnBKn14VavEuuIr31CY6YY2i2L9P8DorS/Qp6ifRR8ZPLdJCFLFfjikNq8DykbYyLd/RP6lSqHcWw==", "dependencies": { "System.IO.Hashing": "10.0.5" } }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "10.0.201", "contentHash": "QbBYhkjgL6rCnBfDbzsAJLlsad13TlBHqYCFDIw56OO2g6ix+9RsmY8uxiQGdWwFKbZXaXyAA6jDCzFYVGCZDw==" }, "System.IO.Hashing": { "type": "Transitive", "resolved": "10.0.5", "contentHash": "8IBJWcCT9+e4Bmevm4T7+fQEiAh133KGiz4oiVTgJckd3Q76OFdR1falgn9lpz7+C4HJvogCDJeAa2QmvbeVtg==" }, "icsharpcode.decompiler": { "type": "Project", "dependencies": { "System.Collections.Immutable": "[9.0.0, )", "System.Reflection.Metadata": "[9.0.0, )" } }, "System.Collections.Immutable": { "type": "CentralTransitive", "requested": "[10.0.5, )", "resolved": "9.0.0", "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" } } } } ================================================ FILE: ILSpy/AboutPage.cs ================================================ // 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 // 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.Composition; using System.IO; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Navigation; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.Updates; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)] [Shared] public sealed class AboutPage : SimpleCommand { readonly SettingsService settingsService; readonly IEnumerable aboutPageAdditions; public AboutPage(SettingsService settingsService, IEnumerable aboutPageAdditions) { this.settingsService = settingsService; this.aboutPageAdditions = aboutPageAdditions; MessageBus.Subscribers += (_, e) => ShowAboutPage(e.TabPage); } public override void Execute(object parameter) { MessageBus.Send(this, new NavigateToEventArgs(new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null), inNewTabPage: true)); } private void ShowAboutPage(TabPageModel tabPage) { tabPage.ShowTextView(Display); } private void Display(DecompilerTextView textView) { AvalonEditTextOutput output = new AvalonEditTextOutput() { Title = Resources.About, EnableHyperlinks = true }; output.WriteLine(Resources.ILSpyVersion + DecompilerVersionInfo.FullVersionWithCommitHash); string prodVersion = GetDotnetProductVersion(); output.WriteLine(Resources.NETFrameworkVersion + prodVersion); output.AddUIElement( delegate { var stackPanel = new StackPanel { HorizontalAlignment = HorizontalAlignment.Center, Orientation = Orientation.Horizontal }; if (UpdateService.LatestAvailableVersion == null) { AddUpdateCheckButton(stackPanel, textView); } else { // we already retrieved the latest version sometime earlier ShowAvailableVersion(UpdateService.LatestAvailableVersion, stackPanel); } var checkBox = new CheckBox { Margin = new Thickness(4), Content = Resources.AutomaticallyCheckUpdatesEveryWeek }; var settings = settingsService.GetSettings(); checkBox.SetBinding(ToggleButton.IsCheckedProperty, new Binding("AutomaticUpdateCheckEnabled") { Source = settings }); return new StackPanel { Margin = new Thickness(0, 4, 0, 0), Cursor = Cursors.Arrow, Children = { stackPanel, checkBox } }; }); output.WriteLine(); foreach (var plugin in aboutPageAdditions) plugin.Write(output); output.WriteLine(); output.Address = new Uri("resource://AboutPage"); using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), Resources.ILSpyAboutPageTxt)) { using (StreamReader r = new StreamReader(s)) { while (r.ReadLine() is { } line) { output.WriteLine(line); } } } output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt")); output.AddVisualLineElementGenerator(new MyLinkElementGenerator("third-party notices", "resource:third-party-notices.txt")); textView.ShowText(output); } private static string GetDotnetProductVersion() { // In case of AOT .Location is null, we need a fallback for that string assemblyLocation = typeof(Uri).Assembly.Location; if (!String.IsNullOrWhiteSpace(assemblyLocation)) { return System.Diagnostics.FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion; } else { var version = typeof(Object).Assembly.GetName().Version; if (version != null) { return version.ToString(); } } return "UNKNOWN"; } sealed class MyLinkElementGenerator : LinkElementGenerator { readonly Uri uri; public MyLinkElementGenerator(string matchText, string url) : base(new Regex(Regex.Escape(matchText))) { this.uri = new Uri(url); this.RequireControlModifierForClick = false; } protected override Uri GetUriFromMatch(Match match) { return uri; } } static void AddUpdateCheckButton(StackPanel stackPanel, DecompilerTextView textView) { Button button = ThemeManager.Current.CreateButton(); button.Content = Resources.CheckUpdates; button.Cursor = Cursors.Arrow; stackPanel.Children.Add(button); button.Click += async delegate { button.Content = Resources.Checking; button.IsEnabled = false; try { AvailableVersionInfo vInfo = await UpdateService.GetLatestVersionAsync(); stackPanel.Children.Clear(); ShowAvailableVersion(vInfo, stackPanel); } catch (Exception ex) { AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput(); exceptionOutput.WriteLine(ex.ToString()); textView.ShowText(exceptionOutput); } }; } static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPanel stackPanel) { if (AppUpdateService.CurrentVersion == availableVersion.Version) { stackPanel.Children.Add( new Image { Width = 16, Height = 16, Source = Images.OK, Margin = new Thickness(4, 0, 4, 0) }); stackPanel.Children.Add( new TextBlock { Text = Resources.UsingLatestRelease, VerticalAlignment = VerticalAlignment.Bottom }); } else if (AppUpdateService.CurrentVersion < availableVersion.Version) { stackPanel.Children.Add( new TextBlock { Text = string.Format(Resources.VersionAvailable, availableVersion.Version), Margin = new Thickness(0, 0, 8, 0), VerticalAlignment = VerticalAlignment.Bottom }); if (availableVersion.DownloadUrl != null) { Button button = ThemeManager.Current.CreateButton(); button.Content = Resources.Download; button.Cursor = Cursors.Arrow; button.Click += delegate { GlobalUtils.OpenLink(availableVersion.DownloadUrl); }; stackPanel.Children.Add(button); } } else { stackPanel.Children.Add(new TextBlock { Text = Resources.UsingNightlyBuildNewerThanLatestRelease }); } } } /// /// Interface that allows plugins to extend the about page. /// public interface IAboutPageAddition { void Write(ISmartTextOutput textOutput); } } ================================================ FILE: ILSpy/Analyzers/AnalyzeCommand.cs ================================================ // 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 // 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.Composition; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using TomsToolbox.Composition; namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] [Shared] internal sealed class AnalyzeContextMenuCommand(AnalyzerTreeViewModel analyzerTreeView) : IContextMenuEntry { public bool IsVisible(TextViewContext context) { if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot)) return false; if (context.SelectedTreeNodes == null) return context.Reference != null && IsValidReference(context.Reference.Reference); return context.SelectedTreeNodes.All(n => n is IMemberTreeNode); } public bool IsEnabled(TextViewContext context) { if (context.SelectedTreeNodes == null) { return context.Reference is { Reference: IEntity }; } return context.SelectedTreeNodes .OfType() .All(node => IsValidReference(node.Member)); } static bool IsValidReference(object reference) { return reference is IEntity and not IField { IsConst: true }; } public void Execute(TextViewContext context) { if (context.SelectedTreeNodes != null) { foreach (var node in context.SelectedTreeNodes.OfType().ToArray()) { analyzerTreeView.Analyze(node.Member); } } else if (context.Reference is { Reference: IEntity entity }) { analyzerTreeView.Analyze(entity); } } } [Export] [Shared] public sealed class AnalyzeCommand(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel) : SimpleCommand { public override bool CanExecute(object parameter) { return assemblyTreeModel.SelectedNodes.All(n => n is IMemberTreeNode); } public override void Execute(object parameter) { foreach (var node in assemblyTreeModel.SelectedNodes.OfType()) { analyzerTreeViewModel.Analyze(node.Member); } } } } ================================================ FILE: ILSpy/Analyzers/AnalyzerEntityTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Diagnostics; using System.Windows; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; #nullable enable namespace ICSharpCode.ILSpy.Analyzers { /// /// Base class for entity nodes. /// public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode { public abstract IEntity? Member { get; } public IEntity? SourceMember { get; protected set; } public override void ActivateItem(IPlatformRoutedEventArgs e) { e.Handled = true; if (this.Member == null || this.Member.MetadataToken.IsNil) { MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); return; } var module = this.Member.ParentModule?.MetadataFile; Debug.Assert(module != null); MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, this.Member.MetadataToken), this.SourceMember)); } public override object? ToolTip => Member?.ParentModule?.MetadataFile?.FileName; public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { if (Member == null) { return true; } foreach (LoadedAssembly asm in removedAssemblies) { if (this.Member.ParentModule!.MetadataFile == asm.GetMetadataFileOrNull()) return false; // remove this node } this.Children.RemoveAll( delegate (SharpTreeNode n) { return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); }); return true; } } } ================================================ FILE: ILSpy/Analyzers/AnalyzerRootNode.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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.Collections.Generic; using System.Collections.Specialized; using System.Linq; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Analyzers; public sealed class AnalyzerRootNode : AnalyzerTreeNode { public AnalyzerRootNode() { MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); } void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Reset) { this.Children.Clear(); } else { var removedAssemblies = e.OldItems?.Cast().ToArray() ?? []; var addedAssemblies = e.NewItems?.Cast().ToArray() ?? []; HandleAssemblyListChanged(removedAssemblies, addedAssemblies); } } public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { this.Children.RemoveAll( delegate (SharpTreeNode n) { AnalyzerTreeNode an = n as AnalyzerTreeNode; return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); }); return true; } } ================================================ FILE: ILSpy/Analyzers/AnalyzerSearchTreeNode.cs ================================================ // 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 // 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; using System.Threading; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Analyzers.TreeNodes; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Analyzers; namespace ICSharpCode.ILSpy.Analyzers { class AnalyzerSearchTreeNode : AnalyzerTreeNode { private readonly ThreadingSupport threading = new ThreadingSupport(); readonly ISymbol symbol; readonly IAnalyzer analyzer; readonly string analyzerHeader; public AnalyzerSearchTreeNode(ISymbol symbol, IAnalyzer analyzer, string analyzerHeader) { this.symbol = symbol; this.analyzer = analyzer ?? throw new ArgumentNullException(nameof(analyzer)); this.LazyLoading = true; this.analyzerHeader = analyzerHeader; } public override object Text => analyzerHeader + (Children.Count > 0 && !threading.IsRunning ? " (" + Children.Count + " in " + threading.EllapsedMilliseconds + "ms)" : ""); public override object Icon => Images.Search; protected override void LoadChildren() { threading.LoadChildren(this, FetchChildren); } protected IEnumerable FetchChildren(CancellationToken ct) { if (symbol is IEntity) { var context = new AnalyzerContext { CancellationToken = ct, Language = Language, AssemblyList = AssemblyList }; var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory); if (context.SortResults) { results = results.OrderBy(tn => tn.Text?.ToString(), NaturalStringComparer.Instance); } return results; } else { throw new NotSupportedException("Currently symbols that are not entities are not supported!"); } } AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol resultSymbol) { if (resultSymbol == null) { throw new ArgumentNullException(nameof(resultSymbol)); } switch (resultSymbol) { case IModule module: return new AnalyzedModuleTreeNode(module, (IEntity)this.symbol); case ITypeDefinition td: return new AnalyzedTypeTreeNode(td, (IEntity)this.symbol); case IField fd: return new AnalyzedFieldTreeNode(fd, (IEntity)this.symbol); case IMethod md: return new AnalyzedMethodTreeNode(md, (IEntity)this.symbol); case IProperty pd: return new AnalyzedPropertyTreeNode(pd, (IEntity)this.symbol); case IEvent ed: return new AnalyzedEventTreeNode(ed, (IEntity)this.symbol); default: throw new ArgumentOutOfRangeException(nameof(resultSymbol), $"Symbol {resultSymbol.GetType().FullName} is not supported."); } } protected override void OnIsVisibleChanged() { base.OnIsVisibleChanged(); if (!this.IsVisible && threading.IsRunning) { this.LazyLoading = true; threading.Cancel(); this.Children.Clear(); RaisePropertyChanged(nameof(Text)); } } public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { // only cancel a running analysis if user has manually added/removed assemblies bool manualAdd = false; foreach (var asm in addedAssemblies) { if (!asm.IsAutoLoaded) manualAdd = true; } if (removedAssemblies.Count > 0 || manualAdd) { this.LazyLoading = true; threading.Cancel(); this.Children.Clear(); RaisePropertyChanged(nameof(Text)); } return true; } } } ================================================ FILE: ILSpy/Analyzers/AnalyzerTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Analyzers; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; namespace ICSharpCode.ILSpy.Analyzers { public abstract class AnalyzerTreeNode : SharpTreeNode { protected static Language Language => App.ExportProvider.GetExportedValue().Language; protected static AssemblyList AssemblyList => App.ExportProvider.GetExportedValue(); public override bool CanDelete() { return Parent is { IsRoot: true }; } public override void DeleteCore() { Parent.Children.Remove(this); } public override void Delete() { DeleteCore(); } public static ICollection> Analyzers => App.ExportProvider .GetExports("Analyzer") .OrderBy(item => item.Metadata?.Order) .ToArray(); /// /// Handles changes to the assembly list. /// public abstract bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies); } } ================================================ FILE: ILSpy/Analyzers/AnalyzerTreeView.xaml ================================================ ================================================ FILE: ILSpy/Analyzers/AnalyzerTreeView.xaml.cs ================================================ // 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 // 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.Composition; using System.Windows.Controls; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Wpf.Composition.AttributedModel; namespace ICSharpCode.ILSpy.Analyzers { /// /// Interaction logic for AnalyzerTreeView.xaml /// [DataTemplate(typeof(AnalyzerTreeViewModel))] [NonShared] [Export] public partial class AnalyzerTreeView { public AnalyzerTreeView() { InitializeComponent(); ContextMenuProvider.Add(this); } private void AnalyzerTreeView_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (SelectedItem is SharpTreeNode sharpTreeNode) { FocusNode(sharpTreeNode); } } } } ================================================ FILE: ILSpy/Analyzers/AnalyzerTreeViewModel.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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.Composition; using System.Linq; using System.Windows; using System.Windows.Input; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Analyzers.TreeNodes; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Analyzers { [ExportToolPane] [Shared] [Export] public class AnalyzerTreeViewModel : ToolPaneModel { public const string PaneContentId = "analyzerPane"; public AnalyzerTreeViewModel(AssemblyTreeModel assemblyTreeModel) { ContentId = PaneContentId; Title = Properties.Resources.Analyze; ShortcutKey = new(Key.R, ModifierKeys.Control); AssociatedCommand = new AnalyzeCommand(assemblyTreeModel, this); } public AnalyzerRootNode Root { get; } = new(); public ICommand AnalyzeCommand => new DelegateCommand(AnalyzeSelected); private AnalyzerTreeNode[] selectedItems = []; public AnalyzerTreeNode[] SelectedItems { get => selectedItems ?? []; set { if (SelectedItems.SequenceEqual(value)) return; selectedItems = value; OnPropertyChanged(); } } private void AnalyzeSelected() { foreach (var node in SelectedItems.OfType()) { Analyze(node.Member); } } void AddOrSelect(AnalyzerTreeNode node) { Show(); AnalyzerTreeNode target = default; if (node is AnalyzerEntityTreeNode { Member: { } member }) { target = this.Root.Children.OfType().FirstOrDefault(item => item.Member == member); } if (target == null) { this.Root.Children.Add(node); target = node; } target.IsExpanded = true; this.SelectedItems = [target]; } public void Analyze(IEntity entity) { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } if (entity.MetadataToken.IsNil) { MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); return; } switch (entity) { case ITypeDefinition td: AddOrSelect(new AnalyzedTypeTreeNode(td, null)); break; case IField fd: if (!fd.IsConst) AddOrSelect(new AnalyzedFieldTreeNode(fd, null)); break; case IMethod md: AddOrSelect(new AnalyzedMethodTreeNode(md, null)); break; case IProperty pd: AddOrSelect(new AnalyzedPropertyTreeNode(pd, null)); break; case IEvent ed: AddOrSelect(new AnalyzedEventTreeNode(ed, null)); break; default: throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported."); } } } } ================================================ FILE: ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs ================================================ // Copyright (c) 2022 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.Composition; using System.Linq; using System.Text; using System.Windows; namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = "Copy results", Category = "Analyze", Order = 200)] [Shared] internal sealed class CopyAnalysisResultsContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) { if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AnalyzerSearchTreeNode)) return true; return false; } public bool IsEnabled(TextViewContext context) { return true; } public void Execute(TextViewContext context) { StringBuilder sb = new StringBuilder(); if (context.SelectedTreeNodes != null) { foreach (var node in context.SelectedTreeNodes) { foreach (var item in node.Children) { sb.AppendLine(item.Text.ToString()); } } } Clipboard.SetText(sb.ToString()); } } } ================================================ FILE: ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs ================================================ // Copyright (c) 2013 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 // 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.Composition; using System.Linq; namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete", Category = "Analyze", Order = 200)] [Shared] internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) { if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot)) return true; return false; } public bool IsEnabled(TextViewContext context) { return true; } public void Execute(TextViewContext context) { if (context.SelectedTreeNodes != null) { foreach (var node in context.SelectedTreeNodes) { node.Parent.Children.Remove(node); } } } } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs ================================================ // 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 // 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 ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { class AnalyzedAccessorTreeNode : AnalyzedMethodTreeNode { readonly string name; public AnalyzedAccessorTreeNode(IMethod analyzedMethod, IEntity source, string name) : base(analyzedMethod, source) { if (string.IsNullOrWhiteSpace(name)) { throw new System.ArgumentException("name must be a non-empty string", nameof(name)); } this.name = name; } public override object Text => name; } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs ================================================ // 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 // 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.Diagnostics; using System.Diagnostics.CodeAnalysis; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode { readonly IEvent analyzedEvent; readonly string prefix; public AnalyzedEventTreeNode(IEvent analyzedEvent, IEntity? source, string prefix = "") { this.analyzedEvent = analyzedEvent ?? throw new ArgumentNullException(nameof(analyzedEvent)); this.prefix = prefix; this.LazyLoading = true; this.SourceMember = source; } public override IEntity Member => analyzedEvent; public override object Icon => EventTreeNode.GetIcon(analyzedEvent); // TODO: This way of formatting is not suitable for events which explicitly implement interfaces. public override object Text => prefix + Language.EntityToString(analyzedEvent, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { if (analyzedEvent.CanAdd) this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, this.SourceMember, "add")); if (analyzedEvent.CanRemove) this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, this.SourceMember, "remove")); if (TryFindBackingField(analyzedEvent, out var backingField)) this.Children.Add(new AnalyzedFieldTreeNode(backingField, this.SourceMember)); foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedEvent)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header)); } } } bool TryFindBackingField(IEvent analyzedEvent, [NotNullWhen(true)] out IField? backingField) { backingField = null; foreach (var field in analyzedEvent.DeclaringTypeDefinition?.GetFields(options: GetMemberOptions.IgnoreInheritedMembers) ?? []) { if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private) { backingField = field; return true; } } return false; } } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs ================================================ // 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 // 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.Diagnostics; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode { readonly IField analyzedField; public AnalyzedFieldTreeNode(IField analyzedField, IEntity? source) { this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField)); this.SourceMember = source; this.LazyLoading = true; } public override object Icon => FieldTreeNode.GetIcon(analyzedField); public override object Text => Language.EntityToString(analyzedField, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedField)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header)); } } } public override IEntity Member => analyzedField; } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs ================================================ // 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 // 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.Diagnostics; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode { readonly IMethod analyzedMethod; readonly string prefix; public AnalyzedMethodTreeNode(IMethod analyzedMethod, IEntity? source, string prefix = "") { this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod)); this.SourceMember = source; this.prefix = prefix; this.LazyLoading = true; } public override object Icon => MethodTreeNode.GetIcon(analyzedMethod); public override object Text => prefix + Language.EntityToString(analyzedMethod, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedMethod)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata!.Header)); } } } public override IEntity Member => analyzedMethod; } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs ================================================ // 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 // 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.Windows; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedModuleTreeNode : AnalyzerEntityTreeNode { readonly IModule analyzedModule; public AnalyzedModuleTreeNode(IModule analyzedModule, IEntity? source) { this.analyzedModule = analyzedModule ?? throw new ArgumentNullException(nameof(analyzedModule)); this.SourceMember = source; this.LazyLoading = true; } public override object Icon => Images.Assembly; public override object Text => analyzedModule.AssemblyName; public override object? ToolTip => analyzedModule.MetadataFile?.FileName; protected override void LoadChildren() { foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedModule)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata!.Header)); } } } public override void ActivateItem(IPlatformRoutedEventArgs e) { e.Handled = true; if (analyzedModule.MetadataFile == null) { MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); return; } MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile)); } public override IEntity? Member => null; public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { if (analyzedModule == null) { return true; } foreach (LoadedAssembly asm in removedAssemblies) { if (this.analyzedModule.MetadataFile == asm.GetMetadataFileOrNull()) return false; // remove this node } this.Children.RemoveAll( delegate (SharpTreeNode n) { return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); }); return true; } } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs ================================================ // 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 // 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.Diagnostics; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { sealed class AnalyzedPropertyTreeNode : AnalyzerEntityTreeNode { readonly IProperty analyzedProperty; readonly string prefix; public AnalyzedPropertyTreeNode(IProperty analyzedProperty, IEntity? source, string prefix = "") { this.analyzedProperty = analyzedProperty ?? throw new ArgumentNullException(nameof(analyzedProperty)); this.prefix = prefix; this.LazyLoading = true; this.SourceMember = source; } public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty); // TODO: This way of formatting is not suitable for properties which explicitly implement interfaces. public override object Text => prefix + Language.EntityToString(analyzedProperty, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { if (analyzedProperty.CanGet) this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, this.SourceMember, "get")); if (analyzedProperty.CanSet) this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, this.SourceMember, "set")); foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedProperty)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata!.Header)); } } } public override IEntity Member => analyzedProperty; } } ================================================ FILE: ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs ================================================ // 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 // 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.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; #nullable enable namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode { readonly ITypeDefinition analyzedType; public AnalyzedTypeTreeNode(ITypeDefinition analyzedType, IEntity? source) { this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType)); this.SourceMember = source; this.LazyLoading = true; } public override object Icon => TypeTreeNode.GetIcon(analyzedType); public override object Text => Language.TypeToString(analyzedType); protected override void LoadChildren() { foreach (var lazy in Analyzers) { var analyzer = lazy.Value; Debug.Assert(analyzer != null); if (analyzer.Show(analyzedType)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata!.Header)); } } } public override IEntity Member => analyzedType; } } ================================================ FILE: ILSpy/App.xaml ================================================ ================================================ FILE: ILSpy/App.xaml.cs ================================================ // 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 // 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.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpyX.Analyzers; using Medo.Application; using TomsToolbox.Wpf.Styles; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; using TomsToolbox.Wpf.Composition; using ICSharpCode.ILSpy.Themes; using System.Globalization; using System.Threading; using Microsoft.Extensions.DependencyInjection; using TomsToolbox.Composition.MicrosoftExtensions; using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy { /// /// Interaction logic for App.xaml /// public partial class App : Application { internal static CommandLineArguments CommandLineArguments; internal static readonly IList StartupExceptions = new List(); public static IExportProvider ExportProvider { get; private set; } internal record ExceptionData(Exception Exception) { public string PluginName { get; init; } } public App() { var cmdArgs = Environment.GetCommandLineArgs().Skip(1); CommandLineArguments = CommandLineArguments.Create(cmdArgs); // This is only a temporary, read only handle to the settings service to access the AllowMultipleInstances setting before DI is initialized. // At runtime, you must use the service via DI! var settingsService = new SettingsService(); bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true) && !settingsService.MiscSettings.AllowMultipleInstances; if (forceSingleInstance) { SingleInstance.Attach(); // will auto-exit for second instance SingleInstance.NewInstanceDetected += SingleInstance_NewInstanceDetected; } InitializeComponent(); if (!InitializeDependencyInjection(settingsService)) { // There is something completely wrong with DI, probably some service registration is missing => nothing we can do to recover, so stop and shut down. Exit += (_, _) => MessageBox.Show(StartupExceptions.FormatExceptions(), "Sorry we crashed!", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly); Shutdown(1); return; } if (!Debugger.IsAttached) { AppDomain.CurrentDomain.UnhandledException += ShowErrorBox; Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException; } TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException; SharpTreeNode.SetImagesProvider(new WpfWindowsTreeNodeImagesProvider()); Resources.RegisterDefaultStyles(); // Register the export provider so that it can be accessed from WPF/XAML components. ExportProviderLocator.Register(ExportProvider); // Add data templates registered via MEF. Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider)); var sessionSettings = settingsService.SessionSettings; ThemeManager.Current.Theme = sessionSettings.Theme; if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture)) { Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new(sessionSettings.CurrentCulture); } ILSpyTraceListener.Install(); if (CommandLineArguments.ArgumentsParser.IsShowingInformation) { MessageBox.Show(CommandLineArguments.ArgumentsParser.GetHelpText(), "ILSpy Command Line Arguments"); } if (CommandLineArguments.ArgumentsParser.RemainingArguments.Any()) { string unknownArguments = string.Join(", ", CommandLineArguments.ArgumentsParser.RemainingArguments); MessageBox.Show(unknownArguments, "ILSpy Unknown Command Line Arguments Passed"); } settingsService.AssemblyListManager.CreateDefaultAssemblyLists(); } public new static App Current => (App)Application.Current; public new MainWindow MainWindow { get => (MainWindow)base.MainWindow; private set => base.MainWindow = value; } private static void SingleInstance_NewInstanceDetected(object sender, NewInstanceEventArgs e) => ExportProvider.GetExportedValue().HandleSingleInstanceCommandLineArguments(e.Args).HandleExceptions(); static Assembly ResolvePluginDependencies(AssemblyLoadContext context, AssemblyName assemblyName) { var rootPath = Path.GetDirectoryName(typeof(App).Assembly.Location); var assemblyFileName = Path.Combine(rootPath, assemblyName.Name + ".dll"); if (!File.Exists(assemblyFileName)) return null; return context.LoadFromAssemblyPath(assemblyFileName); } private bool InitializeDependencyInjection(SettingsService settingsService) { // Add custom logic for resolution of dependencies. // This necessary because the AssemblyLoadContext.LoadFromAssemblyPath and related methods, // do not automatically load dependencies. AssemblyLoadContext.Default.Resolving += ResolvePluginDependencies; try { var services = new ServiceCollection(); var pluginDir = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName); if (pluginDir != null) { foreach (var plugin in Directory.GetFiles(pluginDir, "*.Plugin.dll")) { var name = Path.GetFileNameWithoutExtension(plugin); try { var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(plugin); services.BindExports(assembly); } catch (Exception ex) { // Cannot show MessageBox here, because WPF would crash with a XamlParseException // Remember and show exceptions in text output, once MainWindow is properly initialized StartupExceptions.Add(new(ex) { PluginName = name }); } } } // Add the built-in parts: First, from ILSpyX services.BindExports(typeof(IAnalyzer).Assembly); // Then from ILSpy itself services.BindExports(Assembly.GetExecutingAssembly()); // Add the settings service services.AddSingleton(settingsService); // Add the export provider services.AddSingleton(_ => ExportProvider); // Add the docking manager services.AddSingleton(serviceProvider => serviceProvider.GetService().DockManager); services.AddTransient(serviceProvider => serviceProvider.GetService().AssemblyList); var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); ExportProvider = new ExportProviderAdapter(serviceProvider); Exit += (_, _) => serviceProvider.Dispose(); return true; } catch (Exception ex) { if (ex is AggregateException aggregate) StartupExceptions.AddRange(aggregate.InnerExceptions.Select(item => new ExceptionData(ex))); else StartupExceptions.Add(new(ex)); return false; } } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainWindow = ExportProvider.GetExportedValue(); MainWindow.Show(); } void DotNet40_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { // On .NET 4.0, an unobserved exception in a task terminates the process unless we mark it as observed e.SetObserved(); } #region Exception Handling static void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { UnhandledException(e.Exception); e.Handled = true; } static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e) { Exception ex = e.ExceptionObject as Exception; if (ex != null) { UnhandledException(ex); } } [ThreadStatic] static bool showingError; internal static void UnhandledException(Exception exception) { Debug.WriteLine(exception.ToString()); for (Exception ex = exception; ex != null; ex = ex.InnerException) { ReflectionTypeLoadException rtle = ex as ReflectionTypeLoadException; if (rtle != null && rtle.LoaderExceptions.Length > 0) { exception = rtle.LoaderExceptions[0]; Debug.WriteLine(exception.ToString()); break; } } if (showingError) { // Ignore re-entrant calls // We run the risk of opening an infinite number of exception dialogs. return; } showingError = true; try { MessageBox.Show(exception.ToString(), "Sorry, we crashed"); } finally { showingError = false; } } #endregion } } ================================================ FILE: ILSpy/AppEnv/AppEnvironment.cs ================================================ using System.Runtime.InteropServices; namespace ICSharpCode.ILSpy.AppEnv { public static class AppEnvironment { public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } } ================================================ FILE: ILSpy/AppEnv/CommandLineArguments.cs ================================================ // 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 // 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 McMaster.Extensions.CommandLineUtils; using System; using System.Collections.Generic; using System.Linq; namespace ICSharpCode.ILSpy.AppEnv { public sealed class CommandLineArguments { // see /doc/Command Line.txt for details public List AssembliesToLoad = new List(); public bool? SingleInstance; public string NavigateTo; public string Search; public string Language; public bool NoActivate; public string ConfigFile; public CommandLineApplication ArgumentsParser { get; } private CommandLineArguments(CommandLineApplication app) { ArgumentsParser = app; } public static CommandLineArguments Create(IEnumerable arguments) { var app = new CommandLineApplication() { // https://natemcmaster.github.io/CommandLineUtils/docs/response-file-parsing.html?tabs=using-attributes ResponseFileHandling = ResponseFileHandling.ParseArgsAsLineSeparated, // Note: options are case-sensitive (!), and, default behavior would be UnrecognizedArgumentHandling.Throw on Parse() UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue }; app.HelpOption(); var instance = new CommandLineArguments(app); try { var oForceNewInstance = app.Option("--newinstance", "Start a new instance of ILSpy even if the user configuration is set to single-instance", CommandOptionType.NoValue); var oNavigateTo = app.Option("-n|--navigateto ", "Navigates to the member specified by the given ID string.\r\nThe member is searched for only in the assemblies specified on the command line.\r\nExample: 'ILSpy ILSpy.exe --navigateto T:ICSharpCode.ILSpy.CommandLineArguments'", CommandOptionType.SingleValue); oNavigateTo.DefaultValue = null; var oSearch = app.Option("-s|--search ", "Search for t:TypeName, m:Member or c:Constant; use exact match (=term), 'should not contain' (-term) or 'must contain' (+term); use /reg(ular)?Ex(pressions)?/ or both - t:/Type(Name)?/...", CommandOptionType.SingleValue); oSearch.DefaultValue = null; var oLanguage = app.Option("-l|--language ", "Selects the specified language.\r\nExample: 'ILSpy --language:C#' or 'ILSpy --language IL'", CommandOptionType.SingleValue); oLanguage.DefaultValue = null; var oConfig = app.Option("-c|--config ", "Provide a specific configuration file.\r\nExample: 'ILSpy --config myconfig.xml'", CommandOptionType.SingleValue); oConfig.DefaultValue = null; var oNoActivate = app.Option("--noactivate", "Do not activate the existing ILSpy instance. This option has no effect if a new ILSpy instance is being started.", CommandOptionType.NoValue); // https://natemcmaster.github.io/CommandLineUtils/docs/arguments.html#variable-numbers-of-arguments // To enable this, MultipleValues must be set to true, and the argument must be the last one specified. var files = app.Argument("Assemblies", "Assemblies to load", multipleValues: true); app.Parse(arguments.ToArray()); if (oForceNewInstance.HasValue()) instance.SingleInstance = false; instance.NavigateTo = oNavigateTo.ParsedValue; instance.Search = oSearch.ParsedValue; instance.Language = oLanguage.ParsedValue; instance.ConfigFile = oConfig.ParsedValue; if (oNoActivate.HasValue()) instance.NoActivate = true; foreach (var assembly in files.Values) { if (!string.IsNullOrWhiteSpace(assembly)) instance.AssembliesToLoad.Add(assembly); } } catch (Exception) { // Intentionally ignore exceptions if any, this is only added to always have an exception-free startup } return instance; } } } ================================================ FILE: ILSpy/AppEnv/CommandLineTools.cs ================================================ // Copyright (c) 2024 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 // 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.IO; using System.Text; namespace ICSharpCode.ILSpy.AppEnv { public class CommandLineTools { /// /// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules. /// /// /// Command line parsing rules: /// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument. /// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark. /// - n backslashes not followed by a quotation mark simply produce n backslashes. /// public static string[] CommandLineToArgumentArray(string commandLine) { if (string.IsNullOrEmpty(commandLine)) return Array.Empty(); var results = new List(); ParseArgv.ParseArgumentsIntoList(commandLine, results); return results.ToArray(); } static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' }; /// /// Escapes a set of arguments according to the CommandLineToArgvW rules. /// /// /// Command line parsing rules: /// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument. /// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark. /// - n backslashes not followed by a quotation mark simply produce n backslashes. /// public static string ArgumentArrayToCommandLine(params string[] arguments) { if (arguments == null) return null; StringBuilder b = new StringBuilder(); for (int i = 0; i < arguments.Length; i++) { if (i > 0) b.Append(' '); AppendArgument(b, arguments[i]); } return b.ToString(); } static void AppendArgument(StringBuilder b, string arg) { if (arg == null) { return; } if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) { b.Append(arg); } else { b.Append('"'); for (int j = 0; ; j++) { int backslashCount = 0; while (j < arg.Length && arg[j] == '\\') { backslashCount++; j++; } if (j == arg.Length) { b.Append('\\', backslashCount * 2); break; } else if (arg[j] == '"') { b.Append('\\', backslashCount * 2 + 1); b.Append('"'); } else { b.Append('\\', backslashCount); b.Append(arg[j]); } } b.Append('"'); } } public static string FullyQualifyPath(string argument) { // Fully qualify the paths before passing them to another process, // because that process might use a different current directory. if (string.IsNullOrEmpty(argument) || argument[0] == '-') return argument; try { if (argument.StartsWith("@")) { return "@" + FullyQualifyPath(argument.Substring(1)); } return Path.Combine(Environment.CurrentDirectory, argument); } catch (ArgumentException) { return argument; } } } // Source: https://github.com/dotnet/runtime/blob/bc9fc5a774d96f95abe0ea5c90fac48b38ed2e67/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L574-L606 // Minor adaptations in GetNextArgument (ValueStringBuilder replaced), otherwise kept as identical as possible public static class ParseArgv { /// Parses a command-line argument string into a list of arguments. /// The argument string. /// The list into which the component arguments should be stored. /// /// This follows the rules outlined in "Parsing C++ Command-Line Arguments" at /// https://msdn.microsoft.com/en-us/library/17w5ykft.aspx. /// public static void ParseArgumentsIntoList(string arguments, List results) { // Iterate through all of the characters in the argument string. for (int i = 0; i < arguments.Length; i++) { while (i < arguments.Length && (arguments[i] == ' ' || arguments[i] == '\t')) i++; if (i == arguments.Length) break; results.Add(GetNextArgument(arguments, ref i)); } } private static string GetNextArgument(string arguments, ref int i) { var currentArgument = new StringBuilder(); bool inQuotes = false; while (i < arguments.Length) { // From the current position, iterate through contiguous backslashes. int backslashCount = 0; while (i < arguments.Length && arguments[i] == '\\') { i++; backslashCount++; } if (backslashCount > 0) { if (i >= arguments.Length || arguments[i] != '"') { // Backslashes not followed by a double quote: // they should all be treated as literal backslashes. currentArgument.Append('\\', backslashCount); } else { // Backslashes followed by a double quote: // - Output a literal slash for each complete pair of slashes // - If one remains, use it to make the subsequent quote a literal. currentArgument.Append('\\', backslashCount / 2); if (backslashCount % 2 != 0) { currentArgument.Append('"'); i++; } } continue; } char c = arguments[i]; // If this is a double quote, track whether we're inside of quotes or not. // Anything within quotes will be treated as a single argument, even if // it contains spaces. if (c == '"') { if (inQuotes && i < arguments.Length - 1 && arguments[i + 1] == '"') { // Two consecutive double quotes inside an inQuotes region should result in a literal double quote // (the parser is left in the inQuotes region). // This behavior is not part of the spec of code:ParseArgumentsIntoList, but is compatible with CRT // and .NET Framework. currentArgument.Append('"'); i++; } else { inQuotes = !inQuotes; } i++; continue; } // If this is a space/tab and we're not in quotes, we're done with the current // argument, it should be added to the results and then reset for the next one. if ((c == ' ' || c == '\t') && !inQuotes) { break; } // Nothing special; add the character to the current argument. currentArgument.Append(c); i++; } return currentArgument.ToString(); } } } ================================================ FILE: ILSpy/AppEnv/SingleInstance.cs ================================================ // Source: https://github.com/medo64/Medo/blob/main/src/Medo/Application/SingleInstance.cs /* Josip Medved * www.medo64.com * MIT License */ //2022-12-01: Compatible with .NET 6 and 7 //2012-11-24: Suppressing bogus CA5122 warning (http://connect.microsoft.com/VisualStudio/feedback/details/729254/bogus-ca5122-warning-about-p-invoke-declarations-should-not-be-safe-critical) //2010-10-07: Added IsOtherInstanceRunning method //2008-11-14: Reworked code to use SafeHandle //2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (SpecifyMarshalingForPInvokeStringArguments, NestedTypesShouldNotBeVisible) //2008-04-10: NewInstanceEventArgs is not nested class anymore //2008-01-26: AutoExit parameter changed to NoAutoExit //2008-01-08: Main method is now called Attach //2008-01-06: System.Environment.Exit returns E_ABORT (0x80004004) //2008-01-03: Added Resources //2007-12-29: New version #nullable enable namespace Medo.Application; using System; using System.Diagnostics; using System.IO.Pipes; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using ICSharpCode.ILSpy.AppEnv; /// /// Handles detection and communication of programs multiple instances. /// This class is thread safe. /// public static class SingleInstance { private static Mutex? _mtxFirstInstance; private static Thread? _thread; private static readonly object _syncRoot = new(); /// /// Returns true if this application is not already started. /// Another instance is contacted via named pipe. /// /// API call failed. public static bool Attach() { return Attach(false); } private static string[] GetILSpyCommandLineArgs() { // Note: NO Skip(1) here because .Args property on SingleInstanceArguments does this for us return Environment.GetCommandLineArgs().AsEnumerable() .Select(CommandLineTools.FullyQualifyPath) .ToArray(); } /// /// Returns true if this application is not already started. /// Another instance is contacted via named pipe. /// /// If true, application will exit after informing another instance. /// API call failed. public static bool Attach(bool noAutoExit) { lock (_syncRoot) { var isFirstInstance = false; try { _mtxFirstInstance = new Mutex(initiallyOwned: true, @"Global\" + MutexName, out isFirstInstance); if (isFirstInstance == false) { //we need to contact previous instance var contentObject = new SingleInstanceArguments() { CommandLine = Environment.CommandLine, CommandLineArgs = GetILSpyCommandLineArgs(), }; var contentBytes = JsonSerializer.SerializeToUtf8Bytes(contentObject); using var clientPipe = new NamedPipeClientStream(".", MutexName, PipeDirection.Out, PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough); clientPipe.Connect(); clientPipe.Write(contentBytes, 0, contentBytes.Length); } else { //there is no application already running. _thread = new Thread(Run) { Name = typeof(SingleInstance).FullName, IsBackground = true }; _thread.Start(); } } catch (Exception ex) { Trace.TraceWarning(ex.Message + " {Medo.Application.SingleInstance}"); } if ((isFirstInstance == false) && (noAutoExit == false)) { Trace.TraceInformation("Exit due to another instance running." + " [" + nameof(SingleInstance) + "]"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Environment.Exit(unchecked((int)0x80004004)); // E_ABORT(0x80004004) } else { Environment.Exit(114); // EALREADY(114) } } return isFirstInstance; } } private static string? _mutexName; private static string MutexName { get { lock (_syncRoot) { if (_mutexName == null) { var assembly = Assembly.GetEntryAssembly(); var sbMutextName = new StringBuilder(); var assName = assembly?.GetName().Name; if (assName != null) { sbMutextName.Append(assName, 0, Math.Min(assName.Length, 31)); sbMutextName.Append('.'); } var sbHash = new StringBuilder(); sbHash.AppendLine(Environment.MachineName); sbHash.AppendLine(Environment.UserName); if (assembly != null) { sbHash.AppendLine(assembly.FullName); sbHash.AppendLine(assembly.Location); } else { var args = Environment.GetCommandLineArgs(); if (args.Length > 0) { sbHash.AppendLine(args[0]); } } foreach (var b in SHA256.HashData(Encoding.UTF8.GetBytes(sbHash.ToString()))) { if (sbMutextName.Length == 63) { sbMutextName.AppendFormat("{0:X1}", b >> 4); } // just take the first nubble if (sbMutextName.Length == 64) { break; } sbMutextName.AppendFormat("{0:X2}", b); } _mutexName = sbMutextName.ToString(); } return _mutexName; } } } /// /// Gets whether there is another instance running. /// It temporary creates mutex. /// public static bool IsOtherInstanceRunning { get { lock (_syncRoot) { if (_mtxFirstInstance != null) { return false; //no other instance is running } else { var tempInstance = new Mutex(true, MutexName, out var isFirstInstance); tempInstance.Close(); return (isFirstInstance == false); } } } } /// /// Occurs in first instance when new instance is detected. /// public static event EventHandler? NewInstanceDetected; /// /// Thread function. /// private static void Run() { using var serverPipe = new NamedPipeServerStream(MutexName, PipeDirection.In, maxNumberOfServerInstances: 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough); while (_mtxFirstInstance != null) { try { if (!serverPipe.IsConnected) { serverPipe.WaitForConnection(); } var contentObject = JsonSerializer.Deserialize(serverPipe); serverPipe.Disconnect(); if (contentObject != null) { NewInstanceDetected?.Invoke(null, new NewInstanceEventArgs( contentObject.CommandLine, contentObject.CommandLineArgs)); } } catch (Exception ex) { Trace.TraceWarning(ex.Message + " [" + nameof(SingleInstance) + "]"); Thread.Sleep(100); } } } [Serializable] private sealed record SingleInstanceArguments { // just a storage [JsonInclude] public required string CommandLine; [JsonInclude] public required string[] CommandLineArgs; } } /// /// Arguments for newly detected application instance. /// public sealed class NewInstanceEventArgs : EventArgs { /// /// Creates new instance. /// /// Command line. /// String array containing the command line arguments in the same format as Environment.GetCommandLineArgs. internal NewInstanceEventArgs(string commandLine, string[] commandLineArgs) { CommandLine = commandLine; _commandLineArgs = new string[commandLineArgs.Length]; Array.Copy(commandLineArgs, _commandLineArgs, _commandLineArgs.Length); } /// /// Gets the command line. /// public string CommandLine { get; } private readonly string[] _commandLineArgs; /// /// Returns a string array containing the command line arguments. /// public string[] GetCommandLineArgs() { var argCopy = new string[_commandLineArgs.Length]; Array.Copy(_commandLineArgs, argCopy, argCopy.Length); return argCopy; } /// /// Gets a string array containing the command line arguments without the name of exectuable. /// public string[] Args { get { var argCopy = new string[_commandLineArgs.Length - 1]; Array.Copy(_commandLineArgs, 1, argCopy, 0, argCopy.Length); return argCopy; } } } ================================================ FILE: ILSpy/AssemblyTree/AssemblyListPane.xaml ================================================ ================================================ FILE: ILSpy/AssemblyTree/AssemblyListPane.xaml.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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.Composition; using System.Windows; using System.Windows.Threading; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Wpf; using TomsToolbox.Wpf.Composition.AttributedModel; namespace ICSharpCode.ILSpy.AssemblyTree { /// /// Interaction logic for AssemblyListPane.xaml /// [DataTemplate(typeof(AssemblyTreeModel))] [NonShared] public partial class AssemblyListPane { public AssemblyListPane() { InitializeComponent(); ContextMenuProvider.Add(this); } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property == DataContextProperty) { if (e.NewValue is not AssemblyTreeModel model) return; model.SetActiveView(this); // If there is already a selected item in the model, we need to scroll it into view, so it can be selected in the UI. var selected = model.SelectedItem; if (selected != null) { this.BeginInvoke(DispatcherPriority.Background, () => { ScrollIntoView(selected); this.SelectedItem = selected; }); } } else if (e.Property == Pane.IsActiveProperty) { if (!true.Equals(e.NewValue)) return; if (SelectedItem is SharpTreeNode selectedItem) { // defer focusing, so it does not interfere with selection via mouse click this.BeginInvoke(() => { if (this.SelectedItem == selectedItem) FocusNode(selectedItem); }); } else { Focus(); } } } } } ================================================ FILE: ILSpy/AssemblyTree/AssemblyTreeModel.cs ================================================ // Copyright (c) 2019 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 // 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.Collections.Specialized; using System.ComponentModel; using System.Composition; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading.Tasks; using System.Windows; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Navigation; using System.Windows.Threading; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.Updates; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; using TomsToolbox.Essentials; using TomsToolbox.Wpf; #nullable enable namespace ICSharpCode.ILSpy.AssemblyTree { [ExportToolPane] [Shared] public class AssemblyTreeModel : ToolPaneModel { public const string PaneContentId = "assemblyListPane"; private AssemblyListPane? activeView; private AssemblyListTreeNode? assemblyListTreeNode; private readonly DispatcherThrottle refreshThrottle; private readonly NavigationHistory history = new(); private NavigationState? navigatingToState; private object? sourceOfReference; private readonly SettingsService settingsService; private readonly LanguageService languageService; private readonly IExportProvider exportProvider; private static Dispatcher UIThreadDispatcher => Application.Current.Dispatcher; public AssemblyTreeModel(SettingsService settingsService, LanguageService languageService, IExportProvider exportProvider) { this.settingsService = settingsService; this.languageService = languageService; this.exportProvider = exportProvider; Title = Resources.Assemblies; ContentId = PaneContentId; IsCloseable = false; ShortcutKey = new KeyGesture(Key.F6); MessageBus.Subscribers += JumpToReference; MessageBus.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e); MessageBus.Subscribers += ApplySessionSettings; MessageBus.Subscribers += ActiveTabPageChanged; MessageBus.Subscribers += (_, e) => history.RemoveAll(s => !DockWorkspace.TabPages.Contains(s.TabPage)); MessageBus.Subscribers += ResetLayout; MessageBus.Subscribers += (_, e) => NavigateTo(e.Request, e.InNewTabPage); MessageBus.Subscribers += (_, _) => { Initialize(); Show(); }; EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler((_, e) => NavigateTo(e))); refreshThrottle = new(DispatcherPriority.Background, RefreshInternal); AssemblyList = settingsService.CreateEmptyAssemblyList(); } private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (sender is SessionSettings sessionSettings) { switch (e.PropertyName) { case nameof(SessionSettings.ActiveAssemblyList): ShowAssemblyList(sessionSettings.ActiveAssemblyList); RefreshDecompiledView(); break; case nameof(SessionSettings.Theme): // update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change) DecompilerTextView.RegisterHighlighting(); RefreshDecompiledView(); break; case nameof(SessionSettings.CurrentCulture): MessageBox.Show(Resources.SettingsChangeRestartRequired, "ILSpy"); break; } } else if (sender is LanguageSettings) { switch (e.PropertyName) { case nameof(LanguageSettings.LanguageId) or nameof(LanguageSettings.LanguageVersionId): RefreshDecompiledView(); break; default: Refresh(); break; } } } public AssemblyList AssemblyList { get; private set; } private SharpTreeNode? root; public SharpTreeNode? Root { get => root; set => SetProperty(ref root, value); } public SharpTreeNode? SelectedItem { get => SelectedItems.FirstOrDefault(); set => SelectedItems = value is null ? [] : [value]; } private SharpTreeNode[] selectedItems = []; public SharpTreeNode[] SelectedItems { get => selectedItems; set { if (selectedItems.SequenceEqual(value)) return; var oldSelection = selectedItems; selectedItems = value; OnPropertyChanged(); TreeView_SelectionChanged(oldSelection, selectedItems); } } public string[]? SelectedPath => GetPathForNode(SelectedItem); private readonly List commandLineLoadedAssemblies = []; private bool HandleCommandLineArguments(CommandLineArguments args) { LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false); if (args.Language != null) languageService.Language = languageService.GetLanguage(args.Language); return true; } /// /// Called on startup or when passed arguments via WndProc from a second instance. /// In the format case, updateSettings is non-null; in the latter it is null. /// private async Task HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, UpdateSettings? updateSettings = null) { var sessionSettings = settingsService.SessionSettings; var relevantAssemblies = commandLineLoadedAssemblies.ToList(); commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore await NavigateOnLaunch(args.NavigateTo, sessionSettings.ActiveTreeViewPath, updateSettings, relevantAssemblies); if (args.Search != null) { MessageBus.Send(this, new ShowSearchPageEventArgs(args.Search)); } } public async Task HandleSingleInstanceCommandLineArguments(string[] args) { var cmdArgs = CommandLineArguments.Create(args); await UIThreadDispatcher.InvokeAsync(async () => { if (!HandleCommandLineArguments(cmdArgs)) return; var window = Application.Current.MainWindow; if (!cmdArgs.NoActivate && window is { WindowState: WindowState.Minimized }) { window.WindowState = WindowState.Normal; } await HandleCommandLineArgumentsAfterShowList(cmdArgs); }); } private async Task NavigateOnLaunch(string? navigateTo, string[]? activeTreeViewPath, UpdateSettings? updateSettings, List relevantAssemblies) { var initialSelection = SelectedItem; if (navigateTo != null) { bool found = false; if (navigateTo.StartsWith("N:", StringComparison.Ordinal)) { string namespaceName = navigateTo.Substring(2); foreach (LoadedAssembly asm in relevantAssemblies) { var asmNode = assemblyListTreeNode?.FindAssemblyNode(asm); if (asmNode != null) { // FindNamespaceNode() blocks the UI if the assembly is not yet loaded, // so use an async wait instead. await asm.GetMetadataFileAsync().Catch(_ => { }); NamespaceTreeNode nsNode = asmNode.FindNamespaceNode(namespaceName); if (nsNode != null) { found = true; if (SelectedItem == initialSelection) { SelectNode(nsNode); } break; } } } } else if (navigateTo == "none") { // Don't navigate anywhere; start empty. // Used by ILSpy VS addin, it'll send us the real location to navigate to via IPC. found = true; } else { IEntity? mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); // Make sure we wait for assemblies being loaded... // BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal await UIThreadDispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal); if (mr is { ParentModule.MetadataFile: not null }) { found = true; if (SelectedItem == initialSelection) { await JumpToReferenceAsync(mr, null); } } } if (!found && SelectedItem == initialSelection) { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.Write($"Cannot find '{navigateTo}' in command line specified assemblies."); DockWorkspace.ShowText(output); } } else if (relevantAssemblies.Count == 1) { // NavigateTo == null and an assembly was given on the command-line: // Select the newly loaded assembly var asmNode = assemblyListTreeNode?.FindAssemblyNode(relevantAssemblies[0]); if (asmNode != null && SelectedItem == initialSelection) { SelectNode(asmNode); } } else if (updateSettings != null) { SharpTreeNode? node = null; if (activeTreeViewPath?.Length > 0) { foreach (var asm in AssemblyList.GetAssemblies()) { if (asm.FileName == activeTreeViewPath[0]) { // FindNodeByPath() blocks the UI if the assembly is not yet loaded, // so use an async wait instead. await asm.GetMetadataFileAsync().Catch(_ => { }); } } node = FindNodeByPath(activeTreeViewPath, true); } if (SelectedItem == initialSelection) { if (node != null) { SelectNode(node); // only if not showing the about page, perform the update check: MessageBus.Send(this, new CheckIfUpdateAvailableEventArgs()); } else { MessageBus.Send(this, new ShowAboutPageEventArgs(DockWorkspace.ActiveTabPage)); } } } } public static IEntity? FindEntityInRelevantAssemblies(string navigateTo, IEnumerable relevantAssemblies) { ITypeReference typeRef; IMemberReference? memberRef = null; if (navigateTo.StartsWith("T:", StringComparison.Ordinal)) { typeRef = IdStringProvider.ParseTypeName(navigateTo); } else { memberRef = IdStringProvider.ParseMemberIdString(navigateTo); typeRef = memberRef.DeclaringTypeReference; } foreach (LoadedAssembly asm in relevantAssemblies.ToList()) { var module = asm.GetMetadataFileOrNull(); if (module != null && CanResolveTypeInPEFile(module, typeRef, out var typeHandle)) { ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) : new SimpleCompilation((PEFile)module, MinimalCorlib.Instance); return memberRef == null ? typeRef.Resolve(new SimpleTypeResolveContext(compilation)) as ITypeDefinition : memberRef.Resolve(new SimpleTypeResolveContext(compilation)); } } return null; } private static bool CanResolveTypeInPEFile(MetadataFile module, ITypeReference typeRef, out EntityHandle typeHandle) { // We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition. if (module.IsReferenceAssembly()) { typeHandle = default; return false; } switch (typeRef) { case GetPotentiallyNestedClassTypeReference topLevelType: typeHandle = topLevelType.ResolveInPEFile(module); return !typeHandle.IsNil; case NestedTypeReference nestedType: if (!CanResolveTypeInPEFile(module, nestedType.DeclaringTypeReference, out typeHandle)) return false; if (typeHandle.Kind == HandleKind.ExportedType) return true; var typeDef = module.Metadata.GetTypeDefinition((TypeDefinitionHandle)typeHandle); typeHandle = typeDef.GetNestedTypes().FirstOrDefault(t => { var td = module.Metadata.GetTypeDefinition(t); var typeName = ReflectionHelper.SplitTypeParameterCountFromReflectionName(module.Metadata.GetString(td.Name), out int typeParameterCount); return nestedType.AdditionalTypeParameterCount == typeParameterCount && nestedType.Name == typeName; }); return !typeHandle.IsNil; default: typeHandle = default; return false; } } public void Initialize() { AssemblyList = settingsService.LoadInitialAssemblyList(); HandleCommandLineArguments(App.CommandLineArguments); var loadPreviousAssemblies = settingsService.MiscSettings.LoadPreviousAssemblies; if (AssemblyList.GetAssemblies().Length == 0 && AssemblyList.ListName == AssemblyListManager.DefaultListName && loadPreviousAssemblies) { LoadInitialAssemblies(AssemblyList); } ShowAssemblyList(AssemblyList); var sessionSettings = settingsService.SessionSettings; if (sessionSettings.ActiveAutoLoadedAssembly != null && File.Exists(sessionSettings.ActiveAutoLoadedAssembly)) { AssemblyList.Open(sessionSettings.ActiveAutoLoadedAssembly, true); } UIThreadDispatcher.BeginInvoke(DispatcherPriority.Loaded, OpenAssemblies); } private async Task OpenAssemblies() { await HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments, settingsService.GetSettings()); if (FormatExceptions(App.StartupExceptions.ToArray(), out var output)) { output.Title = "Startup errors"; DockWorkspace.AddTabPage(); DockWorkspace.ShowText(output); } } private static bool FormatExceptions(App.ExceptionData[] exceptions, [NotNullWhen(true)] out AvalonEditTextOutput? output) { output = null; var result = exceptions.FormatExceptions(); if (result.IsNullOrEmpty()) return false; output = new(); output.Write(result); return true; } private void ShowAssemblyList(string name) { AssemblyList list = settingsService.AssemblyListManager.LoadList(name); //Only load a new list when it is a different one if (list.ListName != AssemblyList.ListName) { ShowAssemblyList(list); SelectNode(Root?.Children.FirstOrDefault()); } } private void ShowAssemblyList(AssemblyList assemblyList) { history.Clear(); AssemblyList.CollectionChanged -= assemblyList_CollectionChanged; AssemblyList = assemblyList; assemblyList.CollectionChanged += assemblyList_CollectionChanged; assemblyListTreeNode = new(assemblyList) { Select = x => SelectNode(x) }; Root = assemblyListTreeNode; var mainWindow = Application.Current?.MainWindow; if (mainWindow == null) return; if (assemblyList.ListName == AssemblyListManager.DefaultListName) #if DEBUG mainWindow.Title = $"ILSpy {DecompilerVersionInfo.FullVersion}"; #else mainWindow.Title = "ILSpy"; #endif else #if DEBUG mainWindow.Title = string.Format(settingsService.MiscSettings.AllowMultipleInstances ? "{1} - {0}" : "{0} - {1}", $"ILSpy {DecompilerVersionInfo.FullVersion}", assemblyList.ListName); #else mainWindow.Title = string.Format(settingsService.MiscSettings.AllowMultipleInstances ? "{1} - {0}" : "{0} - {1}", "ILSpy", assemblyList.ListName); #endif } private void assemblyList_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Reset) { history.RemoveAll(_ => true); } if (e.OldItems != null) { var oldAssemblies = new HashSet(e.OldItems.Cast()); history.RemoveAll(n => n.TreeNodes.Any( nd => nd.AncestorsAndSelf().OfType().Any( a => oldAssemblies.Contains(a.LoadedAssembly)))); } MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(e)); } private static void LoadInitialAssemblies(AssemblyList assemblyList) { // Called when loading an empty assembly list; so that // the user can see something initially. System.Reflection.Assembly[] initialAssemblies = { typeof(object).Assembly, typeof(Uri).Assembly, typeof(System.Linq.Enumerable).Assembly, typeof(System.Xml.XmlDocument).Assembly, typeof(System.Windows.Markup.MarkupExtension).Assembly, typeof(System.Windows.Rect).Assembly, typeof(System.Windows.UIElement).Assembly, typeof(System.Windows.FrameworkElement).Assembly }; foreach (System.Reflection.Assembly asm in initialAssemblies) assemblyList.OpenAssembly(asm.Location); } public AssemblyTreeNode? FindAssemblyNode(LoadedAssembly asm) { return assemblyListTreeNode?.FindAssemblyNode(asm); } #region Node Selection public void SelectNode(SharpTreeNode? node, bool inNewTabPage = false) { if (node == null) return; if (node.AncestorsAndSelf().Any(item => item.IsHidden)) { MessageBox.Show(Resources.NavigationFailed, "ILSpy", MessageBoxButton.OK, MessageBoxImage.Exclamation); return; } if (inNewTabPage) { DockWorkspace.AddTabPage(); SelectedItem = null; } if (SelectedItem == node) { UIThreadDispatcher.BeginInvoke(RefreshDecompiledView); } else { activeView?.ScrollIntoView(node); SelectedItem = node; UIThreadDispatcher.BeginInvoke(DispatcherPriority.Background, () => { activeView?.ScrollIntoView(node); }); } } public void SelectNodes(IEnumerable nodes) { // Ensure nodes exist var nodesList = nodes.Select(n => FindNodeByPath(GetPathForNode(n), true)) .ExceptNullItems() .ToArray(); if (!nodesList.Any() || nodesList.Any(n => n.AncestorsAndSelf().Any(a => a.IsHidden))) { return; } foreach (var node in nodesList) { activeView?.ScrollIntoView(node); } SelectedItems = nodesList.ToArray(); } /// /// Retrieves a node using the .ToString() representations of its ancestors. /// public SharpTreeNode? FindNodeByPath(string[]? path, bool returnBestMatch) { if (path == null) return null; var node = Root; var bestMatch = node; foreach (var element in path) { if (node == null) break; bestMatch = node; node.EnsureLazyChildren(); if (node is ILSpyTreeNode ilSpyTreeNode) ilSpyTreeNode.EnsureChildrenFiltered(); node = node.Children.FirstOrDefault(c => c.ToString() == element); } return returnBestMatch ? node ?? bestMatch : node; } /// /// Gets the .ToString() representation of the node's ancestors. /// public static string[]? GetPathForNode(SharpTreeNode? node) { if (node == null) return null; List path = new List(); while (node.Parent != null) { path.Add(node.ToString()!); node = node.Parent; } path.Reverse(); return path.ToArray(); } public ILSpyTreeNode? FindTreeNode(object? reference) { if (assemblyListTreeNode == null) return null; switch (reference) { case LoadedAssembly lasm: return assemblyListTreeNode.FindAssemblyNode(lasm); case MetadataFile asm: return assemblyListTreeNode.FindAssemblyNode(asm); case Resource res: return assemblyListTreeNode.FindResourceNode(res); case ValueTuple resName: return assemblyListTreeNode.FindResourceNode(resName.Item1, resName.Item2); case ITypeDefinition type: return assemblyListTreeNode.FindTypeNode(type); case IField fd: return assemblyListTreeNode.FindFieldNode(fd); case IMethod md: return assemblyListTreeNode.FindMethodNode(md); case IProperty pd: return assemblyListTreeNode.FindPropertyNode(pd); case IEvent ed: return assemblyListTreeNode.FindEventNode(ed); case INamespace nd: return assemblyListTreeNode.FindNamespaceNode(nd); default: return null; } } private void JumpToReference(object? sender, NavigateToReferenceEventArgs e) { JumpToReferenceAsync(e.Reference, e.Source, e.InNewTabPage).HandleExceptions(); IsActive = true; } /// /// Jumps to the specified reference. /// /// /// Returns a task that will signal completion when the decompilation of the jump target has finished. /// The task will be marked as canceled if the decompilation is canceled. /// private Task JumpToReferenceAsync(object? reference, object? source, bool inNewTabPage = false) { this.sourceOfReference = source; var decompilationTask = Task.CompletedTask; switch (reference) { case Decompiler.Disassembler.OpCodeInfo opCode: GlobalUtils.OpenLink(opCode.Link); break; case EntityReference unresolvedEntity: string protocol = unresolvedEntity.Protocol; var file = unresolvedEntity.ResolveAssembly(AssemblyList); if (file == null) { break; } if (protocol != "decompile") { foreach (var handler in exportProvider.GetExportedValues()) { var node = handler.Resolve(protocol, file, unresolvedEntity.Handle, out bool newTabPage); if (node != null) { SelectNode(node, newTabPage || inNewTabPage); return decompilationTask; } } } var possibleToken = MetadataTokenHelpers.TryAsEntityHandle(MetadataTokens.GetToken(unresolvedEntity.Handle)); if (possibleToken != null) { var typeSystem = new DecompilerTypeSystem(file, file.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached); reference = typeSystem.MainModule.ResolveEntity(possibleToken.Value); goto default; } break; default: var treeNode = FindTreeNode(reference); if (treeNode != null) SelectNode(treeNode, inNewTabPage); break; } return decompilationTask; } #endregion private void LoadAssemblies(IEnumerable fileNames, List? loadedAssemblies = null, bool focusNode = true) { using (Keyboard.FocusedElement.PreserveFocus(!focusNode)) { AssemblyTreeNode? lastNode = null; var assemblyList = AssemblyList; foreach (string file in fileNames) { var assembly = assemblyList.OpenAssembly(file); if (loadedAssemblies != null) { loadedAssemblies.Add(assembly); } else { var node = assemblyListTreeNode?.FindAssemblyNode(assembly); if (node != null && focusNode) { lastNode = node; activeView?.ScrollIntoView(node); SelectedItems = [.. SelectedItems, node]; } } } if (focusNode && lastNode != null) { activeView?.FocusNode(lastNode); } } } #region Decompile (TreeView_SelectionChanged) private void TreeView_SelectionChanged(SharpTreeNode[] oldSelection, SharpTreeNode[] newSelection) { var activeTabPage = DockWorkspace.ActiveTabPage; ViewState? oldState = activeTabPage.GetState(); ViewState? newState; if (navigatingToState == null) { if (oldState != null) { history.UpdateCurrent(new NavigationState(activeTabPage, oldState)); } newState = new ViewState { DecompiledNodes = [.. newSelection.Cast()] }; } else { newState = navigatingToState.ViewState; } if (newSelection.Length == 0) { // To cancel any pending decompilation requests and show an empty tab DecompileSelectedNodes(newState); } else { var delayDecompilationRequestDueToContextMenu = Mouse.RightButton == MouseButtonState.Pressed; if (!delayDecompilationRequestDueToContextMenu) { var previousNodes = oldState?.DecompiledNodes ?.Select(n => FindNodeByPath(GetPathForNode(n), true)) .ExceptNullItems() .ToArray() ?? []; if (!previousNodes.SequenceEqual(SelectedItems)) { DecompileSelectedNodes(newState); } } else { // ensure that we are only connected once to the event, else we might get multiple notifications ContextMenuProvider.ContextMenuClosed -= ContextMenuClosed; ContextMenuProvider.ContextMenuClosed += ContextMenuClosed; } } MessageBus.Send(this, new AssemblyTreeSelectionChangedEventArgs()); return; void ContextMenuClosed(object? sender, EventArgs e) { ContextMenuProvider.ContextMenuClosed -= ContextMenuClosed; UIThreadDispatcher.BeginInvoke(DispatcherPriority.Background, () => { if (Mouse.RightButton != MouseButtonState.Pressed) { RefreshDecompiledView(); } }); } } public void DecompileSelectedNodes(ViewState? newState = null) { object? source = this.sourceOfReference; this.sourceOfReference = null; var activeTabPage = DockWorkspace.ActiveTabPage; if (activeTabPage.FrozenContent) { activeTabPage = DockWorkspace.AddTabPage(); } activeTabPage.SupportsLanguageSwitching = true; if (newState != null && navigatingToState == null) { history.Record(new NavigationState(activeTabPage, newState)); } if (SelectedItems.Length == 1) { if (SelectedItem is ILSpyTreeNode node && node.View(activeTabPage)) return; } if (newState?.ViewedUri != null) { NavigateTo(new(newState.ViewedUri, null)); return; } var options = activeTabPage.CreateDecompilationOptions(); options.TextViewState = newState as DecompilerTextViewState; activeTabPage.ShowTextViewAsync(textView => { return textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, source, options); }); } public void RefreshDecompiledView() { DecompileSelectedNodes(DockWorkspace.ActiveTabPage.GetState() as DecompilerTextViewState); } public Language CurrentLanguage => languageService.Language; public LanguageVersion? CurrentLanguageVersion => languageService.LanguageVersion; public IEnumerable SelectedNodes => GetTopLevelSelection().OfType(); #endregion public void NavigateHistory(bool forward, NavigationState? toState = null) { try { TabPageModel tabPage = DockWorkspace.ActiveTabPage; var currentState = tabPage.GetState(); if (currentState != null) history.UpdateCurrent(new NavigationState(tabPage, currentState)); NavigationState newState; do { newState = forward ? history.GoForward() : history.GoBack(); } while (newState != null && toState != null && toState != newState); if (newState == null) return; navigatingToState = newState; TabPageModel activeTabPage = newState.TabPage; Debug.Assert(DockWorkspace.TabPages.Contains(activeTabPage)); DockWorkspace.ActiveTabPage = activeTabPage; if (newState.TreeNodes.Any()) { SelectNodes(newState.TreeNodes); } else if (newState.ViewState.ViewedUri != null) { NavigateTo(new(newState.ViewState.ViewedUri, null)); } } finally { navigatingToState = null; } } public NavigationState[] GetNavigateHistory(bool forward) => forward ? history.ForwardList : history.BackList; public bool CanNavigateBack => history.CanNavigateBack; public bool CanNavigateForward => history.CanNavigateForward; private void NavigateTo(RequestNavigateEventArgs e, bool inNewTabPage = false) { if (e.Uri.Scheme != "resource") { return; } TabPageModel tabPage = DockWorkspace.ActiveTabPage; ViewState? oldState = tabPage.GetState(); ViewState? newState; if (navigatingToState == null) { if (oldState != null) { history.UpdateCurrent(new NavigationState(tabPage, oldState)); } newState = new ViewState { ViewedUri = e.Uri }; if (inNewTabPage) { tabPage = DockWorkspace.AddTabPage(); } } else { newState = navigatingToState.ViewState; tabPage = DockWorkspace.ActiveTabPage = navigatingToState.TabPage; } bool needsNewNavigationEntry = !inNewTabPage && selectedItems?.Length == 0; UnselectAll(); if (e.Uri.Host == "aboutpage") { MessageBus.Send(this, new ShowAboutPageEventArgs(DockWorkspace.ActiveTabPage)); e.Handled = true; } else { AvalonEditTextOutput output = new AvalonEditTextOutput { Address = e.Uri, Title = e.Uri.AbsolutePath, EnableHyperlinks = true }; using (Stream? s = typeof(App).Assembly.GetManifestResourceStream(typeof(App), e.Uri.AbsolutePath)) { if (s != null) { using StreamReader r = new StreamReader(s); string? line; while ((line = r.ReadLine()) != null) { output.Write(line); output.WriteLine(); } } } DockWorkspace.ShowText(output); e.Handled = true; } if (navigatingToState == null) { // the call to UnselectAll() above already creates a new navigation entry, // we just need to make sure it contains something useful. if (!needsNewNavigationEntry) { history.UpdateCurrent(new NavigationState(tabPage, tabPage.GetState())); } else { history.Record(new NavigationState(tabPage, tabPage.GetState())); } } } public void Refresh() { refreshThrottle.Tick(); } private void RefreshInternal() { using (Keyboard.FocusedElement.PreserveFocus()) { var path = GetPathForNode(SelectedItem); ShowAssemblyList(settingsService.AssemblyListManager.LoadList(AssemblyList.ListName)); SelectNode(FindNodeByPath(path, true), inNewTabPage: false); RefreshDecompiledView(); } } private void UnselectAll() { SelectedItems = []; } private IEnumerable GetTopLevelSelection() { var selection = this.SelectedItems; var selectionHash = new HashSet(selection); return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a))); } public void SetActiveView(AssemblyListPane activeView) { this.activeView = activeView; } public void SortAssemblyList() { using (activeView?.LockUpdates()) { AssemblyList.Sort(AssemblyComparer.Instance); } } private class AssemblyComparer : IComparer { public static readonly AssemblyComparer Instance = new(); int IComparer.Compare(LoadedAssembly? x, LoadedAssembly? y) { return string.Compare(x?.ShortName, y?.ShortName, StringComparison.CurrentCulture); } } public void CollapseAll() { using (activeView?.LockUpdates()) { CollapseChildren(Root); } } private static void CollapseChildren(SharpTreeNode? node) { if (node is null) return; foreach (var child in node.Children) { if (!child.IsExpanded) continue; CollapseChildren(child); child.IsExpanded = false; } } public void OpenFiles(string[] fileNames, bool focusNode = true) { if (fileNames == null) throw new ArgumentNullException(nameof(fileNames)); if (focusNode) UnselectAll(); LoadAssemblies(fileNames, focusNode: focusNode); } private void ApplySessionSettings(object? sender, ApplySessionSettingsEventArgs e) { var settings = e.SessionSettings; settings.ActiveAssemblyList = AssemblyList.ListName; settings.ActiveTreeViewPath = SelectedPath; settings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(SelectedItem); } private static string? GetAutoLoadedAssemblyNode(SharpTreeNode? node) { var assemblyTreeNode = node? .AncestorsAndSelf() .OfType() .FirstOrDefault(); var loadedAssembly = assemblyTreeNode?.LoadedAssembly; return loadedAssembly is not { IsLoaded: true, IsAutoLoaded: true } ? null : loadedAssembly.FileName; } private void ActiveTabPageChanged(object? sender, ActiveTabPageChangedEventArgs e) { if (e.ViewState is not { } state) return; if (state.DecompiledNodes != null) { SelectNodes(state.DecompiledNodes); } else { NavigateTo(new(state.ViewedUri, null)); } } private void ResetLayout(object? sender, ResetLayoutEventArgs e) { RefreshDecompiledView(); } } } ================================================ FILE: ILSpy/AvalonEdit/ITextMarker.cs ================================================ // Copyright (c) 2014 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 // 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.Windows; using System.Windows.Media; namespace ICSharpCode.ILSpy.AvalonEdit { /// /// Represents a text marker. /// public interface ITextMarker { /// /// Gets the start offset of the marked text region. /// int StartOffset { get; } /// /// Gets the end offset of the marked text region. /// int EndOffset { get; } /// /// Gets the length of the marked region. /// int Length { get; } /// /// Deletes the text marker. /// void Delete(); /// /// Gets whether the text marker was deleted. /// bool IsDeleted { get; } /// /// Event that occurs when the text marker is deleted. /// event EventHandler Deleted; /// /// Gets/Sets the background color. /// Color? BackgroundColor { get; set; } /// /// Gets/Sets the foreground color. /// Color? ForegroundColor { get; set; } /// /// Gets/Sets the font weight. /// FontWeight? FontWeight { get; set; } /// /// Gets/Sets the font style. /// FontStyle? FontStyle { get; set; } /// /// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers. /// TextMarkerTypes MarkerTypes { get; set; } /// /// Gets/Sets the color of the marker. /// Color MarkerColor { get; set; } /// /// Gets/Sets an object with additional data for this text marker. /// object Tag { get; set; } /// /// Gets/Sets an object that will be displayed as tooltip in the text editor. /// object ToolTip { get; set; } } [Flags] public enum TextMarkerTypes { /// /// Use no marker /// None = 0x0000, /// /// Use squiggly underline marker /// SquigglyUnderline = 0x001, /// /// Normal underline. /// NormalUnderline = 0x002, /// /// Dotted underline. /// DottedUnderline = 0x004, /// /// Horizontal line in the scroll bar. /// LineInScrollBar = 0x0100, /// /// Small triangle in the scroll bar, pointing to the right. /// ScrollBarRightTriangle = 0x0400, /// /// Small triangle in the scroll bar, pointing to the left. /// ScrollBarLeftTriangle = 0x0800, /// /// Small circle in the scroll bar. /// CircleInScrollBar = 0x1000 } public interface ITextMarkerService { /// /// Creates a new text marker. The text marker will be invisible at first, /// you need to set one of the Color properties to make it visible. /// ITextMarker Create(int startOffset, int length); /// /// Gets the list of text markers. /// IEnumerable TextMarkers { get; } /// /// Removes the specified text marker. /// void Remove(ITextMarker marker); /// /// Removes all text markers that match the condition. /// void RemoveAll(Predicate predicate); /// /// Finds all text markers at the specified offset. /// IEnumerable GetMarkersAtOffset(int offset); } } ================================================ FILE: ILSpy/AvalonEdit/TextMarkerService.cs ================================================ // Copyright (c) 2014 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 // 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; using System.Windows; using System.Windows.Media; using System.Windows.Threading; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Rendering; namespace ICSharpCode.ILSpy.AvalonEdit { using TextView = ICSharpCode.AvalonEdit.Rendering.TextView; /// /// Handles the text markers for a code editor. /// sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService { TextSegmentCollection markers; TextView textView; public TextMarkerService(TextView textView) { if (textView == null) throw new ArgumentNullException(nameof(textView)); this.textView = textView; textView.DocumentChanged += OnDocumentChanged; OnDocumentChanged(null, null); } void OnDocumentChanged(object sender, EventArgs e) { if (textView.Document != null) markers = new TextSegmentCollection(textView.Document); else markers = null; } #region ITextMarkerService public ITextMarker Create(int startOffset, int length) { if (markers == null) throw new InvalidOperationException("Cannot create a marker when not attached to a document"); int textLength = textView.Document.TextLength; if (startOffset < 0 || startOffset > textLength) throw new ArgumentOutOfRangeException(nameof(startOffset), startOffset, "Value must be between 0 and " + textLength); if (length < 0 || startOffset + length > textLength) throw new ArgumentOutOfRangeException(nameof(length), length, "length must not be negative and startOffset+length must not be after the end of the document"); TextMarker m = new TextMarker(this, startOffset, length); markers.Add(m); // no need to mark segment for redraw: the text marker is invisible until a property is set return m; } public IEnumerable GetMarkersAtOffset(int offset) { if (markers == null) return Enumerable.Empty(); else return markers.FindSegmentsContaining(offset); } public IEnumerable TextMarkers { get { return markers ?? Enumerable.Empty(); } } public void RemoveAll(Predicate predicate) { if (predicate == null) throw new ArgumentNullException(nameof(predicate)); if (markers != null) { foreach (TextMarker m in markers.ToArray()) { if (predicate(m)) Remove(m); } } } public void Remove(ITextMarker marker) { if (marker == null) throw new ArgumentNullException(nameof(marker)); TextMarker m = marker as TextMarker; if (markers != null && markers.Remove(m)) { Redraw(m); m.OnDeleted(); } } /// /// Redraws the specified text segment. /// internal void Redraw(ISegment segment) { textView.Redraw(segment, DispatcherPriority.Normal); RedrawRequested?.Invoke(this, EventArgs.Empty); } public event EventHandler RedrawRequested; #endregion #region DocumentColorizingTransformer protected override void ColorizeLine(DocumentLine line) { if (markers == null) return; int lineStart = line.Offset; int lineEnd = lineStart + line.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length)) { Brush foregroundBrush = null; if (marker.ForegroundColor != null) { foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value); foregroundBrush.Freeze(); } ChangeLinePart( Math.Max(marker.StartOffset, lineStart), Math.Min(marker.EndOffset, lineEnd), element => { if (foregroundBrush != null) { element.TextRunProperties.SetForegroundBrush(foregroundBrush); } Typeface tf = element.TextRunProperties.Typeface; element.TextRunProperties.SetTypeface(new Typeface( tf.FontFamily, marker.FontStyle ?? tf.Style, marker.FontWeight ?? tf.Weight, tf.Stretch )); } ); } } #endregion #region IBackgroundRenderer public KnownLayer Layer { get { // draw behind selection return KnownLayer.Selection; } } public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext) { if (textView == null) throw new ArgumentNullException(nameof(textView)); if (drawingContext == null) throw new ArgumentNullException(nameof(drawingContext)); if (markers == null || !textView.VisualLinesValid) return; var visualLines = textView.VisualLines; if (visualLines.Count == 0) return; int viewStart = visualLines.First().FirstDocumentLine.Offset; int viewEnd = visualLines.Last().LastDocumentLine.EndOffset; foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) { if (marker.BackgroundColor != null) { BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); geoBuilder.AlignToWholePixels = true; geoBuilder.CornerRadius = 3; geoBuilder.AddSegment(textView, marker); Geometry geometry = geoBuilder.CreateGeometry(); if (geometry != null) { Color color = marker.BackgroundColor.Value; SolidColorBrush brush = new SolidColorBrush(color); brush.Freeze(); drawingContext.DrawGeometry(brush, null, geometry); } } var underlineMarkerTypes = TextMarkerTypes.SquigglyUnderline | TextMarkerTypes.NormalUnderline | TextMarkerTypes.DottedUnderline; if ((marker.MarkerTypes & underlineMarkerTypes) != 0) { foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) { Point startPoint = r.BottomLeft; Point endPoint = r.BottomRight; Brush usedBrush = new SolidColorBrush(marker.MarkerColor); usedBrush.Freeze(); if ((marker.MarkerTypes & TextMarkerTypes.SquigglyUnderline) != 0) { double offset = 2.5; int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4); StreamGeometry geometry = new StreamGeometry(); using (StreamGeometryContext ctx = geometry.Open()) { ctx.BeginFigure(startPoint, false, false); ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false); } geometry.Freeze(); Pen usedPen = new Pen(usedBrush, 1); usedPen.Freeze(); drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry); } if ((marker.MarkerTypes & TextMarkerTypes.NormalUnderline) != 0) { Pen usedPen = new Pen(usedBrush, 1); usedPen.Freeze(); drawingContext.DrawLine(usedPen, startPoint, endPoint); } if ((marker.MarkerTypes & TextMarkerTypes.DottedUnderline) != 0) { Pen usedPen = new Pen(usedBrush, 1); usedPen.DashStyle = DashStyles.Dot; usedPen.Freeze(); drawingContext.DrawLine(usedPen, startPoint, endPoint); } } } } } IEnumerable CreatePoints(Point start, Point end, double offset, int count) { for (int i = 0; i < count; i++) yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0)); } #endregion } sealed class TextMarker : TextSegment, ITextMarker { readonly TextMarkerService service; public TextMarker(TextMarkerService service, int startOffset, int length) { if (service == null) throw new ArgumentNullException(nameof(service)); this.service = service; this.StartOffset = startOffset; this.Length = length; this.markerTypes = TextMarkerTypes.None; } public event EventHandler Deleted; public bool IsDeleted { get { return !this.IsConnectedToCollection; } } public void Delete() { service.Remove(this); } internal void OnDeleted() { Deleted?.Invoke(this, EventArgs.Empty); } void Redraw() { service.Redraw(this); } Color? backgroundColor; public Color? BackgroundColor { get { return backgroundColor; } set { if (backgroundColor != value) { backgroundColor = value; Redraw(); } } } Color? foregroundColor; public Color? ForegroundColor { get { return foregroundColor; } set { if (foregroundColor != value) { foregroundColor = value; Redraw(); } } } FontWeight? fontWeight; public FontWeight? FontWeight { get { return fontWeight; } set { if (fontWeight != value) { fontWeight = value; Redraw(); } } } FontStyle? fontStyle; public FontStyle? FontStyle { get { return fontStyle; } set { if (fontStyle != value) { fontStyle = value; Redraw(); } } } public object Tag { get; set; } TextMarkerTypes markerTypes; public TextMarkerTypes MarkerTypes { get { return markerTypes; } set { if (markerTypes != value) { markerTypes = value; Redraw(); } } } Color markerColor; public Color MarkerColor { get { return markerColor; } set { if (markerColor != value) { markerColor = value; Redraw(); } } } public object ToolTip { get; set; } } } ================================================ FILE: ILSpy/Commands/BrowseBackCommand.cs ================================================ // 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 // 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.Collections; using System.Composition; using System.Linq; using System.Windows.Input; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Back), ToolbarIcon = "Images/Back", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 0)] [Shared] sealed class BrowseBackCommand : CommandWrapper, IProvideParameterList { readonly AssemblyTreeModel assemblyTreeModel; public BrowseBackCommand(AssemblyTreeModel assemblyTreeModel) : base(NavigationCommands.BrowseBack) { this.assemblyTreeModel = assemblyTreeModel; } protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e) { base.OnCanExecute(sender, e); e.Handled = true; e.CanExecute = assemblyTreeModel.CanNavigateBack; } protected override void OnExecute(object sender, ExecutedRoutedEventArgs e) { if (assemblyTreeModel.CanNavigateBack) { e.Handled = true; assemblyTreeModel.NavigateHistory(false, e.Parameter as NavigationState); } } public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(false).Reverse(); public object GetParameterText(object parameter) { return (parameter as NavigationState)?.NavigationText; } } } ================================================ FILE: ILSpy/Commands/BrowseForwardCommand.cs ================================================ // 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 // 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.Collections; using System.Composition; using System.Linq; using System.Windows.Input; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Forward), ToolbarIcon = "Images/Forward", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 1)] [Shared] sealed class BrowseForwardCommand : CommandWrapper, IProvideParameterList { private readonly AssemblyTreeModel assemblyTreeModel; public BrowseForwardCommand(AssemblyTreeModel assemblyTreeModel) : base(NavigationCommands.BrowseForward) { this.assemblyTreeModel = assemblyTreeModel; } protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e) { base.OnCanExecute(sender, e); e.Handled = true; e.CanExecute = assemblyTreeModel.CanNavigateForward; } protected override void OnExecute(object sender, ExecutedRoutedEventArgs e) { if (assemblyTreeModel.CanNavigateForward) { e.Handled = true; assemblyTreeModel.NavigateHistory(true, e.Parameter as NavigationState); } } public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(true).Reverse(); public object GetParameterText(object parameter) { return (parameter as NavigationState)?.NavigationText; } } } ================================================ FILE: ILSpy/Commands/CheckForUpdatesCommand.cs ================================================ // 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 // 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.Composition; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._CheckUpdates), MenuOrder = 5000)] [Shared] sealed class CheckForUpdatesCommand : SimpleCommand { public override void Execute(object parameter) { MessageBus.Send(this, new CheckIfUpdateAvailableEventArgs(notify: true)); } } } ================================================ FILE: ILSpy/Commands/CommandWrapper.cs ================================================ // 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 // 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.Windows; using System.Windows.Input; namespace ICSharpCode.ILSpy { abstract class CommandWrapper : ICommand { private readonly ICommand wrappedCommand; protected CommandWrapper(ICommand wrappedCommand) { this.wrappedCommand = wrappedCommand; Application.Current.MainWindow?.CommandBindings.Add(new CommandBinding(wrappedCommand, OnExecute, OnCanExecute)); } public static ICommand Unwrap(ICommand command) { if (command is CommandWrapper w) return w.wrappedCommand; return command; } public event EventHandler CanExecuteChanged { add { wrappedCommand.CanExecuteChanged += value; } remove { wrappedCommand.CanExecuteChanged -= value; } } public void Execute(object parameter) { wrappedCommand.Execute(parameter); } public bool CanExecute(object parameter) { return wrappedCommand.CanExecute(parameter); } protected abstract void OnExecute(object sender, ExecutedRoutedEventArgs e); protected virtual void OnCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } } } ================================================ FILE: ILSpy/Commands/CompareContextMenuEntry.cs ================================================ // Copyright (c) 2025 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 // 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.Composition; using System.Threading.Tasks; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpy.Views; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = "Compare...", Order = 9999)] [Shared] internal sealed class CompareContextMenuEntry(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { var left = ((AssemblyTreeNode)context.SelectedTreeNodes[0]).LoadedAssembly; var right = ((AssemblyTreeNode)context.SelectedTreeNodes[1]).LoadedAssembly; var tabPage = dockWorkspace.AddTabPage(); CompareViewModel.Show(tabPage, left, right, assemblyTreeModel); } public bool IsEnabled(TextViewContext context) { return true; } public bool IsVisible(TextViewContext context) { return context.SelectedTreeNodes is [AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true }, AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true }]; } } } ================================================ FILE: ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs ================================================ // 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 // 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.Composition; using System.Windows; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.CopyName), Icon = "images/Copy", Order = 9999)] [Shared] public class CopyFullyQualifiedNameContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) { return GetMemberNodeFromContext(context) != null; } public bool IsEnabled(TextViewContext context) => true; public void Execute(TextViewContext context) { var member = GetMemberNodeFromContext(context)?.Member; if (member == null) return; Clipboard.SetText(member.ReflectionName); } private IMemberTreeNode GetMemberNodeFromContext(TextViewContext context) { return context.SelectedTreeNodes?.Length == 1 ? context.SelectedTreeNodes[0] as IMemberTreeNode : null; } } } ================================================ FILE: ILSpy/Commands/CreateDiagramContextMenuEntry.cs ================================================ // Copyright (c) 2024 Christoph Wille 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 // 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.Composition; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX.MermaidDiagrammer; using Microsoft.Win32; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntry(Header = nameof(Resources._CreateDiagram), Category = nameof(Resources.Save), Icon = "Images/Save")] [Shared] sealed class CreateDiagramContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; if (assembly == null) return; var selectedPath = SelectDestinationFolder(); if (string.IsNullOrEmpty(selectedPath)) return; dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new() { EnableHyperlinks = true }; Stopwatch stopwatch = Stopwatch.StartNew(); try { var command = new GenerateHtmlDiagrammer { Assembly = assembly.FileName, OutputFolder = selectedPath }; command.Run(); } catch (OperationCanceledException) { output.WriteLine(); output.WriteLine(Resources.GenerationWasCancelled); throw; } stopwatch.Stop(); output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1")); output.WriteLine(); output.WriteLine("Learn more: " + "https://github.com/icsharpcode/ILSpy/wiki/Diagramming#tips-for-using-the-html-diagrammer"); output.WriteLine(); var diagramHtml = Path.Combine(selectedPath, "index.html"); output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItem(diagramHtml); }); output.WriteLine(); return output; }, ct), Properties.Resources.CreatingDiagram).Then(dockWorkspace.ShowText).HandleExceptions(); return; } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { return context.SelectedTreeNodes?.Length == 1 && context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode tn && tn.LoadedAssembly.IsLoadedAsValidAssembly; } static string SelectDestinationFolder() { OpenFolderDialog dialog = new(); dialog.Multiselect = false; dialog.Title = "Select target folder"; if (dialog.ShowDialog() != true) { return null; } string selectedPath = Path.GetDirectoryName(dialog.FolderName); bool directoryNotEmpty; try { directoryNotEmpty = Directory.EnumerateFileSystemEntries(selectedPath).Any(); } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { MessageBox.Show( "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", "Target directory not accessible", MessageBoxButton.OK, MessageBoxImage.Error); return null; } return dialog.FolderName; } } } ================================================ FILE: ILSpy/Commands/DecompileAllCommand.cs ================================================ // 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 // 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. #if DEBUG using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Composition; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] [Shared] sealed class DecompileAllCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand { public override bool CanExecute(object parameter) { return System.IO.Directory.Exists("c:\\temp\\decompiled"); } public override void Execute(object parameter) { dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Parallel.ForEach( Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate (LoadedAssembly asm) { if (!asm.HasLoadError) { Stopwatch w = Stopwatch.StartNew(); Exception exception = null; using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs")) { try { var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions(); options.CancellationToken = ct; options.FullDecompilation = true; new CSharpLanguage().DecompileAssembly(asm, new PlainTextOutput(writer), options); } catch (Exception ex) { writer.WriteLine(ex.ToString()); exception = ex; } } lock (output) { output.Write(asm.ShortName + " - " + w.Elapsed); if (exception != null) { output.Write(" - "); output.Write(exception.GetType().Name); } output.WriteLine(); } } }); return output; }, ct)).Then(dockWorkspace.ShowText).HandleExceptions(); } } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile100x), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)] [Shared] sealed class Decompile100TimesCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : SimpleCommand { public override void Execute(object parameter) { const int numRuns = 100; var language = languageService.Language; var nodes = assemblyTreeModel.SelectedNodes.ToArray(); var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions(); dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { options.CancellationToken = ct; Stopwatch w = Stopwatch.StartNew(); for (int i = 0; i < numRuns; ++i) { foreach (var node in nodes) { node.Decompile(language, new PlainTextOutput(), options); } } w.Stop(); AvalonEditTextOutput output = new AvalonEditTextOutput(); double msPerRun = w.Elapsed.TotalMilliseconds / numRuns; output.Write($"Average time: {msPerRun.ToString("f1")}ms\n"); return output; }, ct)).Then(output => dockWorkspace.ShowText(output)).HandleExceptions(); } } } #endif ================================================ FILE: ILSpy/Commands/DecompileCommand.cs ================================================ // 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 // 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.Composition; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.Decompile), Order = 10)] [Shared] class DecompileCommand : IContextMenuEntry { public bool IsVisible(TextViewContext context) { if (context.SelectedTreeNodes == null) return context.Reference?.Reference is IEntity; return context.SelectedTreeNodes.Length == 1 && context.SelectedTreeNodes.All(n => n is IMemberTreeNode); } public bool IsEnabled(TextViewContext context) { if (context.SelectedTreeNodes == null) return context.Reference?.Reference is IEntity; foreach (IMemberTreeNode node in context.SelectedTreeNodes) { if (!IsValidReference(node.Member)) return false; } return true; } bool IsValidReference(object reference) { return reference is IEntity; } public void Execute(TextViewContext context) { IEntity selection = null; if (context.SelectedTreeNodes?[0] is IMemberTreeNode node) { selection = node.Member; } else if (context.Reference?.Reference is IEntity entity) { selection = entity; } if (selection != null) MessageBus.Send(this, new NavigateToReferenceEventArgs(selection)); } } } ================================================ FILE: ILSpy/Commands/DecompileInNewViewCommand.cs ================================================ // 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 // 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.Composition; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), InputGestureText = "MMB", Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] [Shared] internal sealed class DecompileInNewViewCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : IContextMenuEntry { public bool IsVisible(TextViewContext context) { return context.SelectedTreeNodes != null || context.Reference?.Reference is IEntity; } public bool IsEnabled(TextViewContext context) { return GetNodes(context).Any(); } public void Execute(TextViewContext context) { DecompileNodes(GetNodes(context).ToArray()); } IEnumerable GetNodes(TextViewContext context) { if (context.SelectedTreeNodes != null) { if (context.TreeView.DataContext != assemblyTreeModel) { return context.SelectedTreeNodes.OfType().Select(FindTreeNode).ExceptNullItems(); } else { return context.SelectedTreeNodes.OfType(); } } else if (context.Reference?.Reference is IEntity entity) { if (assemblyTreeModel.FindTreeNode(entity) is { } node) { return new[] { node }; } } return Array.Empty(); ILSpyTreeNode FindTreeNode(IMemberTreeNode node) { if (node is ILSpyTreeNode ilspyNode) return ilspyNode; return assemblyTreeModel.FindTreeNode(node.Member); } } void DecompileNodes(ILSpyTreeNode[] nodes) { if (nodes.Length == 0) return; dockWorkspace.ActiveTabPage = dockWorkspace.AddTabPage(); if (assemblyTreeModel.SelectedItems.SequenceEqual(nodes)) assemblyTreeModel.DecompileSelectedNodes(); else assemblyTreeModel.SelectNodes(nodes); } } } ================================================ FILE: ILSpy/Commands/DelegateCommand.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace ICSharpCode.ILSpy.Commands { public class DelegateCommand : ICommand { private readonly Action action; private readonly Func canExecute; public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public DelegateCommand(Action action) : this(action, () => true) { } public DelegateCommand(Action action, Func canExecute) { this.action = action; this.canExecute = canExecute; } public bool CanExecute(object parameter) { return canExecute(); } public void Execute(object parameter) { action(); } } public class DelegateCommand : ICommand { private readonly Action action; private readonly Func canExecute; public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public DelegateCommand(Action action) : this(action, _ => true) { } public DelegateCommand(Action action, Func canExecute) { this.action = action; this.canExecute = canExecute; } public bool CanExecute(object parameter) { return canExecute((T)parameter); } public void Execute(object parameter) { action((T)parameter); } } } ================================================ FILE: ILSpy/Commands/DisassembleAllCommand.cs ================================================ // 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 // 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. #if DEBUG using System; using System.Collections.Concurrent; using System.Composition; using System.Diagnostics; using System.Threading.Tasks; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] [Shared] sealed class DisassembleAllCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand { public override bool CanExecute(object parameter) { return System.IO.Directory.Exists("c:\\temp\\disassembled"); } public override void Execute(object parameter) { dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new(); Parallel.ForEach( Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true), new() { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, asm => { if (!asm.HasLoadError) { Stopwatch w = Stopwatch.StartNew(); Exception exception = null; using (var writer = new System.IO.StreamWriter("c:\\temp\\disassembled\\" + asm.Text.Replace("(", "").Replace(")", "").Replace(' ', '_') + ".il")) { try { var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions(); options.FullDecompilation = true; options.CancellationToken = ct; new ILLanguage(dockWorkspace).DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), options); } catch (Exception ex) { writer.WriteLine(ex.ToString()); exception = ex; } } lock (output) { output.Write(asm.ShortName + " - " + w.Elapsed); if (exception != null) { output.Write(" - "); output.Write(exception.GetType().Name); } output.WriteLine(); } } }); return output; }, ct)).Then(dockWorkspace.ShowText).HandleExceptions(); } } } #endif ================================================ FILE: ILSpy/Commands/ExitCommand.cs ================================================ // 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 // 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.Composition; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.E_xit), MenuOrder = 99999, MenuCategory = nameof(Resources.Exit))] [Shared] sealed class ExitCommand(MainWindow mainWindow) : SimpleCommand { public override void Execute(object parameter) { mainWindow.Close(); } } } ================================================ FILE: ILSpy/Commands/ExportCommandAttribute.cs ================================================ // 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 // 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.Composition; using System.Windows.Input; namespace ICSharpCode.ILSpy { #region Toolbar public interface IToolbarCommandMetadata { string ToolbarIcon { get; } string ToolTip { get; } string ToolbarCategory { get; } object Tag { get; } double ToolbarOrder { get; } } [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ExportToolbarCommandAttribute : ExportAttribute, IToolbarCommandMetadata { public ExportToolbarCommandAttribute() : base("ToolbarCommand", typeof(ICommand)) { } public string ToolTip { get; set; } public string ToolbarIcon { get; set; } public string ToolbarCategory { get; set; } public double ToolbarOrder { get; set; } public object Tag { get; set; } } #endregion #region Main Menu public interface IMainMenuCommandMetadata { string MenuID { get; } string MenuIcon { get; } string Header { get; } string ParentMenuID { get; } string MenuCategory { get; } string InputGestureText { get; } bool IsEnabled { get; } double MenuOrder { get; } } [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ExportMainMenuCommandAttribute : ExportAttribute, IMainMenuCommandMetadata { public ExportMainMenuCommandAttribute() : base("MainMenuCommand", typeof(ICommand)) { } /// /// Gets/Sets the ID of this menu item. Menu entries are not required to have an ID, /// however, setting it allows to declare nested menu structures. /// The built-in menus have the IDs "_File", "_View", "_Window" and "_Help". /// Plugin authors are advised to use GUIDs as identifiers to prevent conflicts. /// /// NOTE: Defining cycles (for example by accidentally setting equal to ) /// will lead to a stack-overflow and crash of ILSpy at startup. /// public string MenuID { get; set; } public string MenuIcon { get; set; } public string Header { get; set; } /// /// Gets/Sets the parent of this menu item. All menu items sharing the same parent will be displayed as sub-menu items. /// If this property is set to , the menu item is displayed in the top-level menu. /// The built-in menus have the IDs "_File", "_View", "_Window" and "_Help". /// /// NOTE: Defining cycles (for example by accidentally setting equal to ) /// will lead to a stack-overflow and crash of ILSpy at startup. /// public string ParentMenuID { get; set; } public string MenuCategory { get; set; } public string InputGestureText { get; set; } public bool IsEnabled { get; set; } = true; public double MenuOrder { get; set; } } #endregion #region Tool Panes [MetadataAttribute] [AttributeUsage(AttributeTargets.Class)] public class ExportToolPaneAttribute : ExportAttribute { public ExportToolPaneAttribute() : base("ToolPane", typeof(ViewModels.ToolPaneModel)) { } } #endregion } ================================================ FILE: ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs ================================================ // Copyright (c) 2021 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.Composition; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using Microsoft.Win32; using static ICSharpCode.ILSpyX.LoadedPackage; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ExtractPackageEntry), Category = nameof(Resources.Save), Icon = "Images/Save")] [Shared] sealed class ExtractPackageEntryContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { var selectedNodes = Array.FindAll(context.SelectedTreeNodes, IsBundleItem); // Get root assembly to infer the initial directory for the save dialog. var bundleNode = selectedNodes.FirstOrDefault()?.Ancestors().OfType() .FirstOrDefault(asm => asm.PackageEntry == null); if (bundleNode == null) return; if (selectedNodes is [AssemblyTreeNode { PackageEntry: { } assembly }]) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(assembly.Name)); dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles; dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName); if (dlg.ShowDialog() == true) Save(dockWorkspace, selectedNodes, dlg.FileName, true); } else if (selectedNodes is [ResourceTreeNode { Resource: { } resource }]) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(resource.Name)); dlg.Filter = Resources.AllFiles[1..]; dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName); if (dlg.ShowDialog() == true) Save(dockWorkspace, selectedNodes, dlg.FileName, true); } else { OpenFolderDialog dlg = new OpenFolderDialog(); dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName); if (dlg.ShowDialog() != true) return; string folderName = dlg.FolderName; if (Directory.EnumerateFileSystemEntries(folderName).Any()) { var result = MessageBox.Show( Resources.AssemblySaveCodeDirectoryNotEmpty, Resources.AssemblySaveCodeDirectoryNotEmptyTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (result == MessageBoxResult.No) return; } Save(dockWorkspace, selectedNodes, folderName, false); } } internal static void Save(DockWorkspace dockWorkspace, ICollection nodes, string path, bool isFile) { dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Stopwatch stopwatch = Stopwatch.StartNew(); Dictionary fileNameCounts = new Dictionary(Platform.FileNameComparer); foreach (var (entry, fileName) in CollectAllFiles(nodes)) { string actualFileName = WholeProjectDecompiler.SanitizeFileName(fileName); while (fileNameCounts.TryGetValue(actualFileName, out int index)) { index++; fileNameCounts[actualFileName] = index; actualFileName = Path.ChangeExtension(actualFileName, index + Path.GetExtension(actualFileName)); } if (!fileNameCounts.ContainsKey(actualFileName)) { fileNameCounts[actualFileName] = 1; } SaveEntry(output, entry, Path.Combine(path, actualFileName)); } stopwatch.Stop(); output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1")); output.WriteLine(); // If we have written files, open explorer and select them grouped by folder; otherwise fall back to opening containing folder. if (isFile && File.Exists(path)) output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItem(path); }); else output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolder(path); }); output.WriteLine(); return output; }, ct)).Then(dockWorkspace.ShowText).HandleExceptions(); static IEnumerable<(PackageEntry Entry, string TargetFileName)> CollectAllFiles(ICollection nodes) { foreach (var node in nodes) { if (node is AssemblyTreeNode { PackageEntry: { } assembly }) { yield return (assembly, Path.GetFileName(assembly.FullName)); } else if (node is ResourceTreeNode { Resource: PackageEntry { } resource }) { yield return (resource, Path.GetFileName(resource.FullName)); } else if (node is AssemblyTreeNode { PackageKind: not null } asm) { var package = asm.LoadedAssembly.GetLoadResultAsync().GetAwaiter().GetResult().Package; foreach (var entry in package.Entries) { yield return (entry, entry.FullName); } } else if (node is PackageFolderTreeNode folder) { int prefixLength = 0; PackageFolder current = folder.Folder; if (nodes.Count > 1) current = current.Parent; while (current != null) { prefixLength += current.Name.Length + 1; current = current.Parent; } if (prefixLength > 0) prefixLength--; foreach (var item in TreeTraversal.PreOrder(folder.Folder, f => f.Folders).SelectMany(f => f.Entries)) { yield return (item, item.FullName.Substring(prefixLength)); } } } } } static void SaveEntry(ITextOutput output, PackageEntry entry, string targetFileName) { output.Write(entry.Name + ": "); using Stream stream = entry.TryOpenStream(); if (stream == null) { output.WriteLine("Could not open stream!"); return; } Directory.CreateDirectory(Path.GetDirectoryName(targetFileName)); stream.Position = 0; using FileStream fileStream = new FileStream(targetFileName, FileMode.OpenOrCreate); stream.CopyTo(fileStream); output.WriteLine("Written to " + targetFileName); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) => context.SelectedTreeNodes?.Any(IsBundleItem) == true; static bool IsBundleItem(SharpTreeNode node) { if (node is AssemblyTreeNode { PackageEntry: { } } or PackageFolderTreeNode) return true; if (node is ResourceTreeNode { Resource: PackageEntry { } resource } && resource.PackageQualifiedFileName.StartsWith("bundle://")) return true; return false; } } [ExportContextMenuEntry(Header = nameof(Resources.ExtractAllPackageEntries), Category = nameof(Resources.Save), Icon = "Images/Save")] [Shared] sealed class ExtractAllPackageEntriesContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { if (context.SelectedTreeNodes is not [AssemblyTreeNode { PackageEntry: null } asm]) return; OpenFolderDialog dlg = new OpenFolderDialog(); dlg.InitialDirectory = Path.GetDirectoryName(asm.LoadedAssembly.FileName); if (dlg.ShowDialog() != true) return; string folderName = dlg.FolderName; if (Directory.EnumerateFileSystemEntries(folderName).Any()) { var result = MessageBox.Show( Resources.AssemblySaveCodeDirectoryNotEmpty, Resources.AssemblySaveCodeDirectoryNotEmptyTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (result == MessageBoxResult.No) return; } ExtractPackageEntryContextMenuEntry.Save(dockWorkspace, [asm], folderName, false); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { return context.SelectedTreeNodes is [AssemblyTreeNode { PackageEntry: null, PackageKind: PackageKind.Bundle }]; } } } ================================================ FILE: ILSpy/Commands/GeneratePdbContextMenuEntry.cs ================================================ // 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.Composition; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using Microsoft.Win32; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.GeneratePortable))] [Shared] class GeneratePdbContextMenuEntry(LanguageService languageService, DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { var selectedNodes = context.SelectedTreeNodes?.OfType().ToArray(); if (selectedNodes == null || selectedNodes.Length == 0) return; GeneratePdbForAssemblies(selectedNodes.Select(n => n.LoadedAssembly), languageService, dockWorkspace); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { var selectedNodes = context.SelectedTreeNodes; return selectedNodes?.Any() == true && selectedNodes.All(n => n is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly); } internal static void GeneratePdbForAssemblies(IEnumerable assemblies, LanguageService languageService, DockWorkspace dockWorkspace) { var assemblyArray = assemblies?.Where(a => a != null).ToArray() ?? []; if (assemblyArray == null || assemblyArray.Length == 0) return; // Ensure at least one assembly supports PDB generation var supported = new Dictionary(); var unsupported = new List(); foreach (var a in assemblyArray) { try { var file = a.GetMetadataFileOrNull() as PEFile; if (PortablePdbWriter.HasCodeViewDebugDirectoryEntry(file)) supported.Add(a, file); else unsupported.Add(a); } catch { unsupported.Add(a); } } if (supported.Count == 0) { // none can be generated string msg = string.Format(Resources.CannotCreatePDBFile, ":" + Environment.NewLine + string.Join(Environment.NewLine, unsupported.Select(u => Path.GetFileName(u.FileName))) + Environment.NewLine); MessageBox.Show(msg); return; } // Ask for target folder var dlg = new OpenFolderDialog(); dlg.Title = Resources.SelectPDBOutputFolder; if (dlg.ShowDialog() != true || string.IsNullOrWhiteSpace(dlg.FolderName)) return; string targetFolder = dlg.FolderName; DecompilationOptions options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions(); dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Stopwatch totalWatch = Stopwatch.StartNew(); options.CancellationToken = ct; int total = assemblyArray.Length; int processed = 0; foreach (var assembly in assemblyArray) { // only process supported assemblies if (!supported.TryGetValue(assembly, out var file)) { output.WriteLine(string.Format(Resources.CannotCreatePDBFile, Path.GetFileName(assembly.FileName))); processed++; if (options.Progress != null) { options.Progress.Report(new DecompilationProgress { Title = string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName)), TotalUnits = total, UnitsCompleted = processed }); } continue; } string fileName = Path.Combine(targetFolder, WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb")); try { using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences), options.DecompilerSettings); decompiler.CancellationToken = ct; PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress, currentProgressTitle: string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName))); } output.WriteLine(string.Format(Resources.GeneratedPDBFile, fileName)); } catch (OperationCanceledException) { output.WriteLine(); output.WriteLine(Resources.GenerationWasCancelled); throw; } catch (Exception ex) { output.WriteLine(string.Format(Resources.GenerationFailedForAssembly, assembly.FileName, ex.Message)); } processed++; if (options.Progress != null) { options.Progress.Report(new DecompilationProgress { Title = string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName)), TotalUnits = total, UnitsCompleted = processed }); } } totalWatch.Stop(); output.WriteLine(); output.WriteLine(Resources.GenerationCompleteInSeconds, totalWatch.Elapsed.TotalSeconds.ToString("F1")); output.WriteLine(); // Select all generated pdb files in explorer var generatedFiles = assemblyArray .Select(a => Path.Combine(targetFolder, WholeProjectDecompiler.CleanUpFileName(a.ShortName, ".pdb"))) .Where(File.Exists) .ToList(); if (generatedFiles.Any()) { output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItems(generatedFiles); }); } else { output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolder(targetFolder); }); } output.WriteLine(); return output; }, ct)).Then(dockWorkspace.ShowText).HandleExceptions(); } } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.GeneratePortable), MenuCategory = nameof(Resources.Save))] [Shared] class GeneratePdbMainMenuEntry(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : SimpleCommand { public override bool CanExecute(object parameter) { return assemblyTreeModel.SelectedNodes?.Any() == true && assemblyTreeModel.SelectedNodes?.All(n => n is AssemblyTreeNode tn && !tn.LoadedAssembly.HasLoadError) == true; } public override void Execute(object parameter) { var selectedNodes = assemblyTreeModel.SelectedNodes?.OfType().ToArray(); if (selectedNodes == null || selectedNodes.Length == 0) return; GeneratePdbContextMenuEntry.GeneratePdbForAssemblies(selectedNodes.Select(n => n.LoadedAssembly), languageService, dockWorkspace); } } } ================================================ FILE: ILSpy/Commands/IProtocolHandler.cs ================================================ // 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.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { public interface IProtocolHandler { ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage); } } ================================================ FILE: ILSpy/Commands/ManageAssemblyListsCommand.cs ================================================ // 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 // 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.Composition; using System.Windows; using System.Windows.Data; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ManageAssembly_Lists), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)] [Shared] sealed class ManageAssemblyListsCommand(SettingsService settingsService) : SimpleCommand, IProvideParameterBinding { public override void Execute(object parameter) { ManageAssemblyListsDialog dlg = new(settingsService) { Owner = parameter as Window }; dlg.ShowDialog(); } public Binding ParameterBinding => new() { RelativeSource = new(RelativeSourceMode.FindAncestor) { AncestorType = typeof(Window) } }; } } ================================================ FILE: ILSpy/Commands/OpenCommand.cs ================================================ // 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 // 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.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; using Microsoft.Win32; namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Open), ToolbarIcon = "Images/Open", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 0)] [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Open), MenuIcon = "Images/Open", MenuCategory = nameof(Resources.Open), MenuOrder = 0)] [Shared] sealed class OpenCommand : CommandWrapper { private readonly AssemblyTreeModel assemblyTreeModel; public OpenCommand(AssemblyTreeModel assemblyTreeModel) : base(ApplicationCommands.Open) { this.assemblyTreeModel = assemblyTreeModel; } protected override void OnExecute(object sender, ExecutedRoutedEventArgs e) { e.Handled = true; OpenFileDialog dlg = new OpenFileDialog { Filter = ".NET assemblies|*.dll;*.exe;*.winmd;*.wasm|Nuget Packages (*.nupkg)|*.nupkg|Portable Program Database (*.pdb)|*.pdb|All files|*.*", Multiselect = true, RestoreDirectory = true }; if (dlg.ShowDialog() == true) { assemblyTreeModel.OpenFiles(dlg.FileNames); } } } } ================================================ FILE: ILSpy/Commands/OpenFromGacCommand.cs ================================================ // 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 // 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.Composition; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.OpenFrom_GAC), MenuIcon = "Images/AssemblyListGAC", MenuCategory = nameof(Resources.Open), MenuOrder = 1)] [Shared] sealed class OpenFromGacCommand(AssemblyTreeModel assemblyTreeModel, MainWindow mainWindow) : SimpleCommand { public override bool CanExecute(object parameter) { return AppEnvironment.IsWindows; } public override void Execute(object parameter) { OpenFromGacDialog dlg = new() { Owner = mainWindow }; if (dlg.ShowDialog() == true) { assemblyTreeModel.OpenFiles(dlg.SelectedFileNames); } } } } ================================================ FILE: ILSpy/Commands/Pdb2XmlCommand.cs ================================================ // 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 // 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. #if DEBUG using System.Collections.Generic; using System.Composition; using System.IO; using System.Linq; using System.Threading.Tasks; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using Microsoft.DiaSymReader.Tools; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDumpPDBAsXML), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)] [Shared] sealed class Pdb2XmlCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand { public override bool CanExecute(object parameter) { var selectedNodes = assemblyTreeModel.SelectedNodes; return selectedNodes?.Any() == true && selectedNodes.All(n => n is AssemblyTreeNode asm && !asm.LoadedAssembly.HasLoadError); } public override void Execute(object parameter) { Execute(assemblyTreeModel.SelectedNodes.OfType(), dockWorkspace); } internal static void Execute(IEnumerable nodes, DockWorkspace dockWorkspace) { var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); var options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.IncludeMethodSpans | PdbToXmlOptions.IncludeTokens; dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); var writer = new TextOutputWriter(output); foreach (var node in nodes) { string pdbFileName = Path.ChangeExtension(node.LoadedAssembly.FileName, ".pdb"); if (!File.Exists(pdbFileName)) continue; using (var pdbStream = File.OpenRead(pdbFileName)) using (var peStream = File.OpenRead(node.LoadedAssembly.FileName)) PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options); } return output; }, ct)).Then(output => dockWorkspace.ShowNodes(output, null, highlighting)).HandleExceptions(); } } [ExportContextMenuEntry(Header = nameof(Resources.DEBUGDumpPDBAsXML))] [Shared] class Pdb2XmlCommandContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { Pdb2XmlCommand.Execute(context.SelectedTreeNodes.OfType(), dockWorkspace); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { var selectedNodes = context.SelectedTreeNodes; return selectedNodes?.Any() == true && selectedNodes.All(n => n is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly); } } } #endif ================================================ FILE: ILSpy/Commands/RefreshCommand.cs ================================================ // 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 // 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.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.RefreshCommand_ReloadAssemblies), ToolbarIcon = "Images/Refresh", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 2)] [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Reload), MenuIcon = "Images/Refresh", MenuCategory = nameof(Resources.Open), MenuOrder = 2)] [Shared] sealed class RefreshCommand : CommandWrapper { private readonly AssemblyTreeModel assemblyTreeModel; public RefreshCommand(AssemblyTreeModel assemblyTreeModel) : base(NavigationCommands.Refresh) { this.assemblyTreeModel = assemblyTreeModel; } protected override void OnExecute(object sender, ExecutedRoutedEventArgs e) { assemblyTreeModel.Refresh(); } } } ================================================ FILE: ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs ================================================ // Copyright (c) 2018 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 // 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.Composition; using System.Linq; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._RemoveAssembliesWithLoadErrors), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)] [Shared] class RemoveAssembliesWithLoadErrors(AssemblyTreeModel assemblyTreeModel) : SimpleCommand { public override bool CanExecute(object parameter) { return assemblyTreeModel.AssemblyList.GetAssemblies().Any(l => l.HasLoadError); } public override void Execute(object parameter) { foreach (var assembly in assemblyTreeModel.AssemblyList.GetAssemblies()) { if (!assembly.HasLoadError) continue; var node = assemblyTreeModel.FindAssemblyNode(assembly); if (node != null && node.CanDelete()) node.Delete(); } } } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ClearAssemblyList), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)] [Shared] class ClearAssemblyList : SimpleCommand { private readonly AssemblyTreeModel assemblyTreeModel; public ClearAssemblyList(AssemblyTreeModel assemblyTreeModel) { this.assemblyTreeModel = assemblyTreeModel; } public override bool CanExecute(object parameter) { return assemblyTreeModel.AssemblyList.Count > 0; } public override void Execute(object parameter) { assemblyTreeModel.AssemblyList.Clear(); } } } ================================================ FILE: ILSpy/Commands/SaveCodeContextMenuEntry.cs ================================================ // 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 // 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.Composition; using System.IO; using System.Linq; using System.Windows; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX.TreeView; using Microsoft.Win32; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/Save")] [Shared] sealed class SaveCodeContextMenuEntry(LanguageService languageService, DockWorkspace dockWorkspace) : IContextMenuEntry { public void Execute(TextViewContext context) { Execute(context.SelectedTreeNodes, languageService, dockWorkspace); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { return CanExecute(context.SelectedTreeNodes); } public static bool CanExecute(IReadOnlyList selectedNodes) { if (selectedNodes == null || selectedNodes.Any(n => !(n is ILSpyTreeNode))) return false; return selectedNodes.Count == 1 || (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode))); } public static void Execute(IReadOnlyList selectedNodes, LanguageService languageService, DockWorkspace dockWorkspace) { var currentLanguage = languageService.Language; var tabPage = dockWorkspace.ActiveTabPage; tabPage.ShowTextView(textView => { if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) { // if there's only one treenode selected // we will invoke the custom Save logic if (singleSelection.Save(tabPage)) return; } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true })) { var selectedPath = SelectSolutionFile(); if (!string.IsNullOrEmpty(selectedPath)) { var assemblies = selectedNodes.OfType() .Select(n => n.LoadedAssembly) .Where(a => a.IsLoadedAsValidAssembly).ToList(); SolutionWriter.CreateSolution(tabPage, textView, selectedPath, currentLanguage, assemblies); } return; } // Fallback: if nobody was able to handle the request, use default behavior. // try to save all nodes to disk. var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions(); options.FullDecompilation = true; textView.SaveToDisk(currentLanguage, selectedNodes.OfType(), options); }); } /// /// Shows a File Selection dialog where the user can select the target file for the solution. /// /// The initial path to show in the dialog. If not specified, the 'Documents' directory /// will be used. /// /// The full path of the selected target file, or null if the user canceled. static string SelectSolutionFile() { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = "Solution.sln"; dlg.Filter = Resources.VisualStudioSolutionFileSlnAllFiles; if (dlg.ShowDialog() != true) { return null; } string selectedPath = Path.GetDirectoryName(dlg.FileName); bool directoryNotEmpty; try { directoryNotEmpty = Directory.EnumerateFileSystemEntries(selectedPath).Any(); } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { MessageBox.Show( "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", "Solution directory not accessible", MessageBoxButton.OK, MessageBoxImage.Error); return null; } if (directoryNotEmpty) { var result = MessageBox.Show( Resources.AssemblySaveCodeDirectoryNotEmpty, Resources.AssemblySaveCodeDirectoryNotEmptyTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (result == MessageBoxResult.No) return null; // -> abort } return dlg.FileName; } } } ================================================ FILE: ILSpy/Commands/SaveCommand.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Windows.Input; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._SaveCode), MenuIcon = "Images/Save", MenuCategory = nameof(Resources.Save), MenuOrder = 0)] [Shared] sealed class SaveCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : CommandWrapper(ApplicationCommands.Save) { protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.Handled = true; e.CanExecute = SaveCodeContextMenuEntry.CanExecute(assemblyTreeModel.SelectedNodes.ToList()); } protected override void OnExecute(object sender, ExecutedRoutedEventArgs e) { SaveCodeContextMenuEntry.Execute(assemblyTreeModel.SelectedNodes.ToList(), languageService, dockWorkspace); } } } ================================================ FILE: ILSpy/Commands/ScopeSearchToAssembly.cs ================================================ // Copyright (c) 2021 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. #nullable enable using System; using System.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisAssembly), Category = nameof(Resources.Analyze), Order = 9999)] [Shared] public class ScopeSearchToAssembly : IContextMenuEntry { private readonly SearchPaneModel searchPane; public ScopeSearchToAssembly(SearchPaneModel searchPane) { this.searchPane = searchPane; } public void Execute(TextViewContext context) { // asmName cannot be null here, because Execute is only called if IsEnabled/IsVisible return true. string asmName = GetAssembly(context)!; string searchTerm = searchPane.SearchTerm; string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm); bool replaced = false; for (int i = 0; i < args.Length; i++) { if (args[i].StartsWith("inassembly:", StringComparison.OrdinalIgnoreCase)) { args[i] = "inassembly:" + asmName; replaced = true; break; } } if (!replaced) { searchTerm += " inassembly:" + asmName; } else { searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args); } searchPane.SearchTerm = searchTerm; } public bool IsEnabled(TextViewContext context) { return GetAssembly(context) != null; } public bool IsVisible(TextViewContext context) { return GetAssembly(context) != null; } string? GetAssembly(TextViewContext context) { if (context.Reference?.Reference is IEntity entity) return entity.ParentModule?.AssemblyName; if (context.SelectedTreeNodes?.Length != 1) return null; switch (context.SelectedTreeNodes[0]) { case AssemblyTreeNode tn: return tn.LoadedAssembly.ShortName; case IMemberTreeNode member: return member.Member?.ParentModule?.AssemblyName; default: return null; } } } } ================================================ FILE: ILSpy/Commands/ScopeSearchToNamespace.cs ================================================ // Copyright (c) 2021 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.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisNamespace), Category = nameof(Resources.Analyze), Order = 9999)] [Shared] public class ScopeSearchToNamespace : IContextMenuEntry { private readonly SearchPaneModel searchPane; public ScopeSearchToNamespace(SearchPaneModel searchPane) { this.searchPane = searchPane; } public void Execute(TextViewContext context) { string ns = GetNamespace(context); string searchTerm = searchPane.SearchTerm; string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm); bool replaced = false; for (int i = 0; i < args.Length; i++) { if (args[i].StartsWith("innamespace:", StringComparison.OrdinalIgnoreCase)) { args[i] = "innamespace:" + ns; replaced = true; break; } } if (!replaced) { searchTerm += " innamespace:" + ns; } else { searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args); } searchPane.SearchTerm = searchTerm; } public bool IsEnabled(TextViewContext context) { return GetNamespace(context) != null; } public bool IsVisible(TextViewContext context) { return GetNamespace(context) != null; } string GetNamespace(TextViewContext context) { if (context.Reference?.Reference is IEntity entity) return entity.Namespace; if (context.SelectedTreeNodes?.Length != 1) return null; switch (context.SelectedTreeNodes[0]) { case NamespaceTreeNode tn: return tn.Name; case IMemberTreeNode member: return member.Member.Namespace; default: return null; } } } } ================================================ FILE: ILSpy/Commands/SearchMsdnContextMenuEntry.cs ================================================ // 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 // 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.Linq; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { using System.Composition; using ICSharpCode.Decompiler.TypeSystem; [ExportContextMenuEntry(Header = nameof(Resources.SearchMSDN), Icon = "images/SearchMsdn", Order = 9999)] [Shared] internal sealed class SearchMsdnContextMenuEntry : IContextMenuEntry { private static string msdnAddress = "https://docs.microsoft.com/dotnet/api/{0}"; public bool IsVisible(TextViewContext context) { if (context.SelectedTreeNodes == null) return false; return context.SelectedTreeNodes.All( n => n is NamespaceTreeNode || n is TypeTreeNode || n is EventTreeNode || n is FieldTreeNode || n is PropertyTreeNode || n is MethodTreeNode); } public bool IsEnabled(TextViewContext context) { if (context.SelectedTreeNodes == null) return false; foreach (var node in context.SelectedTreeNodes) { if (node is TypeTreeNode typeNode && !typeNode.IsPublicAPI) return false; if (node is EventTreeNode eventNode && (!eventNode.IsPublicAPI || !IsAccessible(eventNode.EventDefinition))) return false; if (node is FieldTreeNode fieldNode && (!fieldNode.IsPublicAPI || !IsAccessible(fieldNode.FieldDefinition) || IsDelegateOrEnumMember(fieldNode.FieldDefinition))) return false; if (node is PropertyTreeNode propertyNode && (!propertyNode.IsPublicAPI || !IsAccessible(propertyNode.PropertyDefinition))) return false; if (node is MethodTreeNode methodNode && (!methodNode.IsPublicAPI || !IsAccessible(methodNode.MethodDefinition) || IsDelegateOrEnumMember(methodNode.MethodDefinition))) return false; if (node is NamespaceTreeNode namespaceNode && string.IsNullOrEmpty(namespaceNode.Name)) return false; } return true; } bool IsAccessible(IEntity entity) { if (entity.DeclaringTypeDefinition == null) return false; switch (entity.DeclaringTypeDefinition.Accessibility) { case Accessibility.Public: case Accessibility.Protected: case Accessibility.ProtectedOrInternal: return true; default: return false; } } bool IsDelegateOrEnumMember(IMember member) { if (member.DeclaringTypeDefinition == null) return false; switch (member.DeclaringTypeDefinition.Kind) { case TypeKind.Delegate: case TypeKind.Enum: return true; default: return false; } } public void Execute(TextViewContext context) { if (context.SelectedTreeNodes != null) { foreach (ILSpyTreeNode node in context.SelectedTreeNodes) { SearchMsdn(node); } } } public static void SearchMsdn(ILSpyTreeNode node) { var address = string.Empty; if (node is NamespaceTreeNode namespaceNode) { address = string.Format(msdnAddress, namespaceNode.Name); } else if (node is IMemberTreeNode memberNode) { var member = memberNode.Member; var memberName = member.ReflectionName.Replace('`', '-').Replace('+', '.'); if (memberName.EndsWith("..ctor", System.StringComparison.Ordinal)) memberName = memberName.Substring(0, memberName.Length - 5) + "-ctor"; address = string.Format(msdnAddress, memberName); } address = address.ToLower(); if (!string.IsNullOrEmpty(address)) GlobalUtils.OpenLink(address); } } } ================================================ FILE: ILSpy/Commands/SelectPdbContextMenuEntry.cs ================================================ // 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.Composition; using System.IO; using System.Linq; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using Microsoft.Win32; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.SelectPDB))] [Shared] class SelectPdbContextMenuEntry(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry { public async void Execute(TextViewContext context) { var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; if (assembly == null) return; OpenFileDialog dlg = new OpenFileDialog(); dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb"); dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); if (dlg.ShowDialog() != true) return; using (context.TreeView.LockUpdates()) { await assembly.LoadDebugInfo(dlg.FileName); } var node = (AssemblyTreeNode)assemblyTreeModel.FindNodeByPath(new[] { assembly.FileName }, true); node.UpdateToolTip(); assemblyTreeModel.SelectNode(node); assemblyTreeModel.RefreshDecompiledView(); } public bool IsEnabled(TextViewContext context) => true; public bool IsVisible(TextViewContext context) { return context.SelectedTreeNodes?.Length == 1 && context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly; } } } ================================================ FILE: ILSpy/Commands/SetThemeCommand.cs ================================================ using System.Composition; namespace ICSharpCode.ILSpy.Commands { [Export] [Shared] public class SetThemeCommand(SettingsService settingsService) : SimpleCommand { public override void Execute(object parameter) { if (parameter is string theme) { settingsService.SessionSettings.Theme = theme; } } } } ================================================ FILE: ILSpy/Commands/ShowCFGContextMenuEntry.cs ================================================ using System; using System.Collections.Generic; using System.Composition; using System.Windows; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; namespace ICSharpCode.ILSpy.Commands { #if DEBUG [ExportContextMenuEntry(Header = "DEBUG -- Show CFG")] [Shared] internal class ShowCFGContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) { try { var container = (BlockContainer)context.Reference.Reference; var cfg = new ControlFlowGraph(container); ExportGraph(cfg.Nodes).Show(); } catch (Exception ex) { MessageBox.Show("Error generating CFG - requires GraphViz dot.exe in PATH" + Environment.NewLine + Environment.NewLine + ex.ToString()); } } public bool IsEnabled(TextViewContext context) { return context.Reference?.Reference is BlockContainer; } public bool IsVisible(TextViewContext context) { return context.Reference?.Reference is BlockContainer; } internal static GraphVizGraph ExportGraph(IReadOnlyList nodes, Func labelFunc = null) { if (labelFunc == null) { labelFunc = node => { var block = node.UserData as Block; return block != null ? block.Label : node.UserData?.ToString(); }; } GraphVizGraph g = new GraphVizGraph(); GraphVizNode[] n = new GraphVizNode[nodes.Count]; for (int i = 0; i < n.Length; i++) { n[i] = new GraphVizNode(nodes[i].UserIndex); n[i].shape = "box"; n[i].label = labelFunc(nodes[i]); g.AddNode(n[i]); } foreach (var source in nodes) { foreach (var target in source.Successors) { g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex)); } if (source.ImmediateDominator != null) { g.AddEdge( new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) { color = "green" }); } } return g; } } #endif } ================================================ FILE: ILSpy/Commands/ShowPane.cs ================================================ using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Commands { class ToolPaneCommand(string contentId, DockWorkspace dockWorkspace) : SimpleCommand { public override void Execute(object parameter) { dockWorkspace.ShowToolPane(contentId); } } class TabPageCommand(TabPageModel model, DockWorkspace dockWorkspace) : SimpleCommand { public override void Execute(object parameter) { // ensure the tab control is focused before setting the active tab page, else the tab will not be focused dockWorkspace.ActiveTabPage?.Focus(); // reset first, else clicking on the already active tab will not focus the tab and the menu checkmark will not be updated dockWorkspace.ActiveTabPage = null; dockWorkspace.ActiveTabPage = model; } } } ================================================ FILE: ILSpy/Commands/SimpleCommand.cs ================================================ // 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 // 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; using System.Windows.Data; using System.Windows.Input; namespace ICSharpCode.ILSpy { public abstract class SimpleCommand : ICommand { public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public abstract void Execute(object parameter); public virtual bool CanExecute(object parameter) { return true; } } public interface IProvideParameterBinding { Binding ParameterBinding { get; } } public interface IProvideParameterList { IEnumerable ParameterList { get; } object GetParameterText(object parameter); } } ================================================ FILE: ILSpy/Commands/SortAssemblyListCommand.cs ================================================ // 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 // 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.Composition; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources.SortAssembly_listName), MenuIcon = "Images/Sort", MenuCategory = nameof(Resources.View))] [ExportToolbarCommand(ToolTip = nameof(Resources.SortAssemblyListName), ToolbarIcon = "Images/Sort", ToolbarCategory = nameof(Resources.View))] [Shared] sealed class SortAssemblyListCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand { public override void Execute(object parameter) { assemblyTreeModel.SortAssemblyList(); } } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._CollapseTreeNodes), MenuIcon = "Images/CollapseAll", MenuCategory = nameof(Resources.View))] [ExportToolbarCommand(ToolTip = nameof(Resources.CollapseTreeNodes), ToolbarIcon = "Images/CollapseAll", ToolbarCategory = nameof(Resources.View))] [Shared] sealed class CollapseAllCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand { public override void Execute(object parameter) { assemblyTreeModel.CollapseAll(); } } } ================================================ FILE: ILSpy/ContextMenuEntry.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Windows; using System.Windows.Controls; using ICSharpCode.AvalonEdit; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpyX.Search; using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; using TomsToolbox.Essentials; using TomsToolbox.Wpf.Composition; namespace ICSharpCode.ILSpy { public interface IContextMenuEntry { bool IsVisible(TextViewContext context); bool IsEnabled(TextViewContext context); void Execute(TextViewContext context); } public class TextViewContext { /// /// Returns the selected nodes in the tree view. /// Returns null, if context menu does not belong to a tree view. /// public SharpTreeNode[] SelectedTreeNodes { get; private set; } /// /// Returns the tree view the context menu is assigned to. /// Returns null, if context menu is not assigned to a tree view. /// public SharpTreeView TreeView { get; private set; } /// /// Returns the text view the context menu is assigned to. /// Returns null, if context menu is not assigned to a text view. /// public DecompilerTextView TextView { get; private set; } /// /// Returns the list box the context menu is assigned to. /// Returns null, if context menu is not assigned to a list box. /// public ListBox ListBox { get; private set; } /// /// Returns the data grid the context menu is assigned to. /// Returns null, if context menu is not assigned to a data grid. /// public DataGrid DataGrid { get; private set; } /// /// Returns the reference the mouse cursor is currently hovering above. /// Returns null, if there was no reference found. /// public ReferenceSegment Reference { get; private set; } /// /// Returns the position in TextView the mouse cursor is currently hovering above. /// Returns null, if TextView returns null; /// public TextViewPosition? Position { get; private set; } /// /// Returns the original source of the context menu event. /// public DependencyObject OriginalSource { get; private set; } public static TextViewContext Create(ContextMenuEventArgs eventArgs, SharpTreeView treeView = null, DecompilerTextView textView = null, ListBox listBox = null, DataGrid dataGrid = null) { ReferenceSegment reference; if (textView is not null) { reference = textView.GetReferenceSegmentAtMousePosition(); } else { reference = (listBox?.SelectedItem ?? dataGrid?.SelectedItem) switch { SearchResult searchResult => new() { Reference = searchResult.Reference }, TreeNodes.IMemberTreeNode treeNode => new() { Reference = treeNode.Member }, { } value => new() { Reference = value }, _ => null }; } var position = textView?.GetPositionFromMousePosition(); var selectedTreeNodes = treeView?.GetTopLevelSelection().ToArray(); return new() { ListBox = listBox, DataGrid = dataGrid, TreeView = treeView, TextView = textView, SelectedTreeNodes = selectedTreeNodes, Reference = reference, Position = position, OriginalSource = eventArgs.OriginalSource as DependencyObject }; } } public interface IContextMenuEntryMetadata { string MenuID { get; } string ParentMenuID { get; } string Icon { get; } string Header { get; } string Category { get; } string InputGestureText { get; } double Order { get; } } [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ExportContextMenuEntryAttribute : ExportAttribute, IContextMenuEntryMetadata { public ExportContextMenuEntryAttribute() : base(typeof(IContextMenuEntry)) { // entries default to end of menu unless given specific order position Order = double.MaxValue; } /// /// Gets/Sets the ID of this menu item. Menu entries are not required to have an ID, /// however, setting it allows to declare nested menu structures. /// Plugin authors are advised to use GUIDs as identifiers to prevent conflicts. /// /// NOTE: Defining cycles (for example by accidentally setting equal to ) /// will lead to a stack-overflow and crash of ILSpy at startup. /// public string MenuID { get; set; } /// /// Gets/Sets the parent of this menu item. All menu items sharing the same parent will be displayed as sub-menu items. /// If this property is set to , the menu item is displayed in the top-level menu. /// /// NOTE: Defining cycles (for example by accidentally setting equal to ) /// will lead to a stack-overflow and crash of ILSpy at startup. /// public string ParentMenuID { get; set; } public string Icon { get; set; } public string Header { get; set; } public string Category { get; set; } public string InputGestureText { get; set; } public double Order { get; set; } } internal class ContextMenuProvider { private static readonly WeakEventSource ContextMenuClosedEventSource = new(); public static event EventHandler ContextMenuClosed { add => ContextMenuClosedEventSource.Subscribe(value); remove => ContextMenuClosedEventSource.Unsubscribe(value); } /// /// Enables extensible context menu support for the specified tree view. /// public static void Add(SharpTreeView treeView) { var provider = new ContextMenuProvider(treeView); treeView.ContextMenuOpening += provider.treeView_ContextMenuOpening; // Context menu is shown only when the ContextMenu property is not null before the // ContextMenuOpening event handler is called. treeView.ContextMenu = new ContextMenu(); } public static void Add(DecompilerTextView textView) { var provider = new ContextMenuProvider(textView); textView.ContextMenuOpening += provider.textView_ContextMenuOpening; // Context menu is shown only when the ContextMenu property is not null before the // ContextMenuOpening event handler is called. textView.ContextMenu = new ContextMenu(); } public static void Add(ListBox listBox) { var provider = new ContextMenuProvider(listBox); listBox.ContextMenuOpening += provider.listBox_ContextMenuOpening; listBox.ContextMenu = new ContextMenu(); } public static void Add(DataGrid dataGrid) { var provider = new ContextMenuProvider(dataGrid); dataGrid.ContextMenuOpening += provider.dataGrid_ContextMenuOpening; dataGrid.ContextMenu = new ContextMenu(); } readonly Control control; readonly SharpTreeView treeView; readonly DecompilerTextView textView; readonly ListBox listBox; readonly DataGrid dataGrid; readonly IExport[] entries; private ContextMenuProvider(Control control) { entries = control.GetExportProvider().GetExports().ToArray(); this.control = control; } ContextMenuProvider(DecompilerTextView textView) : this((Control)textView) { this.textView = textView ?? throw new ArgumentNullException(nameof(textView)); } ContextMenuProvider(SharpTreeView treeView) : this((Control)treeView) { this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView)); } ContextMenuProvider(ListBox listBox) : this((Control)listBox) { this.listBox = listBox ?? throw new ArgumentNullException(nameof(listBox)); } ContextMenuProvider(DataGrid dataGrid) : this((Control)dataGrid) { this.dataGrid = dataGrid ?? throw new ArgumentNullException(nameof(dataGrid)); } void treeView_ContextMenuOpening(object sender, ContextMenuEventArgs e) { var context = TextViewContext.Create(e, treeView: treeView); if (context.SelectedTreeNodes.Length == 0) { e.Handled = true; // don't show the menu return; } if (ShowContextMenu(context, out var menu)) treeView.ContextMenu = menu; else // hide the context menu. e.Handled = true; } void textView_ContextMenuOpening(object sender, ContextMenuEventArgs e) { var context = TextViewContext.Create(e, textView: textView); if (ShowContextMenu(context, out var menu)) textView.ContextMenu = menu; else // hide the context menu. e.Handled = true; } void listBox_ContextMenuOpening(object sender, ContextMenuEventArgs e) { var context = TextViewContext.Create(e, listBox: listBox); if (ShowContextMenu(context, out var menu)) listBox.ContextMenu = menu; else // hide the context menu. e.Handled = true; } void dataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e) { var context = TextViewContext.Create(e, dataGrid: dataGrid); if (ShowContextMenu(context, out var menu)) dataGrid.ContextMenu = menu; else // hide the context menu. e.Handled = true; } bool ShowContextMenu(TextViewContext context, out ContextMenu menu) { // Closing event is raised on the control where mouse is clicked, not on the control that opened the menu, so we hook on the global window event. var window = Window.GetWindow(control)!; window.ContextMenuClosing += ContextMenu_Closing; void ContextMenu_Closing(object sender, EventArgs e) { window.ContextMenuClosing -= ContextMenu_Closing; ContextMenuClosedEventSource.Raise(this, EventArgs.Empty); } menu = new ContextMenu(); var menuGroups = new Dictionary[]>(); IExport[] topLevelGroup = null; foreach (var group in entries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.ParentMenuID)) { if (group.Key == null) { topLevelGroup = group.ToArray(); } else { menuGroups.Add(group.Key, group.ToArray()); } } BuildMenu(topLevelGroup ?? Array.Empty>(), menu.Items); return menu.Items.Count > 0; void BuildMenu(IExport[] menuGroup, ItemCollection parent) { foreach (var category in menuGroup.GroupBy(c => c.Metadata.Category)) { var needSeparatorForCategory = parent.Count > 0; foreach (var entryPair in category) { var entry = entryPair.Value; if (entry.IsVisible(context)) { if (needSeparatorForCategory) { parent.Add(new Separator()); needSeparatorForCategory = false; } var menuItem = new MenuItem(); menuItem.Header = ResourceHelper.GetString(entryPair.Metadata.Header); menuItem.InputGestureText = entryPair.Metadata.InputGestureText; if (!string.IsNullOrEmpty(entryPair.Metadata.Icon)) { menuItem.Icon = new Image { Width = 16, Height = 16, Source = Images.Load(entryPair.Value, entryPair.Metadata.Icon) }; } if (entryPair.Value.IsEnabled(context)) { menuItem.Click += delegate { entry.Execute(context); }; } else menuItem.IsEnabled = false; parent.Add(menuItem); if (entryPair.Metadata.MenuID != null && menuGroups.TryGetValue(entryPair.Metadata.MenuID, out var group)) { BuildMenu(group, menuItem.Items); } } } } } } } } ================================================ FILE: ILSpy/Controls/CollapsiblePanel.cs ================================================ // Copyright (c) 2014 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media.Animation; using System.Windows.Threading; namespace ICSharpCode.ILSpy.Controls { /// /// Allows animated collapsing of the content of this panel. /// public class CollapsiblePanel : ContentControl { static CollapsiblePanel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CollapsiblePanel), new FrameworkPropertyMetadata(typeof(CollapsiblePanel))); FocusableProperty.OverrideMetadata(typeof(CollapsiblePanel), new FrameworkPropertyMetadata(false)); } public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register( "IsCollapsed", typeof(bool), typeof(CollapsiblePanel), new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged))); public bool IsCollapsed { get { return (bool)GetValue(IsCollapsedProperty); } set { SetValue(IsCollapsedProperty, value); } } public static readonly DependencyProperty CollapseOrientationProperty = DependencyProperty.Register("CollapseOrientation", typeof(Orientation), typeof(CollapsiblePanel), new FrameworkPropertyMetadata(Orientation.Vertical)); public Orientation CollapseOrientation { get { return (Orientation)GetValue(CollapseOrientationProperty); } set { SetValue(CollapseOrientationProperty, value); } } public static readonly DependencyProperty DurationProperty = DependencyProperty.Register( "Duration", typeof(TimeSpan), typeof(CollapsiblePanel), new UIPropertyMetadata(TimeSpan.FromMilliseconds(250))); /// /// The duration in milliseconds of the animation. /// public TimeSpan Duration { get { return (TimeSpan)GetValue(DurationProperty); } set { SetValue(DurationProperty, value); } } protected internal static readonly DependencyProperty AnimationProgressProperty = DependencyProperty.Register( "AnimationProgress", typeof(double), typeof(CollapsiblePanel), new FrameworkPropertyMetadata(1.0)); /// /// Value between 0 and 1 specifying how far the animation currently is. /// protected internal double AnimationProgress { get { return (double)GetValue(AnimationProgressProperty); } set { SetValue(AnimationProgressProperty, value); } } protected internal static readonly DependencyProperty AnimationProgressXProperty = DependencyProperty.Register( "AnimationProgressX", typeof(double), typeof(CollapsiblePanel), new FrameworkPropertyMetadata(1.0)); /// /// Value between 0 and 1 specifying how far the animation currently is. /// protected internal double AnimationProgressX { get { return (double)GetValue(AnimationProgressXProperty); } set { SetValue(AnimationProgressXProperty, value); } } protected internal static readonly DependencyProperty AnimationProgressYProperty = DependencyProperty.Register( "AnimationProgressY", typeof(double), typeof(CollapsiblePanel), new FrameworkPropertyMetadata(1.0)); /// /// Value between 0 and 1 specifying how far the animation currently is. /// protected internal double AnimationProgressY { get { return (double)GetValue(AnimationProgressYProperty); } set { SetValue(AnimationProgressYProperty, value); } } static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((CollapsiblePanel)d).SetupAnimation((bool)e.NewValue); } void SetupAnimation(bool isCollapsed) { if (this.IsLoaded) { // If the animation is already running, calculate remaining portion of the time double currentProgress = AnimationProgress; if (!isCollapsed) { currentProgress = 1.0 - currentProgress; } DoubleAnimation animation = new DoubleAnimation(); animation.To = isCollapsed ? 0.0 : 1.0; animation.Duration = TimeSpan.FromSeconds(Duration.TotalSeconds * currentProgress); animation.FillBehavior = FillBehavior.HoldEnd; this.BeginAnimation(AnimationProgressProperty, animation); if (CollapseOrientation == Orientation.Horizontal) { this.BeginAnimation(AnimationProgressXProperty, animation); this.AnimationProgressY = 1.0; } else { this.AnimationProgressX = 1.0; this.BeginAnimation(AnimationProgressYProperty, animation); } } else { this.AnimationProgress = isCollapsed ? 0.0 : 1.0; this.AnimationProgressX = (CollapseOrientation == Orientation.Horizontal) ? this.AnimationProgress : 1.0; this.AnimationProgressY = (CollapseOrientation == Orientation.Vertical) ? this.AnimationProgress : 1.0; } } } sealed class CollapsiblePanelProgressToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is double) return (double)value > 0 ? Visibility.Visible : Visibility.Collapsed; else return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } public class SelfCollapsingPanel : CollapsiblePanel { public static readonly DependencyProperty CanCollapseProperty = DependencyProperty.Register("CanCollapse", typeof(bool), typeof(SelfCollapsingPanel), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnCanCollapseChanged))); public bool CanCollapse { get { return (bool)GetValue(CanCollapseProperty); } set { SetValue(CanCollapseProperty, value); } } static void OnCanCollapseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { SelfCollapsingPanel panel = (SelfCollapsingPanel)d; if ((bool)e.NewValue) { if (!panel.HeldOpenByMouse) panel.IsCollapsed = true; } else { panel.IsCollapsed = false; } } bool HeldOpenByMouse { get { return IsMouseOver || IsMouseCaptureWithin; } } protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); if (CanCollapse && !HeldOpenByMouse) IsCollapsed = true; } protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); if (CanCollapse && !HeldOpenByMouse) IsCollapsed = true; } } } ================================================ FILE: ILSpy/Controls/CultureSelectionConverter.cs ================================================ // Copyright (c) 2021 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.Globalization; using System.Windows.Data; using System.Windows.Markup; namespace ICSharpCode.ILSpy.Controls { public class CultureSelectionConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string s) return s.Equals(parameter); return value == parameter; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if ((bool)value) return parameter; return Binding.DoNothing; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: ILSpy/Controls/CustomDialog.cs ================================================ // Copyright (c) 2014 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 // 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.Drawing; using System.Windows.Forms; namespace ICSharpCode.ILSpy.Controls { public sealed class CustomDialog : System.Windows.Forms.Form { System.Windows.Forms.Label label; System.Windows.Forms.Panel panel; int acceptButton; int cancelButton; int result = -1; /// /// Gets the index of the button pressed. /// public int Result { get { return result; } } public CustomDialog(string caption, string message, int acceptButton, int cancelButton, string[] buttonLabels) { this.SuspendLayout(); MyInitializeComponent(); this.Icon = null; this.acceptButton = acceptButton; this.cancelButton = cancelButton; this.Text = caption; using (Graphics g = this.CreateGraphics()) { Size size = TextRenderer.MeasureText(message, label.Font, new((int)(Screen.FromControl(this).WorkingArea.Width * 0.9), int.MaxValue), TextFormatFlags.NoPrefix | TextFormatFlags.WordBreak); Size clientSize = new Size((int)Math.Ceiling(size.Width * 96 / g.DpiX) + DockPadding.Left + DockPadding.Right, (int)Math.Ceiling(size.Height * 96 / g.DpiY) + DockPadding.Top + DockPadding.Bottom); Button[] buttons = new Button[buttonLabels.Length]; int[] positions = new int[buttonLabels.Length]; int pos = 0; for (int i = 0; i < buttons.Length; i++) { Button newButton = new Button(); newButton.FlatStyle = FlatStyle.System; newButton.Tag = i; string buttonLabel = buttonLabels[i]; newButton.Text = buttonLabel; newButton.Click += new EventHandler(ButtonClick); Size buttonSize = TextRenderer.MeasureText(buttonLabel, newButton.Font); newButton.Width = Math.Max(newButton.Width, ((int)Math.Ceiling(buttonSize.Width * 96 / g.DpiX / 8.0) + 1) * 8); positions[i] = pos; buttons[i] = newButton; pos += newButton.Width + 4; } if (acceptButton >= 0) { AcceptButton = buttons[acceptButton]; } if (cancelButton >= 0) { CancelButton = buttons[cancelButton]; } pos += 4; // add space before first button // (we don't start with pos=4 because this space doesn't belong to the button panel) if (pos > clientSize.Width) { clientSize.Width = pos; } clientSize.Height += panel.Height; this.ClientSize = clientSize; int start = (clientSize.Width - pos) / 2; for (int i = 0; i < buttons.Length; i++) { buttons[i].Location = new Point(start + positions[i], 4); } panel.Controls.AddRange(buttons); } label.Text = message; this.ResumeLayout(false); } protected override void OnKeyDown(KeyEventArgs e) { if (cancelButton == -1 && e.KeyCode == Keys.Escape) { this.Close(); } else if (e.KeyCode == Keys.C && e.Control) { Clipboard.SetText(this.Text + Environment.NewLine + label.Text); } } void ButtonClick(object sender, EventArgs e) { result = (int)((Control)sender).Tag; this.Close(); } /// /// This method is required for Windows Forms designer support. /// Do not change the method contents inside the source code editor. The Forms designer might /// not be able to load this method if it was changed manually. /// void MyInitializeComponent() { this.panel = new System.Windows.Forms.Panel(); this.label = new System.Windows.Forms.Label(); // // panel // this.panel.Dock = System.Windows.Forms.DockStyle.Bottom; this.panel.Location = new System.Drawing.Point(4, 80); this.panel.Name = "panel"; this.panel.Size = new System.Drawing.Size(266, 32); this.panel.TabIndex = 0; // // label // this.label.Dock = System.Windows.Forms.DockStyle.Fill; this.label.FlatStyle = System.Windows.Forms.FlatStyle.System; this.label.Location = new System.Drawing.Point(4, 4); this.label.Name = "label"; this.label.Size = new System.Drawing.Size(266, 76); this.label.TabIndex = 1; this.label.UseMnemonic = false; // // CustomDialog // this.ClientSize = new System.Drawing.Size(274, 112); this.Controls.Add(this.label); this.Controls.Add(this.panel); this.DockPadding.Left = 4; this.DockPadding.Right = 4; this.DockPadding.Top = 4; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.ShowInTaskbar = false; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "CustomDialog"; this.KeyPreview = true; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "CustomDialog"; this.AutoScaleMode = AutoScaleMode.Dpi; this.AutoScaleDimensions = new SizeF(96, 96); } } } ================================================ FILE: ILSpy/Controls/ExtensionMethods.cs ================================================ // 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 // 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.Windows; using System.Windows.Markup; namespace ICSharpCode.ILSpy.Controls { /// /// ExtensionMethods used in ILSpy. /// public static class ExtensionMethods { /// /// Sets the value of a dependency property on using a markup extension. /// /// This method does not support markup extensions like x:Static that depend on /// having a XAML file as context. public static void SetValueToExtension(this DependencyObject targetObject, DependencyProperty property, MarkupExtension markupExtension) { // This method was copied from ICSharpCode.Core.Presentation (with permission to switch license to X11) if (targetObject == null) throw new ArgumentNullException(nameof(targetObject)); if (property == null) throw new ArgumentNullException(nameof(property)); if (markupExtension == null) throw new ArgumentNullException(nameof(markupExtension)); var serviceProvider = new SetValueToExtensionServiceProvider(targetObject, property); targetObject.SetValue(property, markupExtension.ProvideValue(serviceProvider)); } sealed class SetValueToExtensionServiceProvider : IServiceProvider, IProvideValueTarget { // This class was copied from ICSharpCode.Core.Presentation (with permission to switch license to X11) readonly DependencyObject targetObject; readonly DependencyProperty targetProperty; public SetValueToExtensionServiceProvider(DependencyObject targetObject, DependencyProperty property) { this.targetObject = targetObject; this.targetProperty = property; } public object GetService(Type serviceType) { if (serviceType == typeof(IProvideValueTarget)) return this; else return null; } public object TargetObject { get { return targetObject; } } public object TargetProperty { get { return targetProperty; } } } } } ================================================ FILE: ILSpy/Controls/GridViewColumnAutoSize.cs ================================================ // Copyright (c) 2014 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 // 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.Windows; using System.Windows.Controls; namespace ICSharpCode.ILSpy.Controls { /// /// This class adds the AutoWidth property to the WPF ListView. /// It supports a semi-colon-separated list of values, for each defined cell. /// Each value can either be a fixed size double, or a percentage. /// The sizes of columns with a percentage will be calculated from the /// remaining width (after assigning the fixed sizes). /// Examples: 50%;25%;25% or 30;100%;50 /// public class GridViewColumnAutoSize { // This class was copied from ICSharpCode.Core.Presentation. public static readonly DependencyProperty AutoWidthProperty = DependencyProperty.RegisterAttached("AutoWidth", typeof(string), typeof(GridViewColumnAutoSize), new FrameworkPropertyMetadata(null, AutoWidthPropertyChanged)); public static string GetAutoWidth(DependencyObject obj) { return (string)obj.GetValue(AutoWidthProperty); } public static void SetAutoWidth(DependencyObject obj, string value) { obj.SetValue(AutoWidthProperty, value); } static void AutoWidthPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { ListView grid = sender as ListView; if (grid == null) return; grid.SizeChanged += delegate (object listView, SizeChangedEventArgs e) { ListView lv = listView as ListView; if (lv == null) return; GridView v = lv.View as GridView; if (v == null) return; CalculateSizes(v, GetAutoWidth(lv), e.NewSize.Width); }; GridView view = grid.View as GridView; if (view == null) return; CalculateSizes(view, args.NewValue as string, grid.ActualWidth); } static void CalculateSizes(GridView view, string sizeValue, double fullWidth) { string[] sizes = (sizeValue ?? "").Split(';'); if (sizes.Length != view.Columns.Count) return; Dictionary> percentages = new Dictionary>(); double remainingWidth = fullWidth - 30; // 30 is a good offset for the scrollbar for (int i = 0; i < view.Columns.Count; i++) { var column = view.Columns[i]; double size; bool isPercentage = !double.TryParse(sizes[i], out size); if (isPercentage) { size = double.Parse(sizes[i].TrimEnd('%', ' ')); percentages.Add(i, w => w * size / 100.0); } else { column.Width = size; remainingWidth -= size; } } if (remainingWidth < 0) return; foreach (var p in percentages) { var column = view.Columns[p.Key]; column.Width = p.Value(remainingWidth); } } } } ================================================ FILE: ILSpy/Controls/MainMenu.xaml ================================================ ================================================ FILE: ILSpy/Controls/MainMenu.xaml.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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.Collections.Generic; using System.Composition; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Composition; using TomsToolbox.ObservableCollections; using TomsToolbox.Wpf; using TomsToolbox.Wpf.Converters; namespace ICSharpCode.ILSpy.Controls { /// /// Interaction logic for MainMenu.xaml /// [Export] [NonShared] public partial class MainMenu { public MainMenu(SettingsService settingsService, IExportProvider exportProvider, DockWorkspace dockWorkspace) { SessionSettings = settingsService.SessionSettings; InitializeComponent(); this.BeginInvoke(() => { InitMainMenu(Menu, exportProvider); InitWindowMenu(WindowMenuItem, Window.GetWindow(this)!.InputBindings, dockWorkspace); }); } public SessionSettings SessionSettings { get; } static void InitMainMenu(Menu mainMenu, IExportProvider exportProvider) { var mainMenuCommands = exportProvider.GetExports("MainMenuCommand"); // Start by constructing the individual flat menus var parentMenuItems = new Dictionary(); var menuGroups = mainMenuCommands.OrderBy(c => c.Metadata?.MenuOrder).GroupBy(c => c.Metadata?.ParentMenuID).ToArray(); foreach (var menu in menuGroups) { // Get or add the target menu item and add all items grouped by menu category var parentMenuItem = GetOrAddParentMenuItem(menu.Key, menu.Key); foreach (var category in menu.GroupBy(c => c.Metadata?.MenuCategory)) { if (parentMenuItem.Items.Count > 0) { parentMenuItem.Items.Add(new Separator { Tag = category.Key }); } foreach (var entry in category) { if (menuGroups.Any(g => g.Key == entry.Metadata?.MenuID)) { var menuItem = GetOrAddParentMenuItem(entry.Metadata?.MenuID, entry.Metadata?.Header); // replace potential dummy text with real name menuItem.Header = ResourceHelper.GetString(entry.Metadata?.Header); parentMenuItem.Items.Add(menuItem); } else { var command = entry.Value; var menuItem = new MenuItem { Command = CommandWrapper.Unwrap(command), Tag = entry.Metadata?.MenuID, Header = ResourceHelper.GetString(entry.Metadata?.Header) }; if (!string.IsNullOrEmpty(entry.Metadata?.MenuIcon)) { menuItem.Icon = new Image { Width = 16, Height = 16, Source = Images.Load(command, entry.Metadata.MenuIcon) }; } menuItem.IsEnabled = entry.Metadata?.IsEnabled ?? false; menuItem.InputGestureText = entry.Metadata?.InputGestureText; if (command is IProvideParameterBinding parameterBinding) { BindingOperations.SetBinding(menuItem, MenuItem.CommandParameterProperty, parameterBinding.ParameterBinding); } parentMenuItem.Items.Add(menuItem); } } } } foreach (var item in parentMenuItems.Values.Where(item => item.Parent == null)) { mainMenu.Items.Add(item); } MenuItem GetOrAddParentMenuItem(string menuId, string resourceKey) { if (!parentMenuItems.TryGetValue(menuId, out var parentMenuItem)) { var topLevelMenuItem = mainMenu.Items.OfType().FirstOrDefault(m => (string)m.Tag == menuId); if (topLevelMenuItem == null) { parentMenuItem = new() { Header = ResourceHelper.GetString(resourceKey), Tag = menuId }; parentMenuItems.Add(menuId, parentMenuItem); } else { parentMenuItems.Add(menuId, topLevelMenuItem); parentMenuItem = topLevelMenuItem; } } return parentMenuItem; } } static void InitWindowMenu(MenuItem windowMenuItem, InputBindingCollection inputBindings, DockWorkspace dockWorkspace) { var defaultItems = windowMenuItem.Items.Cast().ToArray(); windowMenuItem.Items.Clear(); var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings, dockWorkspace)).ToArray(); var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage, dockWorkspace)); var allItems = new ObservableCompositeCollection(defaultItems, [new Separator()], toolItems, [new Separator()], tabItems); windowMenuItem.ItemsSource = allItems; } static Control CreateMenuItem(TabPageModel pane, DockWorkspace dockWorkspace) { var header = new TextBlock { MaxWidth = 200, TextTrimming = TextTrimming.CharacterEllipsis }; header.SetBinding(TextBlock.TextProperty, new Binding(nameof(pane.Title)) { Source = pane }); var menuItem = new MenuItem { Command = new TabPageCommand(pane, dockWorkspace), Header = header, IsCheckable = true }; menuItem.SetBinding(MenuItem.IsCheckedProperty, new Binding(nameof(dockWorkspace.ActiveTabPage)) { Source = dockWorkspace, ConverterParameter = pane, Converter = BinaryOperationConverter.Equality, Mode = BindingMode.OneWay }); return menuItem; } static Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings, DockWorkspace dockWorkspace) { var menuItem = new MenuItem { Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId, dockWorkspace), Header = pane.Title }; var shortcutKey = pane.ShortcutKey; if (shortcutKey != null) { inputBindings.Add(new InputBinding(menuItem.Command, shortcutKey)); menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture); } if (!string.IsNullOrEmpty(pane.Icon)) { menuItem.Icon = new Image { Width = 16, Height = 16, Source = Images.Load(pane, pane.Icon) }; } return menuItem; } } } ================================================ FILE: ILSpy/Controls/MainToolBar.xaml ================================================ ================================================ FILE: ILSpy/Controls/MainToolBar.xaml.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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.ComponentModel; using System.Composition; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; namespace ICSharpCode.ILSpy.Controls { /// /// Interaction logic for MainToolBar.xaml /// [Export] [NonShared] public partial class MainToolBar { public MainToolBar(IExportProvider exportProvider) { InitializeComponent(); this.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => { InitToolbar(ToolBar, exportProvider); Window.GetWindow(this)!.KeyDown += MainWindow_KeyDown; }); } static void InitToolbar(ToolBar toolBar, IExportProvider exportProvider) { int navigationPos = 0; int openPos = 1; var toolbarCommandsByCategory = exportProvider .GetExports("ToolbarCommand") .OrderBy(c => c.Metadata?.ToolbarOrder) .GroupBy(c => c.Metadata?.ToolbarCategory); foreach (var commandCategory in toolbarCommandsByCategory) { if (commandCategory.Key == nameof(Properties.Resources.Navigation)) { foreach (var command in commandCategory) { toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command)); openPos++; } } else if (commandCategory.Key == nameof(Properties.Resources.Open)) { foreach (var command in commandCategory) { toolBar.Items.Insert(openPos++, CreateToolbarItem(command)); } } else { toolBar.Items.Add(new Separator()); foreach (var command in commandCategory) { toolBar.Items.Add(CreateToolbarItem(command)); } } } } static UIElement CreateToolbarItem(IExport commandExport) { var command = commandExport.Value; Button toolbarItem = new() { Style = ThemeManager.Current.CreateToolBarButtonStyle(), Command = CommandWrapper.Unwrap(command), ToolTip = Properties.Resources.ResourceManager.GetString( commandExport.Metadata?.ToolTip ?? string.Empty), Tag = commandExport.Metadata?.Tag, Content = new Image { Width = 16, Height = 16, Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon) } }; if (command is IProvideParameterBinding parameterBinding) { BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty, parameterBinding.ParameterBinding); } if (command is IProvideParameterList parameterList) { toolbarItem.Margin = new Thickness(2, 0, 0, 0); var dropDownPanel = new StackPanel { Orientation = Orientation.Horizontal }; var dropDownToggle = new ToggleButton { Style = ThemeManager.Current.CreateToolBarToggleButtonStyle(), Content = "▾", Padding = new Thickness(0), MinWidth = 0, Margin = new Thickness(0, 0, 2, 0) }; var contextMenu = new ContextMenu { PlacementTarget = dropDownPanel, Tag = command }; ContextMenuService.SetPlacement(toolbarItem, PlacementMode.Bottom); toolbarItem.ContextMenu = contextMenu; toolbarItem.ContextMenuOpening += (_, _) => PrepareParameterList(contextMenu); dropDownToggle.Checked += (_, _) => { PrepareParameterList(contextMenu); contextMenu.Placement = PlacementMode.Bottom; contextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true); }; BindingOperations.SetBinding(dropDownToggle, ToggleButton.IsCheckedProperty, new Binding(nameof(contextMenu.IsOpen)) { Source = contextMenu }); BindingOperations.SetBinding(dropDownToggle, IsEnabledProperty, new Binding(nameof(IsEnabled)) { Source = toolbarItem }); // When the toggle button is checked, clicking it to uncheck will dismiss the menu first // which unchecks the toggle button via binding above and the click is used to open it again. // This is a workaround to ignore the click to uncheck the already unchecked toggle button. // We have to ensure the dismissing click is on the toggle button, otherwise the flag // will not get cleared and menu will not open next time. Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(contextMenu, (_, e) => { var point = e.GetPosition(dropDownToggle); dropDownToggle.Tag = dropDownToggle.InputHitTest(point); }); dropDownToggle.PreviewMouseLeftButtonDown += (_, e) => { e.Handled = dropDownToggle.Tag != null; dropDownToggle.Tag = null; }; dropDownPanel.Children.Add(toolbarItem); dropDownPanel.Children.Add(dropDownToggle); return dropDownPanel; } return toolbarItem; } static void PrepareParameterList(ContextMenu menu) { const int maximumParameterListCount = 20; var command = (ICommand)menu.Tag; var parameterList = (IProvideParameterList)command; menu.Items.Clear(); foreach (var parameter in parameterList.ParameterList) { MenuItem parameterItem = new MenuItem(); parameterItem.Command = CommandWrapper.Unwrap(command); parameterItem.CommandParameter = parameter; parameterItem.CommandTarget = menu.PlacementTarget; parameterItem.InputGestureText = " "; var headerPresenter = new ContentPresenter { RecognizesAccessKey = false }; parameterItem.Header = headerPresenter; var header = parameterList.GetParameterText(parameter); switch (header) { case SharpTreeNode node: headerPresenter.Content = node.NavigationText; if (node.Icon is ImageSource icon) parameterItem.Icon = new Image { Width = 16, Height = 16, Source = icon }; break; default: headerPresenter.Content = header; break; } menu.Items.Add(parameterItem); if (menu.Items.Count >= maximumParameterListCount) break; } } void MainWindow_KeyDown(object sender, KeyEventArgs e) { if (e.Handled || e.KeyboardDevice.Modifiers != ModifierKeys.Alt || e.Key != Key.System) return; switch (e.SystemKey) { case Key.A: assemblyListComboBox.Focus(); e.Handled = true; break; case Key.L: languageComboBox.Focus(); e.Handled = true; break; case Key.E: // Alt+V was already taken by _View menu languageVersionComboBox.Focus(); e.Handled = true; break; } } } } ================================================ FILE: ILSpy/Controls/MarkupExtensions.cs ================================================ // 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 // 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.Windows; using System.Windows.Markup; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls { [MarkupExtensionReturnType(typeof(Color))] class ControlColor : MarkupExtension { readonly float val; /// /// Amount of highlight (0..1) /// public float Highlight { get; set; } /// /// val: Color value in the range 105..255. /// public ControlColor(float val) { if (!(val >= 105 && val <= 255)) throw new ArgumentOutOfRangeException(nameof(val)); this.val = val; } public override object ProvideValue(IServiceProvider serviceProvider) { if (val > 227) { return Interpolate(227, SystemColors.ControlLightColor, 255, SystemColors.ControlLightLightColor); } else if (val > 160) { return Interpolate(160, SystemColors.ControlDarkColor, 227, SystemColors.ControlLightColor); } else { return Interpolate(105, SystemColors.ControlDarkDarkColor, 160, SystemColors.ControlDarkColor); } } Color Interpolate(float v1, Color c1, float v2, Color c2) { float v = (val - v1) / (v2 - v1); Color c = c1 * (1 - v) + c2 * v; return c * (1 - Highlight) + SystemColors.HighlightColor * Highlight; } } } ================================================ FILE: ILSpy/Controls/ResourceObjectTable.xaml ================================================ ================================================ FILE: ILSpy/Controls/ResourceObjectTable.xaml.cs ================================================ // 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 // 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; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace ICSharpCode.ILSpy.Controls { /// /// Interaction logic for ResourceObjectTable.xaml /// public partial class ResourceObjectTable : UserControl { ICollectionView filteredView; string filter; public ResourceObjectTable(IEnumerable resources, FrameworkElement container) { InitializeComponent(); // set size to fit decompiler window container.SizeChanged += OnParentSizeChanged; if (!double.IsNaN(container.ActualWidth)) Width = Math.Max(container.ActualWidth - 45, 0); MaxHeight = container.ActualHeight; filteredView = CollectionViewSource.GetDefaultView(resources); filteredView.Filter = OnResourceFilter; resourceListView.ItemsSource = filteredView; } private bool OnResourceFilter(object obj) { if (string.IsNullOrEmpty(filter)) return true; if (obj is TreeNodes.ResourcesFileTreeNode.SerializedObjectRepresentation item) return item.Key?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true || item.Value?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true; return false; // make it obvious search is not working } private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) { if (e.WidthChanged && !double.IsNaN(e.NewSize.Width)) Width = Math.Max(e.NewSize.Width - 45, 0); if (e.HeightChanged) MaxHeight = e.NewSize.Height; } private void OnFilterTextChanged(object sender, TextChangedEventArgs e) { filter = resourceFilterBox.Text; filteredView?.Refresh(); } void ExecuteCopy(object sender, ExecutedRoutedEventArgs args) { StringBuilder sb = new StringBuilder(); foreach (var item in resourceListView.SelectedItems) { if (item is TreeNodes.ResourcesFileTreeNode.SerializedObjectRepresentation so) { switch (args.Parameter) { case "Key": sb.AppendLine(so.Key); continue; case "Value": sb.AppendLine(so.Value); continue; case "Type": sb.AppendLine(so.Type); continue; default: sb.AppendLine($"{so.Key}\t{so.Value}\t{so.Type}"); continue; } } sb.AppendLine(item.ToString()); } Clipboard.SetText(sb.ToString()); } void CanExecuteCopy(object sender, CanExecuteRoutedEventArgs args) { args.CanExecute = true; } } } ================================================ FILE: ILSpy/Controls/ResourceStringTable.xaml ================================================ ================================================ FILE: ILSpy/Controls/ResourceStringTable.xaml.cs ================================================ // 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 // 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; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace ICSharpCode.ILSpy.Controls { /// /// Interaction logic for ResourceStringTable.xaml /// public partial class ResourceStringTable : UserControl { ICollectionView filteredView; string filter; public ResourceStringTable(IEnumerable strings, FrameworkElement container) { InitializeComponent(); // set size to fit decompiler window container.SizeChanged += OnParentSizeChanged; if (!double.IsNaN(container.ActualWidth)) Width = Math.Max(container.ActualWidth - 45, 0); MaxHeight = container.ActualHeight; filteredView = CollectionViewSource.GetDefaultView(strings); filteredView.Filter = OnResourceFilter; resourceListView.ItemsSource = filteredView; } private bool OnResourceFilter(object obj) { if (string.IsNullOrEmpty(filter)) return true; if (obj is KeyValuePair item) return item.Key?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true || item.Value?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true; return false; // make it obvious search is not working } private void OnParentSizeChanged(object sender, SizeChangedEventArgs e) { if (e.WidthChanged && !double.IsNaN(e.NewSize.Width)) Width = Math.Max(e.NewSize.Width - 45, 0); if (e.HeightChanged) MaxHeight = e.NewSize.Height; } private void OnFilterTextChanged(object sender, TextChangedEventArgs e) { filter = resourceFilterBox.Text; filteredView?.Refresh(); } void ExecuteCopy(object sender, ExecutedRoutedEventArgs args) { StringBuilder sb = new StringBuilder(); foreach (var item in resourceListView.SelectedItems) { if (item is KeyValuePair pair) { switch (args.Parameter) { case "Key": sb.AppendLine(pair.Key); continue; case "Value": sb.AppendLine(pair.Value); continue; default: sb.AppendLine($"{pair.Key}\t{pair.Value}"); continue; } } sb.AppendLine(item.ToString()); } Clipboard.SetText(sb.ToString()); } void CanExecuteCopy(object sender, CanExecuteRoutedEventArgs args) { args.CanExecute = true; } } } ================================================ FILE: ILSpy/Controls/SearchBox.cs ================================================ // 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; namespace ICSharpCode.ILSpy.Controls { public class SearchBox : TextBox { static SearchBox() { DefaultStyleKeyProperty.OverrideMetadata( typeof(SearchBox), new FrameworkPropertyMetadata(typeof(SearchBox))); } #region Dependency properties public static DependencyProperty WatermarkTextProperty = DependencyProperty.Register("WatermarkText", typeof(string), typeof(SearchBox)); public static DependencyProperty WatermarkColorProperty = DependencyProperty.Register("WatermarkColor", typeof(Brush), typeof(SearchBox)); public static DependencyProperty HasTextProperty = DependencyProperty.Register("HasText", typeof(bool), typeof(SearchBox)); public static readonly DependencyProperty UpdateDelayProperty = DependencyProperty.Register("UpdateDelay", typeof(TimeSpan), typeof(SearchBox), new FrameworkPropertyMetadata(TimeSpan.FromMilliseconds(200))); #endregion #region Public Properties public string WatermarkText { get { return (string)GetValue(WatermarkTextProperty); } set { SetValue(WatermarkTextProperty, value); } } public Brush WatermarkColor { get { return (Brush)GetValue(WatermarkColorProperty); } set { SetValue(WatermarkColorProperty, value); } } public bool HasText { get { return (bool)GetValue(HasTextProperty); } private set { SetValue(HasTextProperty, value); } } public TimeSpan UpdateDelay { get { return (TimeSpan)GetValue(UpdateDelayProperty); } set { SetValue(UpdateDelayProperty, value); } } #endregion #region Handlers private void IconBorder_MouseLeftButtonUp(object obj, MouseButtonEventArgs e) { if (this.HasText) this.Text = string.Empty; } #endregion #region Overrides DispatcherTimer timer; protected override void OnTextChanged(TextChangedEventArgs e) { base.OnTextChanged(e); HasText = this.Text.Length > 0; if (timer == null) { timer = new DispatcherTimer(); timer.Tick += timer_Tick; } timer.Stop(); timer.Interval = this.UpdateDelay; timer.Start(); UpdateWatermarkLabel(); } private void UpdateWatermarkLabel() { Label wl = (Label)GetTemplateChild("WatermarkLabel"); if (wl != null) wl.Visibility = HasText ? Visibility.Hidden : Visibility.Visible; } void timer_Tick(object sender, EventArgs e) { timer.Stop(); timer = null; var textBinding = GetBindingExpression(TextProperty); if (textBinding != null) { textBinding.UpdateSource(); } } protected override void OnLostFocus(RoutedEventArgs e) { UpdateWatermarkLabel(); base.OnLostFocus(e); } protected override void OnGotFocus(RoutedEventArgs e) { UpdateWatermarkLabel(); base.OnGotFocus(e); } public override void OnApplyTemplate() { base.OnApplyTemplate(); Border iconBorder = GetTemplateChild("PART_IconBorder") as Border; if (iconBorder != null) { iconBorder.MouseLeftButtonUp += IconBorder_MouseLeftButtonUp; } } protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.Escape && this.Text.Length > 0) { this.Text = string.Empty; e.Handled = true; } else { base.OnKeyDown(e); } } #endregion } } ================================================ FILE: ILSpy/Controls/SearchBoxStyle.xaml ================================================ ================================================ FILE: ILSpy/Controls/SortableGridViewColumn.cs ================================================ // 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 // 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.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace ICSharpCode.ILSpy.Controls { /// /// Allows to automatically sort a grid view. /// public class SortableGridViewColumn : GridViewColumn { // This class was copied from ICSharpCode.Core.Presentation. static readonly ComponentResourceKey headerTemplateKey = new ComponentResourceKey(typeof(SortableGridViewColumn), "ColumnHeaderTemplate"); public SortableGridViewColumn() { this.SetValueToExtension(HeaderTemplateProperty, new DynamicResourceExtension(headerTemplateKey)); } string sortBy; public string SortBy { get { return sortBy; } set { if (sortBy != value) { sortBy = value; OnPropertyChanged(new PropertyChangedEventArgs("SortBy")); } } } #region SortDirection property public static readonly DependencyProperty SortDirectionProperty = DependencyProperty.RegisterAttached("SortDirection", typeof(ColumnSortDirection), typeof(SortableGridViewColumn), new FrameworkPropertyMetadata(ColumnSortDirection.None, OnSortDirectionChanged)); public ColumnSortDirection SortDirection { get { return (ColumnSortDirection)GetValue(SortDirectionProperty); } set { SetValue(SortDirectionProperty, value); } } public static ColumnSortDirection GetSortDirection(ListView listView) { return (ColumnSortDirection)listView.GetValue(SortDirectionProperty); } public static void SetSortDirection(ListView listView, ColumnSortDirection value) { listView.SetValue(SortDirectionProperty, value); } static void OnSortDirectionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { ListView grid = sender as ListView; if (grid != null) { SortableGridViewColumn col = GetCurrentSortColumn(grid); if (col != null) col.SortDirection = (ColumnSortDirection)args.NewValue; Sort(grid); } } #endregion #region CurrentSortColumn property public static readonly DependencyProperty CurrentSortColumnProperty = DependencyProperty.RegisterAttached("CurrentSortColumn", typeof(SortableGridViewColumn), typeof(SortableGridViewColumn), new FrameworkPropertyMetadata(OnCurrentSortColumnChanged)); public static SortableGridViewColumn GetCurrentSortColumn(ListView listView) { return (SortableGridViewColumn)listView.GetValue(CurrentSortColumnProperty); } public static void SetCurrentSortColumn(ListView listView, SortableGridViewColumn value) { listView.SetValue(CurrentSortColumnProperty, value); } static void OnCurrentSortColumnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { ListView grid = sender as ListView; if (grid != null) { SortableGridViewColumn oldColumn = (SortableGridViewColumn)args.OldValue; if (oldColumn != null) oldColumn.SortDirection = ColumnSortDirection.None; SortableGridViewColumn newColumn = (SortableGridViewColumn)args.NewValue; if (newColumn != null) { newColumn.SortDirection = GetSortDirection(grid); } Sort(grid); } } #endregion #region SortMode property public static readonly DependencyProperty SortModeProperty = DependencyProperty.RegisterAttached("SortMode", typeof(ListViewSortMode), typeof(SortableGridViewColumn), new FrameworkPropertyMetadata(ListViewSortMode.None, OnSortModeChanged)); public static ListViewSortMode GetSortMode(ListView listView) { return (ListViewSortMode)listView.GetValue(SortModeProperty); } public static void SetSortMode(ListView listView, ListViewSortMode value) { listView.SetValue(SortModeProperty, value); } static void OnSortModeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { ListView grid = sender as ListView; if (grid != null) { if ((ListViewSortMode)args.NewValue != ListViewSortMode.None) grid.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(GridViewColumnHeaderClickHandler)); else grid.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(GridViewColumnHeaderClickHandler)); } } static void GridViewColumnHeaderClickHandler(object sender, RoutedEventArgs e) { ListView grid = sender as ListView; GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader; if (grid != null && headerClicked != null && headerClicked.Role != GridViewColumnHeaderRole.Padding) { if (headerClicked.Column == GetCurrentSortColumn(grid)) { switch (GetSortDirection(grid)) { case ColumnSortDirection.None: SetSortDirection(grid, ColumnSortDirection.Ascending); break; case ColumnSortDirection.Ascending: SetSortDirection(grid, ColumnSortDirection.Descending); break; case ColumnSortDirection.Descending: SetSortDirection(grid, ColumnSortDirection.None); break; } } else { SetSortDirection(grid, ColumnSortDirection.Ascending); SetCurrentSortColumn(grid, headerClicked.Column as SortableGridViewColumn); } } } #endregion static void Sort(ListView grid) { ColumnSortDirection currentDirection = GetSortDirection(grid); SortableGridViewColumn column = GetCurrentSortColumn(grid); ICollectionView dataView = CollectionViewSource.GetDefaultView(grid.ItemsSource); if (dataView == null) return; if (column != null && GetSortMode(grid) == ListViewSortMode.Automatic && currentDirection != ColumnSortDirection.None) { string sortBy = column.SortBy; if (sortBy == null) { Binding binding = column.DisplayMemberBinding as Binding; if (binding != null && binding.Path != null) { sortBy = binding.Path.Path; } } dataView.SortDescriptions.Clear(); if (sortBy != null) { ListSortDirection direction; if (currentDirection == ColumnSortDirection.Descending) direction = ListSortDirection.Descending; else direction = ListSortDirection.Ascending; dataView.SortDescriptions.Add(new SortDescription(sortBy, direction)); } } else { dataView.SortDescriptions.Clear(); } dataView.Refresh(); } } public enum ColumnSortDirection { None, Ascending, Descending } public enum ListViewSortMode { /// /// Disable automatic sorting when sortable columns are clicked. /// None, /// /// Fully automatic sorting. /// Automatic, /// /// Automatically update SortDirection and CurrentSortColumn properties, /// but do not actually sort the data. /// HalfAutomatic } sealed class ColumnSortDirectionToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return Equals(value, parameter) ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } } } ================================================ FILE: ILSpy/Controls/TreeView/EditTextBox.cs ================================================ // Copyright (c) 2020 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Input; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Controls.TreeView { class EditTextBox : TextBox { static EditTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(EditTextBox), new FrameworkPropertyMetadata(typeof(EditTextBox))); } public EditTextBox() { Loaded += delegate { Init(); }; } public SharpTreeViewItem Item { get; set; } public SharpTreeNode Node { get { return Item.Node; } } void Init() { Text = Node.LoadEditText(); Focus(); SelectAll(); } protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.Enter) { Commit(); } else if (e.Key == Key.Escape) { Node.IsEditing = false; } } protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { if (Node.IsEditing) { Commit(); } } bool committing; void Commit() { if (!committing) { committing = true; Node.IsEditing = false; if (!Node.SaveEditText(Text)) { Item.Focus(); } Node.RaisePropertyChanged(nameof(Text)); //if (Node.SaveEditText(Text)) { // Node.IsEditing = false; // Node.RaisePropertyChanged("Text"); //} //else { // Init(); //} committing = false; } } } } ================================================ FILE: ILSpy/Controls/TreeView/ExtensionMethods.cs ================================================ // Copyright (c) 2020 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 // 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.Collections; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls.TreeView { static class ExtensionMethods { public static T FindAncestor(this DependencyObject d) where T : class { return AncestorsAndSelf(d).OfType().FirstOrDefault(); } public static IEnumerable AncestorsAndSelf(this DependencyObject d) { while (d != null) { yield return d; d = VisualTreeHelper.GetParent(d); } } public static void AddOnce(this IList list, object item) { if (!list.Contains(item)) { list.Add(item); } } } } ================================================ FILE: ILSpy/Controls/TreeView/GeneralAdorner.cs ================================================ // Copyright (c) 2020 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 // 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.Windows; using System.Windows.Documents; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls.TreeView { public class GeneralAdorner : Adorner { public GeneralAdorner(UIElement target) : base(target) { } FrameworkElement child; public FrameworkElement Child { get { return child; } set { if (child != value) { RemoveVisualChild(child); RemoveLogicalChild(child); child = value; AddLogicalChild(value); AddVisualChild(value); InvalidateMeasure(); } } } protected override int VisualChildrenCount { get { return child == null ? 0 : 1; } } protected override Visual GetVisualChild(int index) { return child; } protected override Size MeasureOverride(Size constraint) { if (child != null) { child.Measure(constraint); return child.DesiredSize; } return new Size(); } protected override Size ArrangeOverride(Size finalSize) { if (child != null) { child.Arrange(new Rect(finalSize)); return finalSize; } return new Size(); } } } ================================================ FILE: ILSpy/Controls/TreeView/InsertMarker.cs ================================================ // Copyright (c) 2020 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 // 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.Windows; using System.Windows.Controls; namespace ICSharpCode.ILSpy.Controls.TreeView { public class InsertMarker : Control { static InsertMarker() { DefaultStyleKeyProperty.OverrideMetadata(typeof(InsertMarker), new FrameworkPropertyMetadata(typeof(InsertMarker))); } } } ================================================ FILE: ILSpy/Controls/TreeView/LinesRenderer.cs ================================================ // Copyright (c) 2020 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 // 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.Diagnostics; using System.Windows; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls.TreeView { class LinesRenderer : FrameworkElement { static LinesRenderer() { pen = new Pen(Brushes.LightGray, 1); pen.Freeze(); } static Pen pen; SharpTreeNodeView NodeView { get { return TemplatedParent as SharpTreeNodeView; } } protected override void OnRender(DrawingContext dc) { if (NodeView.Node == null) { // This seems to happen sometimes with DataContext==DisconnectedItem, // though I'm not sure why WPF would call OnRender() on a disconnected node Debug.WriteLine($"LinesRenderer.OnRender() called with DataContext={NodeView.DataContext}"); return; } var indent = NodeView.CalculateIndent(); var p = new Point(indent + 4.5, 0); if (!NodeView.Node.IsRoot || NodeView.ParentTreeView.ShowRootExpander) { dc.DrawLine(pen, new Point(p.X, ActualHeight / 2), new Point(p.X + 10, ActualHeight / 2)); } if (NodeView.Node.IsRoot) return; if (NodeView.Node.IsLast) { dc.DrawLine(pen, p, new Point(p.X, ActualHeight / 2)); } else { dc.DrawLine(pen, p, new Point(p.X, ActualHeight)); } var current = NodeView.Node; while (true) { p.X -= 19; current = current.Parent; if (p.X < 0) break; if (!current.IsLast) { dc.DrawLine(pen, p, new Point(p.X, ActualHeight)); } } } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpGridView.cs ================================================ // Copyright (c) 2020 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 // 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.Windows; using System.Windows.Controls; namespace ICSharpCode.ILSpy.Controls.TreeView { public class SharpGridView : GridView { static SharpGridView() { ItemContainerStyleKey = new ComponentResourceKey(typeof(SharpTreeView), "GridViewItemContainerStyleKey"); } public static ResourceKey ItemContainerStyleKey { get; private set; } protected override object ItemContainerDefaultStyleKey { get { return ItemContainerStyleKey; } } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeNodeView.cs ================================================ // Copyright (c) 2020 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 // 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.ComponentModel; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Controls.TreeView { public class SharpTreeNodeView : Control { static SharpTreeNodeView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeNodeView), new FrameworkPropertyMetadata(typeof(SharpTreeNodeView))); } public static readonly DependencyProperty TextBackgroundProperty = DependencyProperty.Register("TextBackground", typeof(Brush), typeof(SharpTreeNodeView)); public Brush TextBackground { get { return (Brush)GetValue(TextBackgroundProperty); } set { SetValue(TextBackgroundProperty, value); } } public SharpTreeNode Node { get { return DataContext as SharpTreeNode; } } public SharpTreeViewItem ParentItem { get; private set; } public static readonly DependencyProperty CellEditorProperty = DependencyProperty.Register("CellEditor", typeof(Control), typeof(SharpTreeNodeView), new FrameworkPropertyMetadata()); public Control CellEditor { get { return (Control)GetValue(CellEditorProperty); } set { SetValue(CellEditorProperty, value); } } public SharpTreeView ParentTreeView { get { return ParentItem.ParentTreeView; } } internal LinesRenderer LinesRenderer { get; private set; } public override void OnApplyTemplate() { base.OnApplyTemplate(); LinesRenderer = Template.FindName("linesRenderer", this) as LinesRenderer; UpdateTemplate(); } protected override void OnVisualParentChanged(DependencyObject oldParent) { base.OnVisualParentChanged(oldParent); ParentItem = this.FindAncestor(); ParentItem.NodeView = this; } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property == DataContextProperty) { UpdateDataContext(e.OldValue as SharpTreeNode, e.NewValue as SharpTreeNode); } } void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode) { if (newNode != null) { newNode.PropertyChanged += Node_PropertyChanged; if (Template != null) { UpdateTemplate(); } } if (oldNode != null) { oldNode.PropertyChanged -= Node_PropertyChanged; } } void Node_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsEditing") { OnIsEditingChanged(); } else if (e.PropertyName == "IsLast") { if (ParentTreeView.ShowLines) { foreach (var child in Node.VisibleDescendantsAndSelf()) { var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem; if (container != null && container.NodeView != null) { container.NodeView.LinesRenderer.InvalidateVisual(); } } } } else if (e.PropertyName == "IsExpanded") { if (Node.IsExpanded) ParentTreeView.HandleExpanding(Node); } } void OnIsEditingChanged() { var textEditorContainer = Template.FindName("textEditorContainer", this) as Border; if (Node.IsEditing) { if (CellEditor == null) textEditorContainer.Child = new EditTextBox() { Item = ParentItem }; else textEditorContainer.Child = CellEditor; } else { textEditorContainer.Child = null; } } void UpdateTemplate() { var spacer = Template.FindName("spacer", this) as FrameworkElement; spacer.Width = CalculateIndent(); var expander = Template.FindName("expander", this) as ToggleButton; if (ParentTreeView.Root == Node && !ParentTreeView.ShowRootExpander) { expander.Visibility = Visibility.Collapsed; } else { expander.ClearValue(VisibilityProperty); } } internal double CalculateIndent() { var result = 19 * Node.Level; if (ParentTreeView.ShowRoot) { if (!ParentTreeView.ShowRootExpander) { if (ParentTreeView.Root != Node) { result -= 15; } } } else { result -= 19; } if (result < 0) { Debug.WriteLine("Negative indent level detected for node " + Node); return 0; } return result; } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeView.cs ================================================ // Copyright (c) 2020 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 // 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.Collections.Specialized; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Threading; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Controls.TreeView { public class SharpTreeView : ListView { static SharpTreeView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView), new FrameworkPropertyMetadata(typeof(SharpTreeView))); SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView), new FrameworkPropertyMetadata(SelectionMode.Extended)); AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView), new FrameworkPropertyMetadata(2)); DefaultItemContainerStyleKey = new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey"); VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView), new FrameworkPropertyMetadata(VirtualizationMode.Recycling)); RegisterCommands(); } public static ResourceKey DefaultItemContainerStyleKey { get; } public SharpTreeView() { SetResourceReference(ItemContainerStyleProperty, DefaultItemContainerStyleKey); } public static readonly DependencyProperty RootProperty = DependencyProperty.Register(nameof(Root), typeof(SharpTreeNode), typeof(SharpTreeView)); public SharpTreeNode Root { get { return (SharpTreeNode)GetValue(RootProperty); } set { SetValue(RootProperty, value); } } public static readonly DependencyProperty ShowRootProperty = DependencyProperty.Register(nameof(ShowRoot), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(true)); public bool ShowRoot { get { return (bool)GetValue(ShowRootProperty); } set { SetValue(ShowRootProperty, value); } } public static readonly DependencyProperty ShowRootExpanderProperty = DependencyProperty.Register(nameof(ShowRootExpander), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(false)); public bool ShowRootExpander { get { return (bool)GetValue(ShowRootExpanderProperty); } set { SetValue(ShowRootExpanderProperty, value); } } public static readonly DependencyProperty AllowDropOrderProperty = DependencyProperty.Register(nameof(AllowDropOrder), typeof(bool), typeof(SharpTreeView)); public bool AllowDropOrder { get { return (bool)GetValue(AllowDropOrderProperty); } set { SetValue(AllowDropOrderProperty, value); } } public static readonly DependencyProperty ShowLinesProperty = DependencyProperty.Register(nameof(ShowLines), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(true)); public bool ShowLines { get { return (bool)GetValue(ShowLinesProperty); } set { SetValue(ShowLinesProperty, value); } } public static bool GetShowAlternation(DependencyObject obj) { return (bool)obj.GetValue(ShowAlternationProperty); } public static void SetShowAlternation(DependencyObject obj, bool value) { obj.SetValue(ShowAlternationProperty, value); } public static readonly DependencyProperty ShowAlternationProperty = DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)); protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property == RootProperty || e.Property == ShowRootProperty || e.Property == ShowRootExpanderProperty) { Reload(); } } TreeFlattener flattener; bool updatesLocked; public IDisposable LockUpdates() { return new UpdateLock(this); } class UpdateLock : IDisposable { SharpTreeView instance; public UpdateLock(SharpTreeView instance) { this.instance = instance; this.instance.updatesLocked = true; } public void Dispose() { this.instance.updatesLocked = false; } } void Reload() { if (flattener != null) { flattener.Stop(); flattener.CollectionChanged -= flattener_CollectionChanged; } if (Root != null) { if (!(ShowRoot && ShowRootExpander)) { Root.IsExpanded = true; } flattener = new TreeFlattener(Root, ShowRoot); flattener.CollectionChanged += flattener_CollectionChanged; this.ItemsSource = flattener; } } void flattener_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Deselect nodes that are being hidden, if any remain in the tree if (e.Action == NotifyCollectionChangedAction.Remove && Items.Count > 0) { List selectedOldItems = null; foreach (SharpTreeNode node in e.OldItems) { if (node.IsSelected) { if (selectedOldItems == null) selectedOldItems = new List(); selectedOldItems.Add(node); } } if (!updatesLocked && selectedOldItems != null) { var list = SelectedItems.Cast().Except(selectedOldItems).ToList(); UpdateFocusedNode(list, Math.Max(0, e.OldStartingIndex - 1)); } } } void UpdateFocusedNode(List newSelection, int topSelectedIndex) { if (updatesLocked) return; SetSelectedItems(newSelection ?? Enumerable.Empty()); if (SelectedItem == null && this.IsKeyboardFocusWithin) { // if we removed all selected nodes, then move the focus to the node // preceding the first of the old selected nodes SelectedIndex = topSelectedIndex; if (SelectedItem != null) FocusNode((SharpTreeNode)SelectedItem); } } protected override DependencyObject GetContainerForItemOverride() { return new SharpTreeViewItem(); } protected override bool IsItemItsOwnContainerOverride(object item) { return item is SharpTreeViewItem; } protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { base.PrepareContainerForItemOverride(element, item); SharpTreeViewItem container = element as SharpTreeViewItem; container.ParentTreeView = this; // Make sure that the line renderer takes into account the new bound data if (container.NodeView != null) { container.NodeView.LinesRenderer.InvalidateVisual(); } } bool doNotScrollOnExpanding; /// /// Handles the node expanding event in the tree view. /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists). /// internal void HandleExpanding(SharpTreeNode node) { if (doNotScrollOnExpanding) return; SharpTreeNode lastVisibleChild = node; while (true) { SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible); if (tmp != null) { lastVisibleChild = tmp; } else { break; } } if (lastVisibleChild != node) { // Make the the expanded children are visible; but don't scroll down // to much (keep node itself visible) base.ScrollIntoView(lastVisibleChild); // For some reason, this only works properly when delaying it... Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action( delegate { base.ScrollIntoView(node); })); } } protected override void OnKeyDown(KeyEventArgs e) { SharpTreeViewItem container = e.OriginalSource as SharpTreeViewItem; switch (e.Key) { case Key.Left: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { if (container.Node.IsExpanded) { container.Node.IsExpanded = false; } else if (container.Node.Parent != null) { this.FocusNode(container.Node.Parent); } e.Handled = true; } break; case Key.Right: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { if (!container.Node.IsExpanded && container.Node.ShowExpander) { container.Node.IsExpanded = true; } else if (container.Node.Children.Count > 0) { // jump to first child: container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down)); } e.Handled = true; } break; case Key.Return: if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) { e.Handled = true; container.Node.ActivateItem(new WpfWindowsRoutedEventArgs(e)); } break; case Key.Space: if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) { e.Handled = true; if (container.Node.IsCheckable) { if (container.Node.IsChecked == null) // If partially selected, we want to select everything container.Node.IsChecked = true; else container.Node.IsChecked = !container.Node.IsChecked; } else { container.Node.ActivateItem(new WpfWindowsRoutedEventArgs(e)); } } break; case Key.Add: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { container.Node.IsExpanded = true; e.Handled = true; } break; case Key.Subtract: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { container.Node.IsExpanded = false; e.Handled = true; } break; case Key.Multiply: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { container.Node.IsExpanded = true; ExpandRecursively(container.Node); e.Handled = true; } break; case Key.Back: if (IsTextSearchEnabled) { var instance = SharpTreeViewTextSearch.GetInstance(this); if (instance != null) { instance.RevertLastCharacter(); e.Handled = true; } } break; case Key.System: // https://github.com/icsharpcode/ILSpy/issues/3378: // Behavior got broken when upgrading to .NET 8.0 for unknown reasons and without // any more specific known cause. We fix it by not handling Alt+Left or Right // in SharpTreeView so it is handled by the window. if (e.SystemKey is Key.Left or Key.Right) { // yes, we do NOT call base.OnKeyDown(e); in this case. return; } break; } if (!e.Handled) base.OnKeyDown(e); } protected override void OnTextInput(TextCompositionEventArgs e) { if (!string.IsNullOrEmpty(e.Text) && IsTextSearchEnabled && (e.OriginalSource == this || ItemsControl.ItemsControlFromItemContainer(e.OriginalSource as DependencyObject) == this)) { var instance = SharpTreeViewTextSearch.GetInstance(this); if (instance != null) { instance.Search(e.Text); e.Handled = true; } } if (!e.Handled) base.OnTextInput(e); } void ExpandRecursively(SharpTreeNode node) { if (node.CanExpandRecursively) { node.IsExpanded = true; foreach (SharpTreeNode child in node.Children) { ExpandRecursively(child); } } } /// /// Scrolls the specified node in view and sets keyboard focus on it. /// public void FocusNode(SharpTreeNode node) { ArgumentNullException.ThrowIfNull(node); ScrollIntoView(node); // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view. if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { OnFocusItem(node); } else { this.BeginInvoke(DispatcherPriority.Loaded, () => OnFocusItem(node)); } } public void ScrollIntoView(SharpTreeNode node) { if (node == null) throw new ArgumentNullException("node"); doNotScrollOnExpanding = true; foreach (SharpTreeNode ancestor in node.Ancestors()) { ancestor.IsExpanded = true; } doNotScrollOnExpanding = false; base.ScrollIntoView(node); } object OnFocusItem(object item) { FrameworkElement element = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (element != null) { element.Focus(); } return null; } protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { return new SharpTreeViewAutomationPeer(this); } #region Track selection protected override void OnSelectionChanged(SelectionChangedEventArgs e) { foreach (SharpTreeNode node in e.RemovedItems) { node.IsSelected = false; } foreach (SharpTreeNode node in e.AddedItems) { node.IsSelected = true; } base.OnSelectionChanged(e); } #endregion #region Drag and Drop protected override void OnDragEnter(DragEventArgs e) { OnDragOver(e); } protected override void OnDragOver(DragEventArgs e) { e.Effects = DragDropEffects.None; if (Root != null && !ShowRoot) { e.Handled = true; Root.CanDrop(new WpfWindowsDragEventArgs(e), Root.Children.Count); } } protected override void OnDrop(DragEventArgs e) { e.Effects = DragDropEffects.None; if (Root != null && !ShowRoot) { e.Handled = true; Root.InternalDrop(new WpfWindowsDragEventArgs(e), Root.Children.Count); } } internal void HandleDragEnter(SharpTreeViewItem item, DragEventArgs e) { HandleDragOver(item, e); } internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e) { HidePreview(); var target = GetDropTarget(item, e); if (target != null) { e.Handled = true; ShowPreview(target.Item, target.Place); } } internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e) { try { HidePreview(); var target = GetDropTarget(item, e); if (target != null) { e.Handled = true; target.Node.InternalDrop(new WpfWindowsDragEventArgs(e), target.Index); } } catch (Exception ex) { Debug.WriteLine(ex.ToString()); throw; } } internal void HandleDragLeave(SharpTreeViewItem item, DragEventArgs e) { HidePreview(); e.Handled = true; } class DropTarget { public SharpTreeViewItem Item; public DropPlace Place; public double Y; public SharpTreeNode Node; public int Index; } DropTarget GetDropTarget(SharpTreeViewItem item, DragEventArgs e) { var dropTargets = BuildDropTargets(item, e); var y = e.GetPosition(item).Y; foreach (var target in dropTargets) { if (target.Y >= y) { return target; } } return null; } List BuildDropTargets(SharpTreeViewItem item, DragEventArgs e) { var result = new List(); var node = item.Node; if (AllowDropOrder) { TryAddDropTarget(result, item, DropPlace.Before, e); } TryAddDropTarget(result, item, DropPlace.Inside, e); if (AllowDropOrder) { if (node.IsExpanded && node.Children.Count > 0) { var firstChildItem = ItemContainerGenerator.ContainerFromItem(node.Children[0]) as SharpTreeViewItem; TryAddDropTarget(result, firstChildItem, DropPlace.Before, e); } else { TryAddDropTarget(result, item, DropPlace.After, e); } } var h = item.ActualHeight; var y1 = 0.2 * h; var y2 = h / 2; var y3 = h - y1; if (result.Count == 2) { if (result[0].Place == DropPlace.Inside && result[1].Place != DropPlace.Inside) { result[0].Y = y3; } else if (result[0].Place != DropPlace.Inside && result[1].Place == DropPlace.Inside) { result[0].Y = y1; } else { result[0].Y = y2; } } else if (result.Count == 3) { result[0].Y = y1; result[1].Y = y3; } if (result.Count > 0) { result[result.Count - 1].Y = h; } return result; } void TryAddDropTarget(List targets, SharpTreeViewItem item, DropPlace place, DragEventArgs e) { SharpTreeNode node; int index; GetNodeAndIndex(item, place, out node, out index); if (node != null) { e.Effects = DragDropEffects.None; if (node.CanDrop(new WpfWindowsDragEventArgs(e), index)) { DropTarget target = new DropTarget() { Item = item, Place = place, Node = node, Index = index }; targets.Add(target); } } } void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index) { node = null; index = 0; if (place == DropPlace.Inside) { node = item.Node; index = node.Children.Count; } else if (place == DropPlace.Before) { if (item.Node.Parent != null) { node = item.Node.Parent; index = node.Children.IndexOf(item.Node); } } else { if (item.Node.Parent != null) { node = item.Node.Parent; index = node.Children.IndexOf(item.Node) + 1; } } } SharpTreeNodeView previewNodeView; InsertMarker insertMarker; DropPlace previewPlace; enum DropPlace { Before, Inside, After } void ShowPreview(SharpTreeViewItem item, DropPlace place) { previewNodeView = item.NodeView; previewPlace = place; if (place == DropPlace.Inside) { previewNodeView.SetResourceReference(SharpTreeNodeView.TextBackgroundProperty, SystemColors.HighlightBrushKey); previewNodeView.SetResourceReference(SharpTreeNodeView.ForegroundProperty, SystemColors.HighlightTextBrushKey); } else { if (insertMarker == null) { var adornerLayer = AdornerLayer.GetAdornerLayer(this); var adorner = new GeneralAdorner(this); insertMarker = new InsertMarker(); adorner.Child = insertMarker; adornerLayer.Add(adorner); } insertMarker.Visibility = Visibility.Visible; var p1 = previewNodeView.TransformToVisual(this).Transform(new Point()); var p = new Point(p1.X + previewNodeView.CalculateIndent() + 4.5, p1.Y - 3); if (place == DropPlace.After) { p.Y += previewNodeView.ActualHeight; } insertMarker.Margin = new Thickness(p.X, p.Y, 0, 0); SharpTreeNodeView secondNodeView = null; var index = flattener.IndexOf(item.Node); if (place == DropPlace.Before) { if (index > 0) { secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index - 1) as SharpTreeViewItem).NodeView; } } else if (index + 1 < flattener.Count) { secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index + 1) as SharpTreeViewItem).NodeView; } var w = p1.X + previewNodeView.ActualWidth - p.X; if (secondNodeView != null) { var p2 = secondNodeView.TransformToVisual(this).Transform(new Point()); w = Math.Max(w, p2.X + secondNodeView.ActualWidth - p.X); } insertMarker.Width = w + 10; } } void HidePreview() { if (previewNodeView != null) { previewNodeView.ClearValue(SharpTreeNodeView.TextBackgroundProperty); previewNodeView.ClearValue(SharpTreeNodeView.ForegroundProperty); if (insertMarker != null) { insertMarker.Visibility = Visibility.Collapsed; } previewNodeView = null; } } #endregion #region Cut / Copy / Paste / Delete Commands static void RegisterCommands() { CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView), new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut)); CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView), new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy)); CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView), new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste)); CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView), new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete)); } static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e) { } static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = false; } static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e) { } static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = false; } static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e) { } static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = false; } static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e) { SharpTreeView treeView = (SharpTreeView)sender; treeView.updatesLocked = true; int selectedIndex = -1; try { foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray()) { if (selectedIndex == -1) selectedIndex = treeView.flattener.IndexOf(node); node.Delete(); } } finally { treeView.updatesLocked = false; treeView.UpdateFocusedNode(null, Math.Max(0, selectedIndex - 1)); } } static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e) { SharpTreeView treeView = (SharpTreeView)sender; e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete()); } /// /// Gets the selected items which do not have any of their ancestors selected. /// public IEnumerable GetTopLevelSelection() { var selection = this.SelectedItems.OfType().ToHashSet(); return selection.Where(item => item.Ancestors().All(a => !selection.Contains(a))); } #endregion public void SetSelectedNodes(IEnumerable nodes) { bool success = this.SetSelectedItems(nodes.ToList()); Debug.Assert(success); } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeView.xaml ================================================ ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeViewAutomationPeer.cs ================================================ // Copyright (c) 2020 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 // 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.Windows.Automation.Peers; namespace ICSharpCode.ILSpy.Controls.TreeView { class SharpTreeViewAutomationPeer : FrameworkElementAutomationPeer { internal SharpTreeViewAutomationPeer(SharpTreeView owner) : base(owner) { } protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Tree; } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeViewItem.cs ================================================ // Copyright (c) 2020 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 // 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.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Controls.TreeView { public class SharpTreeViewItem : ListViewItem { static SharpTreeViewItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeViewItem), new FrameworkPropertyMetadata(typeof(SharpTreeViewItem))); } public SharpTreeNode Node { get { return DataContext as SharpTreeNode; } } public SharpTreeNodeView NodeView { get; internal set; } public SharpTreeView ParentTreeView { get; internal set; } protected override void OnKeyDown(KeyEventArgs e) { switch (e.Key) { case Key.F2: if (Node.IsEditable && ParentTreeView != null && ParentTreeView.SelectedItems.Count == 1 && ParentTreeView.SelectedItems[0] == Node) { Node.IsEditing = true; e.Handled = true; } break; case Key.Escape: if (Node.IsEditing) { Node.IsEditing = false; e.Handled = true; } break; } } protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { return new SharpTreeViewItemAutomationPeer(this); } #region Mouse Point startPoint; bool wasSelected; bool wasDoubleClick; protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { wasSelected = IsSelected; if (!IsSelected) { base.OnMouseLeftButtonDown(e); } if (Mouse.LeftButton == MouseButtonState.Pressed) { startPoint = e.GetPosition(null); CaptureMouse(); if (e.ClickCount == 2) { wasDoubleClick = true; } } } protected override void OnMouseMove(MouseEventArgs e) { if (IsMouseCaptured) { var currentPoint = e.GetPosition(null); if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance || Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) { var selection = ParentTreeView.GetTopLevelSelection().ToArray(); if (Node.CanDrag(selection)) { Node.StartDrag(this, selection, new WpfWindowsDragDropManager()); } } } } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { if (wasDoubleClick) { wasDoubleClick = false; Node.ActivateItem(new WpfWindowsRoutedEventArgs(e)); if (!e.Handled) { if (!Node.IsRoot || ParentTreeView.ShowRootExpander) { Node.IsExpanded = !Node.IsExpanded; } } } ReleaseMouseCapture(); if (wasSelected) { base.OnMouseLeftButtonDown(e); } } protected override void OnMouseUp(MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Middle) { Node.ActivateItemSecondary(new WpfWindowsRoutedEventArgs(e)); } else { base.OnMouseUp(e); } } #endregion #region Drag and Drop protected override void OnDragEnter(DragEventArgs e) { ParentTreeView.HandleDragEnter(this, e); } protected override void OnDragOver(DragEventArgs e) { ParentTreeView.HandleDragOver(this, e); } protected override void OnDrop(DragEventArgs e) { ParentTreeView.HandleDrop(this, e); } protected override void OnDragLeave(DragEventArgs e) { ParentTreeView.HandleDragLeave(this, e); } #endregion } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeViewItemAutomationPeer.cs ================================================ // Copyright (c) 2020 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 // 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.ComponentModel; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Controls.TreeView { class SharpTreeViewItemAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider { internal SharpTreeViewItemAutomationPeer(SharpTreeViewItem owner) : base(owner) { SharpTreeViewItem.DataContextChanged += OnDataContextChanged; SharpTreeNode node = SharpTreeViewItem.DataContext as SharpTreeNode; if (node == null) return; node.PropertyChanged += OnPropertyChanged; } private SharpTreeViewItem SharpTreeViewItem { get { return (SharpTreeViewItem)base.Owner; } } protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.TreeItem; } public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.ExpandCollapse) return this; return base.GetPattern(patternInterface); } public void Collapse() { } public void Expand() { } public ExpandCollapseState ExpandCollapseState { get { SharpTreeNode node = SharpTreeViewItem.DataContext as SharpTreeNode; if (node == null || !node.ShowExpander) return ExpandCollapseState.LeafNode; return node.IsExpanded ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; } } private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName != "IsExpanded") return; SharpTreeNode node = sender as SharpTreeNode; if (node == null || node.Children.Count == 0) return; bool newValue = node.IsExpanded; bool oldValue = !newValue; RaisePropertyChangedEvent( ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, oldValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed, newValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed); } private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { SharpTreeNode oldNode = e.OldValue as SharpTreeNode; if (oldNode != null) oldNode.PropertyChanged -= OnPropertyChanged; SharpTreeNode newNode = e.NewValue as SharpTreeNode; if (newNode != null) newNode.PropertyChanged += OnPropertyChanged; } } } ================================================ FILE: ILSpy/Controls/TreeView/SharpTreeViewTextSearch.cs ================================================ // Copyright (c) 2020 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 // 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.Runtime.InteropServices; using System.Windows; using System.Windows.Threading; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.Controls.TreeView { /// /// Custom TextSearch-implementation. /// Fixes #67 - Moving to class member in tree view by typing in first character of member name selects parent assembly /// public partial class SharpTreeViewTextSearch : DependencyObject { static readonly DependencyPropertyKey TextSearchInstancePropertyKey = DependencyProperty.RegisterAttachedReadOnly("TextSearchInstance", typeof(SharpTreeViewTextSearch), typeof(SharpTreeViewTextSearch), new FrameworkPropertyMetadata(null)); static readonly DependencyProperty TextSearchInstanceProperty = TextSearchInstancePropertyKey.DependencyProperty; DispatcherTimer timer; bool isActive; int lastMatchIndex; string matchPrefix; readonly Stack inputStack; readonly SharpTreeView treeView; private SharpTreeViewTextSearch(SharpTreeView treeView) { if (treeView == null) throw new ArgumentNullException(nameof(treeView)); this.treeView = treeView; inputStack = new Stack(8); ClearState(); } public static SharpTreeViewTextSearch GetInstance(SharpTreeView sharpTreeView) { var textSearch = (SharpTreeViewTextSearch)sharpTreeView.GetValue(TextSearchInstanceProperty); if (textSearch == null) { textSearch = new SharpTreeViewTextSearch(sharpTreeView); sharpTreeView.SetValue(TextSearchInstancePropertyKey, textSearch); } return textSearch; } public bool RevertLastCharacter() { if (!isActive || inputStack.Count == 0) return false; matchPrefix = matchPrefix.Substring(0, matchPrefix.Length - inputStack.Pop().Length); ResetTimeout(); return true; } public bool Search(string nextChar) { int startIndex = isActive ? lastMatchIndex : Math.Max(0, treeView.SelectedIndex); bool lookBackwards = inputStack.Count > 0 && string.Compare(inputStack.Peek(), nextChar, StringComparison.OrdinalIgnoreCase) == 0; int nextMatchIndex = IndexOfMatch(matchPrefix + nextChar, startIndex, lookBackwards, out bool wasNewCharUsed); if (nextMatchIndex != -1) { if (!isActive || nextMatchIndex != startIndex) { treeView.SelectedItem = treeView.Items[nextMatchIndex]; treeView.FocusNode((SharpTreeNode)treeView.SelectedItem); lastMatchIndex = nextMatchIndex; } if (wasNewCharUsed) { matchPrefix += nextChar; inputStack.Push(nextChar); } isActive = true; } if (isActive) { ResetTimeout(); } return nextMatchIndex != -1; } int IndexOfMatch(string needle, int startIndex, bool tryBackward, out bool charWasUsed) { charWasUsed = false; if (treeView.Items.Count == 0 || string.IsNullOrEmpty(needle)) return -1; int index = -1; int fallbackIndex = -1; bool fallbackMatch = false; int i = startIndex; var comparisonType = treeView.IsTextSearchCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; do { var item = (SharpTreeNode)treeView.Items[i]; if (item != null && item.Text != null) { string text = item.Text.ToString(); if (text.StartsWith(needle, comparisonType)) { charWasUsed = true; index = i; break; } if (tryBackward) { if (fallbackMatch && matchPrefix != string.Empty) { if (fallbackIndex == -1 && text.StartsWith(matchPrefix, comparisonType)) { fallbackIndex = i; } } else { fallbackMatch = true; } } } i++; if (i >= treeView.Items.Count) i = 0; } while (i != startIndex); return index == -1 ? fallbackIndex : index; } void ClearState() { isActive = false; matchPrefix = string.Empty; lastMatchIndex = -1; inputStack.Clear(); timer?.Stop(); timer = null; } void ResetTimeout() { if (timer == null) { timer = new DispatcherTimer(DispatcherPriority.Normal); timer.Tick += (sender, e) => ClearState(); } else { timer.Stop(); } timer.Interval = TimeSpan.FromMilliseconds(NativeMethods.GetDoubleClickTime() * 2); timer.Start(); } } } ================================================ FILE: ILSpy/Controls/TreeView/WpfWindowsDataObject.cs ================================================ using System.Windows; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.Controls.TreeView { public sealed class WpfWindowsDataObject : IPlatformDataObject { private readonly IDataObject _dataObject; public WpfWindowsDataObject(IDataObject dataObject) { _dataObject = dataObject; } public object GetData(string format) { return _dataObject.GetData(format); } public bool GetDataPresent(string format) { return _dataObject.GetDataPresent(format); } public void SetData(string format, object data) { _dataObject.SetData(format, data); } object IPlatformDataObject.UnderlyingDataObject => _dataObject; } } ================================================ FILE: ILSpy/Controls/TreeView/WpfWindowsDragDropManager.cs ================================================ using System.Windows; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.Controls.TreeView { public class WpfWindowsDragDropManager : IPlatformDragDrop { public XPlatDragDropEffects DoDragDrop(object dragSource, IPlatformDataObject data, XPlatDragDropEffects allowedEffects) { return (XPlatDragDropEffects)DragDrop.DoDragDrop(dragSource as DependencyObject, data.UnderlyingDataObject, (DragDropEffects)allowedEffects); } } } ================================================ FILE: ILSpy/Controls/TreeView/WpfWindowsDragEventArgs.cs ================================================ using System.Windows; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.Controls.TreeView { public class WpfWindowsDragEventArgs : IPlatformDragEventArgs { private readonly DragEventArgs _eventArgs; public WpfWindowsDragEventArgs(DragEventArgs eventArgs) { _eventArgs = eventArgs; } public XPlatDragDropEffects Effects { get => (XPlatDragDropEffects)_eventArgs.Effects; set => _eventArgs.Effects = (DragDropEffects)value; } public IPlatformDataObject Data => new WpfWindowsDataObject(_eventArgs.Data); } } ================================================ FILE: ILSpy/Controls/TreeView/WpfWindowsRoutedEventArgs.cs ================================================ using System.Windows; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.Controls.TreeView { public class WpfWindowsRoutedEventArgs : IPlatformRoutedEventArgs { private readonly RoutedEventArgs _eventArgs; public WpfWindowsRoutedEventArgs(RoutedEventArgs eventArgs) { _eventArgs = eventArgs; } public bool Handled { get => _eventArgs.Handled; set => _eventArgs.Handled = value; } } } ================================================ FILE: ILSpy/Controls/XamlResourceExtension.cs ================================================ // 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 // 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.Windows.Markup; namespace ICSharpCode.ILSpy.Controls { class XamlResourceExtension : MarkupExtension { readonly string name; public XamlResourceExtension(string name) { this.name = name ?? throw new ArgumentNullException(nameof(name)); } public override object ProvideValue(IServiceProvider serviceProvider) { return Images.Load(null, name); } } } ================================================ FILE: ILSpy/Controls/ZoomButtons.cs ================================================ // Copyright (c) 2014 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 // 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 System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls { public class ZoomButtons : RangeBase { static ZoomButtons() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomButtons), new FrameworkPropertyMetadata(typeof(ZoomButtons))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); var uxPlus = (ButtonBase)Template.FindName("uxPlus", this); var uxMinus = (ButtonBase)Template.FindName("uxMinus", this); var uxReset = (ButtonBase)Template.FindName("uxReset", this); if (uxPlus != null) uxPlus.Click += OnZoomInClick; if (uxMinus != null) uxMinus.Click += OnZoomOutClick; if (uxReset != null) uxReset.Click += OnResetClick; } const double ZoomFactor = 1.1; void OnZoomInClick(object sender, EventArgs e) { SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value * ZoomFactor)); } void OnZoomOutClick(object sender, EventArgs e) { SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value / ZoomFactor)); } void OnResetClick(object sender, EventArgs e) { SetCurrentValue(ValueProperty, 1.0); } } } ================================================ FILE: ILSpy/Controls/ZoomScrollViewer.cs ================================================ // Copyright (c) 2014 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 // 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.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls { public class ZoomScrollViewer : ScrollViewer { static ZoomScrollViewer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(typeof(ZoomScrollViewer))); } public static readonly DependencyProperty CurrentZoomProperty = DependencyProperty.Register("CurrentZoom", typeof(double), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CalculateZoomButtonCollapsed, CoerceZoom)); public double CurrentZoom { get { return (double)GetValue(CurrentZoomProperty); } set { SetValue(CurrentZoomProperty, value); } } static object CoerceZoom(DependencyObject d, object baseValue) { var zoom = (double)baseValue; ZoomScrollViewer sv = (ZoomScrollViewer)d; return Math.Max(sv.MinimumZoom, Math.Min(sv.MaximumZoom, zoom)); } public static readonly DependencyProperty MinimumZoomProperty = DependencyProperty.Register("MinimumZoom", typeof(double), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(0.2)); public double MinimumZoom { get { return (double)GetValue(MinimumZoomProperty); } set { SetValue(MinimumZoomProperty, value); } } public static readonly DependencyProperty MaximumZoomProperty = DependencyProperty.Register("MaximumZoom", typeof(double), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(5.0)); public double MaximumZoom { get { return (double)GetValue(MaximumZoomProperty); } set { SetValue(MaximumZoomProperty, value); } } public static readonly DependencyProperty MouseWheelZoomProperty = DependencyProperty.Register("MouseWheelZoom", typeof(bool), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(true)); public bool MouseWheelZoom { get { return (bool)GetValue(MouseWheelZoomProperty); } set { SetValue(MouseWheelZoomProperty, value); } } public static readonly DependencyProperty AlwaysShowZoomButtonsProperty = DependencyProperty.Register("AlwaysShowZoomButtons", typeof(bool), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(false, CalculateZoomButtonCollapsed)); public bool AlwaysShowZoomButtons { get { return (bool)GetValue(AlwaysShowZoomButtonsProperty); } set { SetValue(AlwaysShowZoomButtonsProperty, value); } } static readonly DependencyPropertyKey ComputedZoomButtonCollapsedPropertyKey = DependencyProperty.RegisterReadOnly("ComputedZoomButtonCollapsed", typeof(bool), typeof(ZoomScrollViewer), new FrameworkPropertyMetadata(true)); public static readonly DependencyProperty ComputedZoomButtonCollapsedProperty = ComputedZoomButtonCollapsedPropertyKey.DependencyProperty; public bool ComputedZoomButtonCollapsed { get { return (bool)GetValue(ComputedZoomButtonCollapsedProperty); } private set { SetValue(ComputedZoomButtonCollapsedPropertyKey, value); } } static void CalculateZoomButtonCollapsed(DependencyObject d, DependencyPropertyChangedEventArgs e) { ZoomScrollViewer z = d as ZoomScrollViewer; if (z != null) z.ComputedZoomButtonCollapsed = (z.AlwaysShowZoomButtons == false) && (z.CurrentZoom == 1.0); } protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) { if (!e.Handled && Keyboard.Modifiers == ModifierKeys.Control && MouseWheelZoom) { double oldZoom = CurrentZoom; double newZoom = RoundToOneIfClose(CurrentZoom * Math.Pow(1.001, e.Delta)); newZoom = Math.Max(this.MinimumZoom, Math.Min(this.MaximumZoom, newZoom)); // adjust scroll position so that mouse stays over the same virtual coordinate ContentPresenter presenter = Template.FindName("PART_Presenter", this) as ContentPresenter; Vector relMousePos; if (presenter != null) { Point mousePos = e.GetPosition(presenter); relMousePos = new Vector(mousePos.X / presenter.ActualWidth, mousePos.Y / presenter.ActualHeight); } else { relMousePos = new Vector(0.5, 0.5); } Point scrollOffset = new Point(this.HorizontalOffset, this.VerticalOffset); Vector oldHalfViewport = new Vector(this.ViewportWidth / 2, this.ViewportHeight / 2); Vector newHalfViewport = oldHalfViewport / newZoom * oldZoom; Point oldCenter = scrollOffset + oldHalfViewport; Point virtualMousePos = scrollOffset + new Vector(relMousePos.X * this.ViewportWidth, relMousePos.Y * this.ViewportHeight); // As newCenter, we want to choose a point between oldCenter and virtualMousePos. The more we zoom in, the closer // to virtualMousePos. We'll create the line x = oldCenter + lambda * (virtualMousePos-oldCenter). // On this line, we need to choose lambda between -1 and 1: // -1 = zoomed out completely // 0 = zoom unchanged // +1 = zoomed in completely // But the zoom factor (newZoom/oldZoom) we have is in the range [0,+Infinity]. // Basically, I just played around until I found a function that maps this to [-1,1] and works well. // "f" is squared because otherwise the mouse simply stays over virtualMousePos, but I wanted virtualMousePos // to move towards the middle -> squaring f causes lambda to be closer to 1, giving virtualMousePos more weight // then oldCenter. double f = Math.Min(newZoom, oldZoom) / Math.Max(newZoom, oldZoom); double lambda = 1 - f * f; if (oldZoom > newZoom) lambda = -lambda; Debug.Print("old: " + oldZoom + ", new: " + newZoom); Point newCenter = oldCenter + lambda * (virtualMousePos - oldCenter); scrollOffset = newCenter - newHalfViewport; SetCurrentValue(CurrentZoomProperty, newZoom); this.ScrollToHorizontalOffset(scrollOffset.X); this.ScrollToVerticalOffset(scrollOffset.Y); e.Handled = true; } base.OnPreviewMouseWheel(e); } internal static double RoundToOneIfClose(double val) { if (Math.Abs(val - 1.0) < 0.001) return 1.0; else return val; } } } ================================================ FILE: ILSpy/Controls/ZoomScrollViewer.xaml ================================================ ================================================ FILE: ILSpy/DecompilationOptions.cs ================================================ // 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 // 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.Threading; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpyX; using DecompilerSettings = ICSharpCode.ILSpyX.Settings.DecompilerSettings; namespace ICSharpCode.ILSpy { /// /// Options passed to the decompiler. /// public class DecompilationOptions { /// /// Gets whether a full decompilation (all members recursively) is desired. /// If this option is false, language bindings are allowed to show the only headers of the decompiled element's children. /// public bool FullDecompilation { get; set; } /// /// Gets/Sets the directory into which the project is saved. /// public string SaveAsProjectDirectory { get; set; } /// /// Gets/sets whether invalid identifiers should be escaped (and therefore the code be made compilable). /// This setting is ignored in case is set. /// public bool EscapeInvalidIdentifiers { get; set; } /// /// Gets the cancellation token that is used to abort the decompiler. /// /// /// Decompilers should regularly call options.CancellationToken.ThrowIfCancellationRequested(); /// to allow for cooperative cancellation of the decompilation task. /// public CancellationToken CancellationToken { get; set; } /// /// Gets the progress reporter. /// /// /// If decompilers do not implement progress reporting, an indeterminate wait bar is displayed. /// public IProgress Progress { get; set; } /// /// Gets the settings for the decompiler. /// public DecompilerSettings DecompilerSettings { get; private set; } /// /// Gets/sets an optional state of a decompiler text view. /// /// /// This state is used to restore test view's state when decompilation is started by Go Back/Forward action. /// public TextView.DecompilerTextViewState TextViewState { get; set; } /// /// Used internally for debugging. /// internal int StepLimit = int.MaxValue; internal bool IsDebug = false; public DecompilationOptions(LanguageVersion version, DecompilerSettings settings, DisplaySettings displaySettings) { if (!Enum.TryParse(version?.Version, out Decompiler.CSharp.LanguageVersion languageVersion)) languageVersion = Decompiler.CSharp.LanguageVersion.Latest; var newSettings = this.DecompilerSettings = settings.Clone(); newSettings.SetLanguageVersion(languageVersion); newSettings.ExpandMemberDefinitions = displaySettings.ExpandMemberDefinitions; newSettings.ExpandUsingDeclarations = displaySettings.ExpandUsingDeclarations; newSettings.FoldBraces = displaySettings.FoldBraces; newSettings.ShowDebugInfo = displaySettings.ShowDebugInfo; newSettings.CSharpFormattingOptions.IndentationString = GetIndentationString(displaySettings); } private string GetIndentationString(DisplaySettings displaySettings) { if (displaySettings.IndentationUseTabs) { int numberOfTabs = displaySettings.IndentationSize / displaySettings.IndentationTabSize; int numberOfSpaces = displaySettings.IndentationSize % displaySettings.IndentationTabSize; return new string('\t', numberOfTabs) + new string(' ', numberOfSpaces); } return new string(' ', displaySettings.IndentationSize); } } } ================================================ FILE: ILSpy/Docking/CloseAllDocumentsCommand.cs ================================================ using System.Composition; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.Docking { [ExportMainMenuCommand(Header = nameof(Resources.Window_CloseAllDocuments), ParentMenuID = nameof(Resources._Window))] [Shared] class CloseAllDocumentsCommand(DockWorkspace dockWorkspace) : SimpleCommand { public override void Execute(object parameter) { dockWorkspace.CloseAllTabs(); } } [ExportMainMenuCommand(Header = nameof(Resources.Window_ResetLayout), ParentMenuID = nameof(Resources._Window))] [Shared] class ResetLayoutCommand(DockWorkspace dockWorkspace) : SimpleCommand { public override void Execute(object parameter) { dockWorkspace.ResetLayout(); } } } ================================================ FILE: ILSpy/Docking/DockLayoutSettings.cs ================================================ // Copyright (c) 2019 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 // 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.IO; using System.Linq; using System.Xml.Linq; using AvalonDock.Layout.Serialization; namespace ICSharpCode.ILSpy.Docking { public class DockLayoutSettings { /// NOTE: do NOT remove any of the (empty) sections, the deserializer is not very resilient and expects this exact order of elements! private const string DefaultLayout = @" "; private string rawSettings; public bool Valid => rawSettings != null; public void Reset() { this.rawSettings = DefaultLayout; } public DockLayoutSettings(XElement element) { if ((element != null) && element.HasElements) { rawSettings = element.Elements().FirstOrDefault()?.ToString(); } } public XElement SaveAsXml() { try { return XElement.Parse(rawSettings); } catch (Exception) { return null; } } public void Deserialize(XmlLayoutSerializer serializer) { if (!Valid) rawSettings = DefaultLayout; try { Deserialize(rawSettings); } catch (Exception) { Deserialize(DefaultLayout); } void Deserialize(string settings) { using (StringReader reader = new StringReader(settings)) { serializer.Deserialize(reader); } } } public void Serialize(XmlLayoutSerializer serializer) { using (StringWriter fs = new StringWriter()) { serializer.Serialize(fs); rawSettings = fs.ToString(); } } } } ================================================ FILE: ILSpy/Docking/DockWorkspace.cs ================================================ // Copyright (c) 2019 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 // 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.Collections.ObjectModel; using System.Collections.Specialized; using System.Composition; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Threading; using AvalonDock; using AvalonDock.Layout; using AvalonDock.Layout.Serialization; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.ILSpy.Analyzers; using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Composition; using TomsToolbox.Essentials; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Docking { [Export] [Shared] public class DockWorkspace : ObservableObjectBase, ILayoutUpdateStrategy { private readonly IExportProvider exportProvider; private readonly ObservableCollection tabPages = []; private ReadOnlyCollection toolPanes; readonly SessionSettings sessionSettings; private DockingManager DockingManager => exportProvider.GetExportedValue(); public DockWorkspace(SettingsService settingsService, IExportProvider exportProvider) { this.exportProvider = exportProvider; sessionSettings = settingsService.SessionSettings; this.tabPages.CollectionChanged += TabPages_CollectionChanged; TabPages = new(tabPages); MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); } private void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems is not { } oldItems) return; foreach (var tab in tabPages.ToArray()) { var state = tab.GetState(); var decompiledNodes = state?.DecompiledNodes; if (decompiledNodes == null) continue; bool found = decompiledNodes .Select(node => node.Ancestors().OfType().LastOrDefault()) .ExceptNullItems() .Any(assemblyNode => !oldItems.Contains(assemblyNode.LoadedAssembly)); if (!found) { tabPages.Remove(tab); } } if (tabPages.Count == 0) { AddTabPage(); } } private void TabPages_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { if (e.NewItems?[0] is TabPageModel model) { ActiveTabPage = model; model.IsActive = true; model.IsVisible = true; } } bool canClose = tabPages.Count > 1; foreach (var item in tabPages) { item.IsCloseable = canClose; } MessageBus.Send(this, new TabPagesCollectionChangedEventArgs(e)); } public TabPageModel AddTabPage(TabPageModel tabPage = null) { TabPageModel item = tabPage ?? exportProvider.GetExportedValue(); tabPages.Add(item); return item; } public ReadOnlyObservableCollection TabPages { get; } public ReadOnlyCollection ToolPanes => toolPanes ??= exportProvider .GetExportedValues("ToolPane") .OrderBy(item => item.Title) .ToArray() .AsReadOnly(); public bool ShowToolPane(string contentId) { var pane = ToolPanes.FirstOrDefault(p => p.ContentId == contentId); if (pane != null) { pane.Show(); return true; } return false; } public void Remove(PaneModel model) { switch (model) { case TabPageModel document: tabPages.Remove(document); break; case ToolPaneModel tool: tool.IsVisible = false; break; } } private TabPageModel activeTabPage = null; public TabPageModel ActiveTabPage { get { return activeTabPage; } set { if (!SetProperty(ref activeTabPage, value)) return; var state = value?.GetState(); if (state == null) return; MessageBus.Send(this, new ActiveTabPageChangedEventArgs(value?.GetState())); } } public PaneModel ActivePane { get => DockingManager.ActiveContent as PaneModel; set => DockingManager.ActiveContent = value; } public void InitializeLayout() { if (tabPages.Count == 0) { // Make sure there is at least one tab open AddTabPage(); } DockingManager.LayoutUpdateStrategy = this; XmlLayoutSerializer serializer = new XmlLayoutSerializer(DockingManager); serializer.LayoutSerializationCallback += LayoutSerializationCallback; try { sessionSettings.DockLayout.Deserialize(serializer); } finally { serializer.LayoutSerializationCallback -= LayoutSerializationCallback; } DockingManager.SetBinding(DockingManager.AnchorablesSourceProperty, new Binding(nameof(ToolPanes))); DockingManager.SetBinding(DockingManager.DocumentsSourceProperty, new Binding(nameof(TabPages))); } void LayoutSerializationCallback(object sender, LayoutSerializationCallbackEventArgs e) { switch (e.Model) { case LayoutAnchorable la: e.Content = this.ToolPanes.FirstOrDefault(p => p.ContentId == la.ContentId); e.Cancel = e.Content == null; la.CanDockAsTabbedDocument = false; if (e.Content is ToolPaneModel toolPaneModel) { e.Cancel = toolPaneModel.IsVisible; toolPaneModel.IsVisible = true; } break; default: e.Cancel = true; break; } } public void ShowText(AvalonEditTextOutput textOutput) { ActiveTabPage.ShowTextView(textView => textView.ShowText(textOutput)); } public Task RunWithCancellation(Func> taskCreation) { return ActiveTabPage.ShowTextViewAsync(textView => textView.RunWithCancellation(taskCreation)); } public Task RunWithCancellation(Func> taskCreation, string progressTitle) { return ActiveTabPage.ShowTextViewAsync(textView => textView.RunWithCancellation(taskCreation, progressTitle)); } internal void ShowNodes(AvalonEditTextOutput output, TreeNodes.ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting) { ActiveTabPage.ShowTextView(textView => textView.ShowNodes(output, nodes, highlighting)); } internal void CloseAllTabs() { var activePage = ActiveTabPage; tabPages.RemoveWhere(page => page != activePage); } internal void ResetLayout() { foreach (var pane in ToolPanes) { pane.IsVisible = false; } CloseAllTabs(); sessionSettings.DockLayout.Reset(); InitializeLayout(); App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => MessageBus.Send(this, new ResetLayoutEventArgs())); } static readonly PropertyInfo previousContainerProperty = typeof(LayoutContent).GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance); public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer) { if (!(anchorableToShow.Content is LegacyToolPaneModel legacyContent)) return false; anchorableToShow.CanDockAsTabbedDocument = false; LayoutAnchorablePane previousContainer; switch (legacyContent.Location) { case LegacyToolPaneLocation.Top: previousContainer = GetContainer(); previousContainer.Children.Add(anchorableToShow); return true; case LegacyToolPaneLocation.Bottom: previousContainer = GetContainer(); previousContainer.Children.Add(anchorableToShow); return true; default: return false; } LayoutAnchorablePane GetContainer() { var anchorable = layout.Descendents().OfType().FirstOrDefault(x => x.Content is T) ?? layout.Hidden.First(x => x.Content is T); return (LayoutAnchorablePane)previousContainerProperty.GetValue(anchorable) ?? (LayoutAnchorablePane)anchorable.Parent; } } public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown) { anchorableShown.IsActive = true; anchorableShown.IsSelected = true; } public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer) { return false; } public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown) { } // Dummy property to make the XAML designer happy, the model is provided by the AvalonDock PaneStyleSelectors, not by the DockWorkspace, but the designer assumes the data context in the PaneStyleSelectors is the DockWorkspace. public PaneModel Model { get; } = null; } } ================================================ FILE: ILSpy/Docking/PaneStyleSelector.cs ================================================ using System.Windows; using System.Windows.Controls; using ICSharpCode.ILSpy.ViewModels; // Copyright (c) 2019 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 // 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. namespace ICSharpCode.ILSpy.Docking { public class PaneStyleSelector : StyleSelector { public Style ToolPaneStyle { get; set; } public Style TabPageStyle { get; set; } public override Style SelectStyle(object item, DependencyObject container) { if (item is TabPageModel) return TabPageStyle; if (item is ToolPaneModel) return ToolPaneStyle; return base.SelectStyle(item, container); } } } ================================================ FILE: ILSpy/Docking/TabPageGuardConverter.cs ================================================ // Copyright (c) 2019 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 // 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.Windows.Data; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf.Converters; namespace ICSharpCode.ILSpy.Docking { public class TabPageGuardConverter : ValueConverter { protected override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value is TabPageModel ? value : Binding.DoNothing; } protected override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value is TabPageModel ? value : Binding.DoNothing; } } } ================================================ FILE: ILSpy/EntityReference.cs ================================================ // 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. #nullable enable using System.Diagnostics; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy { [DebuggerDisplay("EntityReference Module={Module}, Handle={Handle}, Protocol={Protocol}")] public class EntityReference { readonly MetadataFile? peFile; public string Module { get; } public Handle Handle { get; } public string Protocol { get; } public EntityReference(string moduleFileName, Handle handle) { this.Module = moduleFileName; this.Handle = handle; this.Protocol = "decompile"; } public EntityReference(string? protocol, string moduleFileName, Handle handle) : this(moduleFileName, handle) { this.Protocol = protocol ?? "decompile"; } public EntityReference(MetadataFile module, Handle handle, string protocol = "decompile") { this.peFile = module; this.Module = module.FileName; this.Handle = handle; this.Protocol = protocol; } public MetadataFile? ResolveAssembly(AssemblyList context) { return peFile ?? context.FindAssembly(Module)?.GetMetadataFileOrNull(); } } } ================================================ FILE: ILSpy/ExtensionMethods.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy { /// /// ExtensionMethods used in ILSpy. /// public static class ExtensionMethods { public static string ToSuffixString(this System.Reflection.Metadata.EntityHandle handle, bool showMetadataTokens, bool useBase10) { if (!showMetadataTokens) return string.Empty; int token = System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(handle); if (useBase10) return " @" + token; return " @" + token.ToString("x8"); } /// /// Takes at most first characters from string, and appends '...' if string is longer. /// String can be null. /// public static string TakeStartEllipsis(this string s, int length) { if (string.IsNullOrEmpty(s) || length >= s.Length) return s; return s.Substring(0, length) + "..."; } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this ICollection collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } public static ICompilation? GetTypeSystemWithCurrentOptionsOrNull(this MetadataFile file, SettingsService settingsService, LanguageVersion languageVersion) { var decompilerSettings = settingsService.DecompilerSettings.Clone(); if (!Enum.TryParse(languageVersion?.Version, out Decompiler.CSharp.LanguageVersion csharpLanguageVersion)) csharpLanguageVersion = Decompiler.CSharp.LanguageVersion.Latest; decompilerSettings.SetLanguageVersion(csharpLanguageVersion); return file .GetLoadedAssembly() .GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(decompilerSettings)); } #region DPI independence public static Rect TransformToDevice(this Rect rect, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; return Rect.Transform(rect, matrix); } public static Rect TransformFromDevice(this Rect rect, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; return Rect.Transform(rect, matrix); } public static Size TransformToDevice(this Size size, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; return new Size(size.Width * matrix.M11, size.Height * matrix.M22); } public static Size TransformFromDevice(this Size size, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; return new Size(size.Width * matrix.M11, size.Height * matrix.M22); } public static Point TransformToDevice(this Point point, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; return matrix.Transform(point); } public static Point TransformFromDevice(this Point point, Visual visual) { Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; return matrix.Transform(point); } #endregion public static T? FindVisualChild(this DependencyObject? depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child is T dependencyObject) { return dependencyObject; } T? childItem = FindVisualChild(child); if (childItem != null) return childItem; } } return null; } public static T? GetParent(this DependencyObject? depObj) where T : DependencyObject { if (depObj == null) return null; while (!(depObj is T)) { var parent = VisualTreeHelper.GetParent(depObj); if (parent == null) return null; depObj = parent; } return (T)depObj; } public static void SelectItem(this DataGrid view, object item) { var container = (DataGridRow)view.ItemContainerGenerator.ContainerFromItem(item); if (container != null) container.IsSelected = true; view.Focus(); } public static double ToGray(this Color? color) { return color?.R * 0.3 + color?.G * 0.6 + color?.B * 0.1 ?? 0.0; } internal static string? FormatExceptions(this IList exceptions) { if (exceptions.Count == 0) return null; string delimiter = $"-------------------------------------------------{Environment.NewLine}"; return string.Join(delimiter, exceptions.Select(FormatException)); } private static string FormatException(App.ExceptionData item) { var output = new StringBuilder(); if (!item.PluginName.IsNullOrEmpty()) output.AppendLine("Error(s) loading plugin: " + item.PluginName); if (item.Exception is System.Reflection.ReflectionTypeLoadException exception) { foreach (var ex in exception.LoaderExceptions.ExceptNullItems()) { output.AppendLine(ex.ToString()); output.AppendLine(); } } else { output.AppendLine(item.Exception.ToString()); } return output.ToString(); } public static IDisposable PreserveFocus(this IInputElement? inputElement, bool preserve = true) { return new RestoreFocusHelper(inputElement, preserve); } private sealed class RestoreFocusHelper(IInputElement? inputElement, bool preserve) : IDisposable { public void Dispose() { if (preserve) { inputElement?.Focus(); } } } } } ================================================ FILE: ILSpy/GlobalUsings.cs ================================================ // Copyright (c) 2024 Tom Englert 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 // 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. global using ICSharpCode.ILSpy.Util; ================================================ FILE: ILSpy/GuessFileType.cs ================================================ // 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 // 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.IO; using System.Text; using System.Xml; namespace ICSharpCode.ILSpy { /// /// Static methods for determining the type of a file. /// static class GuessFileType { public static FileType DetectFileType(Stream stream) { StreamReader reader; if (stream.Length >= 2) { int firstByte = stream.ReadByte(); int secondByte = stream.ReadByte(); switch ((firstByte << 8) | secondByte) { case 0xfffe: // UTF-16 LE BOM / UTF-32 LE BOM case 0xfeff: // UTF-16 BE BOM stream.Position -= 2; reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true); break; case 0xefbb: // start of UTF-8 BOM if (stream.ReadByte() == 0xbf) { reader = new StreamReader(stream, Encoding.UTF8); break; } else { return FileType.Binary; } default: if (IsUTF8(stream, (byte)firstByte, (byte)secondByte)) { stream.Position = 0; reader = new StreamReader(stream, Encoding.UTF8); break; } else { return FileType.Binary; } } } else { return FileType.Binary; } // Now we got a StreamReader with the correct encoding // Check for XML now try { XmlTextReader xmlReader = new XmlTextReader(reader); xmlReader.XmlResolver = null; xmlReader.MoveToContent(); return FileType.Xml; } catch (XmlException) { return FileType.Text; } } static bool IsUTF8(Stream fs, byte firstByte, byte secondByte) { int max = (int)Math.Min(fs.Length, 500000); // look at max. 500 KB const int ASCII = 0; const int Error = 1; const int UTF8 = 2; const int UTF8Sequence = 3; int state = ASCII; int sequenceLength = 0; byte b; for (int i = 0; i < max; i++) { if (i == 0) { b = firstByte; } else if (i == 1) { b = secondByte; } else { b = (byte)fs.ReadByte(); } if (b < 0x80) { // normal ASCII character if (state == UTF8Sequence) { state = Error; break; } } else if (b < 0xc0) { // 10xxxxxx : continues UTF8 byte sequence if (state == UTF8Sequence) { --sequenceLength; if (sequenceLength < 0) { state = Error; break; } else if (sequenceLength == 0) { state = UTF8; } } else { state = Error; break; } } else if (b >= 0xc2 && b < 0xf5) { // beginning of byte sequence if (state == UTF8 || state == ASCII) { state = UTF8Sequence; if (b < 0xe0) { sequenceLength = 1; // one more byte following } else if (b < 0xf0) { sequenceLength = 2; // two more bytes following } else { sequenceLength = 3; // three more bytes following } } else { state = Error; break; } } else { // 0xc0, 0xc1, 0xf5 to 0xff are invalid in UTF-8 (see RFC 3629) state = Error; break; } } return state != Error; } } enum FileType { Binary, Text, Xml } } ================================================ FILE: ILSpy/ILSpy.csproj ================================================  WinExe net10.0-windows major win-x64;win-arm64 False false false true ICSharpCode.ILSpy True Images\ILSpy-Large.ico app.manifest True ..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk true 1 true full true True pdbonly true license.txt True True Resources.resx PublicResXFileCodeGenerator Resources.Designer.cs Microsoft.VCToolsVersion.default.props $(MSBuildToolsPath)\..\..\..\VC\ $(MSBuildToolsPath)\..\..\..\..\VC\ $(VCBasePath)Auxiliary\Build\$(VCToolsVersionPropsFileNameDefault) True $(NoWarn);1701;1702;CA1001;CA2213 $(NoWarn);1701;1702;CA1001;CA2213 powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/sort-resx.ps1 ================================================ FILE: ILSpy/ILSpySettingsFilePathProvider.cs ================================================ // Copyright (c) 2022 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 // 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.IO; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy { internal class ILSpySettingsFilePathProvider : ISettingsFilePathProvider { public string GetSettingsFilePath() { if (App.CommandLineArguments.ConfigFile != null) return App.CommandLineArguments.ConfigFile; var assemblyLocation = typeof(MainWindow).Assembly.Location; if (!String.IsNullOrWhiteSpace(assemblyLocation)) { string localPath = Path.Combine(Path.GetDirectoryName(assemblyLocation), "ILSpy.xml"); if (File.Exists(localPath)) return localPath; } return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ICSharpCode", "ILSpy.xml"); } } } ================================================ FILE: ILSpy/ILSpyTraceListener.cs ================================================ // Copyright (c) 2014 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 // 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.Threading; using ICSharpCode.ILSpy.Controls; namespace ICSharpCode.ILSpy { class ILSpyTraceListener : DefaultTraceListener { [Conditional("DEBUG")] public static void Install() { Trace.Listeners.Clear(); Trace.Listeners.Add(new ILSpyTraceListener()); } public ILSpyTraceListener() { base.AssertUiEnabled = false; } HashSet ignoredStacks = new HashSet(); bool dialogIsOpen; public override void Fail(string message) { this.Fail(message, null); } public override void Fail(string message, string detailMessage) { base.Fail(message, detailMessage); // let base class write the assert to the debug console string topFrame = ""; string stackTrace = ""; try { stackTrace = new StackTrace(true).ToString(); var frames = stackTrace.Split('\r', '\n') .Where(f => f.Length > 0) .SkipWhile(f => f.Contains("ILSpyTraceListener") || f.Contains("System.Diagnostics")) .ToList(); topFrame = frames[0]; stackTrace = string.Join(Environment.NewLine, frames); } catch { } lock (ignoredStacks) { if (ignoredStacks.Contains(topFrame)) return; if (dialogIsOpen) return; dialogIsOpen = true; } // We might be unable to display a dialog here, e.g. because // we're on the UI thread but dispatcher processing is disabled. // In any case, we don't want to pump messages while the dialog is displaying, // so we create a separate UI thread for the dialog: int result = 0; var thread = new Thread(() => result = ShowAssertionDialog(message, detailMessage, stackTrace)); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); if (result == 0) { // throw throw new AssertionFailedException(message); } else if (result == 1) { // debug Debugger.Break(); } else if (result == 2) { // ignore } else if (result == 3) { lock (ignoredStacks) { ignoredStacks.Add(topFrame); } } } int ShowAssertionDialog(string message, string detailMessage, string stackTrace) { message = message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace; string[] buttonTexts = { "Throw", "Debug", "Ignore", "Ignore All" }; CustomDialog inputBox = new CustomDialog("Assertion Failed", message.TakeStartEllipsis(750), -1, 2, buttonTexts); inputBox.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; inputBox.ShowInTaskbar = true; // make this window more visible, because it effectively interrupts the decompilation process. try { inputBox.ShowDialog(); return inputBox.Result; } finally { dialogIsOpen = false; inputBox.Dispose(); } } } class AssertionFailedException : Exception { public AssertionFailedException(string message) : base(message) { } } } ================================================ FILE: ILSpy/ISmartTextOutput.cs ================================================ // 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Themes; namespace ICSharpCode.ILSpy { /// /// Adds additional WPF-specific output features to . /// public interface ISmartTextOutput : ITextOutput { /// /// Inserts an interactive UI element at the current position in the text output. /// void AddUIElement(Func element); void BeginSpan(HighlightingColor highlightingColor); void EndSpan(); /// /// Gets/sets the title displayed in the document tab's header. /// string Title { get; set; } } public static class SmartTextOutputExtensions { /// /// Creates a button. /// public static void AddButton(this ISmartTextOutput output, ImageSource icon, string text, RoutedEventHandler click) { output.AddUIElement( delegate { Button button = ThemeManager.Current.CreateButton(); button.Cursor = Cursors.Arrow; button.Margin = new Thickness(2); button.Padding = new Thickness(9, 1, 9, 1); button.MinWidth = 73; if (icon != null) { button.Content = new StackPanel { Orientation = Orientation.Horizontal, Children = { new Image { Width = 16, Height = 16, Source = icon, Margin = new Thickness(0, 0, 4, 0) }, new TextBlock { Text = text } } }; } else { button.Content = text; } button.Click += click; return button; }); } } } ================================================ FILE: ILSpy/Images/AccessOverlayIcon.cs ================================================ // 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 // 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. namespace ICSharpCode.ILSpy { internal enum AccessOverlayIcon { Public, Protected, Internal, ProtectedInternal, Private, PrivateProtected, CompilerControlled } } ================================================ FILE: ILSpy/Images/Assembly.xaml ================================================ ================================================ FILE: ILSpy/Images/AssemblyList.xaml ================================================ ================================================ FILE: ILSpy/Images/AssemblyListGAC.xaml ================================================ ================================================ FILE: ILSpy/Images/AssemblyLoading.xaml ================================================ ================================================ FILE: ILSpy/Images/AssemblyWarning.xaml ================================================ ================================================ FILE: ILSpy/Images/Back.xaml ================================================ ================================================ FILE: ILSpy/Images/Class.xaml ================================================ ================================================ FILE: ILSpy/Images/Close.xaml ================================================ ================================================ FILE: ILSpy/Images/CollapseAll.xaml ================================================ ================================================ FILE: ILSpy/Images/Constructor.xaml ================================================ ================================================ FILE: ILSpy/Images/Copy.xaml ================================================ ================================================ FILE: ILSpy/Images/Delegate.xaml ================================================ ================================================ FILE: ILSpy/Images/Delete.xaml ================================================ ================================================ FILE: ILSpy/Images/DictionaryContain.xaml ================================================ ================================================ FILE: ILSpy/Images/Enum.xaml ================================================ ================================================ FILE: ILSpy/Images/EnumValue.xaml ================================================ ================================================ FILE: ILSpy/Images/Event.xaml ================================================ ================================================ FILE: ILSpy/Images/ExpandAll.xaml ================================================ ================================================ FILE: ILSpy/Images/ExportOverlay.xaml ================================================ ================================================ FILE: ILSpy/Images/ExtensionMethod.xaml ================================================ ================================================ FILE: ILSpy/Images/Field.xaml ================================================ ================================================ FILE: ILSpy/Images/FieldReadOnly.xaml ================================================ ================================================ FILE: ILSpy/Images/FindAssembly.xaml ================================================ ================================================ FILE: ILSpy/Images/Folder.Closed.xaml ================================================ ================================================ FILE: ILSpy/Images/Folder.Open.xaml ================================================ ================================================ FILE: ILSpy/Images/Forward.xaml ================================================ ================================================ FILE: ILSpy/Images/Header.xaml ================================================ ================================================ FILE: ILSpy/Images/Heap.xaml ================================================ ================================================ FILE: ILSpy/Images/Images.cs ================================================ // 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 // 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.IO; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace ICSharpCode.ILSpy { using ICSharpCode.Decompiler.TypeSystem; static class Images { private static readonly Rect iconRect = new Rect(0, 0, 16, 16); static ImageSource Load(string icon) { var image = new DrawingImage(LoadDrawingGroup(null, "Images/" + icon)); if (image.CanFreeze) { image.Freeze(); } return image; } public static readonly ImageSource ILSpyIcon = new BitmapImage(new Uri("pack://application:,,,/ILSpy;component/images/ILSpy.ico")); public static readonly ImageSource ViewCode = Load("ViewCode"); public static readonly ImageSource Save = Load("Save"); public static readonly ImageSource OK = Load("OK"); public static readonly ImageSource Delete = Load("Delete"); public static readonly ImageSource Search = Load("Search"); public static readonly ImageSource Assembly = Load("Assembly"); public static readonly ImageSource AssemblyWarning = Load("AssemblyWarning"); public static readonly ImageSource AssemblyLoading = Load(nameof(AssemblyLoading)); public static readonly ImageSource FindAssembly = Load("FindAssembly"); public static readonly ImageSource Library = Load("Library"); public static readonly ImageSource Namespace = Load("Namespace"); public static readonly ImageSource ReferenceFolder = Load("ReferenceFolder"); public static readonly ImageSource NuGet = Load(null, "Images/NuGet.png"); public static readonly ImageSource MetadataFile = Load("MetadataFile"); public static readonly ImageSource WebAssemblyFile = Load("WebAssembly"); public static readonly ImageSource ProgramDebugDatabase = Load("ProgramDebugDatabase"); public static readonly ImageSource Metadata = Load("Metadata"); public static readonly ImageSource Heap = Load("Heap"); public static readonly ImageSource Header = Load("Header"); public static readonly ImageSource MetadataTable = Load("MetadataTable"); public static readonly ImageSource MetadataTableGroup = Load("MetadataTableGroup"); public static readonly ImageSource ListFolder = Load("ListFolder"); public static readonly ImageSource ListFolderOpen = Load("ListFolder.Open"); public static readonly ImageSource SubTypes = Load("SubTypes"); public static readonly ImageSource SuperTypes = Load("SuperTypes"); public static readonly ImageSource FolderOpen = Load("Folder.Open"); public static readonly ImageSource FolderClosed = Load("Folder.Closed"); public static readonly ImageSource Resource = Load("Resource"); public static readonly ImageSource ResourceImage = Load("ResourceImage"); public static readonly ImageSource ResourceResourcesFile = Load("ResourceResourcesFile"); public static readonly ImageSource ResourceXml = Load("ResourceXml"); public static readonly ImageSource ResourceXsd = Load("ResourceXslt"); public static readonly ImageSource ResourceXslt = Load("ResourceXslt"); public static readonly ImageSource Class = Load("Class"); public static readonly ImageSource Struct = Load("Struct"); public static readonly ImageSource Interface = Load("Interface"); public static readonly ImageSource Delegate = Load("Delegate"); public static readonly ImageSource Enum = Load("Enum"); public static readonly ImageSource Type = Load("ShowPublicOnly"); public static readonly ImageSource Field = Load("Field"); public static readonly ImageSource FieldReadOnly = Load("FieldReadOnly"); public static readonly ImageSource Literal = Load("Literal"); public static readonly ImageSource EnumValue = Load("EnumValue"); public static readonly ImageSource Method = Load("Method"); public static readonly ImageSource Constructor = Load("Constructor"); public static readonly ImageSource VirtualMethod = Load("VirtualMethod"); public static readonly ImageSource Operator = Load("Operator"); public static readonly ImageSource ExtensionMethod = Load("ExtensionMethod"); public static readonly ImageSource PInvokeMethod = Load("PInvokeMethod"); public static readonly ImageSource Property = Load("Property"); public static readonly ImageSource Indexer = Load("Indexer"); public static readonly ImageSource Event = Load("Event"); private static readonly ImageSource OverlayProtected = Load("OverlayProtected"); private static readonly ImageSource OverlayInternal = Load("OverlayInternal"); private static readonly ImageSource OverlayProtectedInternal = Load("OverlayProtectedInternal"); private static readonly ImageSource OverlayPrivate = Load("OverlayPrivate"); private static readonly ImageSource OverlayPrivateProtected = Load("OverlayPrivateProtected"); private static readonly ImageSource OverlayCompilerControlled = Load("OverlayCompilerControlled"); private static readonly ImageSource OverlayReference = Load("ReferenceOverlay"); private static readonly ImageSource OverlayStatic = Load("OverlayStatic"); public static readonly ImageSource TypeReference = GetIcon("ShowPublicOnly", "ReferenceOverlay"); public static readonly ImageSource MethodReference = GetIcon("Method", "ReferenceOverlay"); public static readonly ImageSource FieldReference = GetIcon("Field", "ReferenceOverlay"); public static readonly ImageSource ExportedType = GetIcon("ShowPublicOnly", "ExportOverlay"); public static ImageSource Load(object part, string icon) { if (icon.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return LoadImage(part, icon); Uri uri = GetUri(part, icon + ".xaml"); if (ResourceExists(uri)) { var image = new DrawingImage(LoadDrawingGroup(part, icon)); if (image.CanFreeze) { image.Freeze(); } return image; } return LoadImage(part, icon + ".png"); } static BitmapImage LoadImage(object part, string icon) { Uri uri = GetUri(part, icon); BitmapImage image = new BitmapImage(uri); if (image.CanFreeze) { image.Freeze(); } return image; } public static Drawing LoadDrawingGroup(object part, string icon) { return (Drawing)Application.LoadComponent(GetUri(part, icon + ".xaml", absolute: false)); } private static Uri GetUri(object part, string icon, bool absolute = true) { Uri uri; var assembly = part?.GetType().Assembly; string prefix; UriKind kind; if (absolute) { prefix = "pack://application:,,,/"; kind = UriKind.Absolute; } else { prefix = "/"; kind = UriKind.Relative; } if (part == null || assembly == typeof(Images).Assembly) { uri = new Uri(prefix + icon, kind); } else { var name = assembly.GetName(); uri = new Uri(prefix + name.Name + ";v" + name.Version + ";component/" + icon, kind); } return uri; } private static bool ResourceExists(Uri uri) { try { Application.GetResourceStream(uri); return true; } catch (IOException) { return false; } } private static readonly TypeIconCache typeIconCache = new TypeIconCache(); private static readonly MemberIconCache memberIconCache = new MemberIconCache(); public static ImageSource GetIcon(TypeIcon icon, AccessOverlayIcon overlay, bool isStatic = false) { lock (typeIconCache) return typeIconCache.GetIcon(icon, overlay, isStatic); } public static ImageSource GetIcon(MemberIcon icon, AccessOverlayIcon overlay, bool isStatic) { lock (memberIconCache) return memberIconCache.GetIcon(icon, overlay, isStatic); } public static AccessOverlayIcon GetOverlayIcon(Accessibility accessibility) { switch (accessibility) { case Accessibility.Public: return AccessOverlayIcon.Public; case Accessibility.Internal: return AccessOverlayIcon.Internal; case Accessibility.ProtectedAndInternal: return AccessOverlayIcon.PrivateProtected; case Accessibility.Protected: return AccessOverlayIcon.Protected; case Accessibility.ProtectedOrInternal: return AccessOverlayIcon.ProtectedInternal; case Accessibility.Private: return AccessOverlayIcon.Private; default: return AccessOverlayIcon.CompilerControlled; } } private static ImageSource GetIcon(string baseImage, string overlay = null, bool isStatic = false) { ImageSource baseImageSource = Load(baseImage); ImageSource overlayImageSource = overlay != null ? Load(overlay) : null; return CreateOverlayImage(baseImageSource, overlayImageSource, isStatic); } private static ImageSource CreateOverlayImage(ImageSource baseImage, ImageSource overlay, bool isStatic) { var group = new DrawingGroup(); Drawing baseDrawing = new ImageDrawing(baseImage, iconRect); if (overlay != null) { var nestedGroup = new DrawingGroup { Transform = new ScaleTransform(0.8, 0.8) }; nestedGroup.Children.Add(baseDrawing); group.Children.Add(nestedGroup); group.Children.Add(new ImageDrawing(overlay, iconRect)); } else { group.Children.Add(baseDrawing); } if (isStatic) { group.Children.Add(new ImageDrawing(Images.OverlayStatic, iconRect)); } var image = new DrawingImage(group); if (image.CanFreeze) { image.Freeze(); } return image; } #region icon caches & overlay management private class TypeIconCache : IconCache { public TypeIconCache() { PreloadPublicIconToCache(TypeIcon.Class, Images.Class); PreloadPublicIconToCache(TypeIcon.Enum, Images.Enum); PreloadPublicIconToCache(TypeIcon.Struct, Images.Struct); PreloadPublicIconToCache(TypeIcon.Interface, Images.Interface); PreloadPublicIconToCache(TypeIcon.Delegate, Images.Delegate); } protected override ImageSource GetBaseImage(TypeIcon icon) { ImageSource baseImage; switch (icon) { case TypeIcon.Class: baseImage = Images.Class; break; case TypeIcon.Enum: baseImage = Images.Enum; break; case TypeIcon.Struct: baseImage = Images.Struct; break; case TypeIcon.Interface: baseImage = Images.Interface; break; case TypeIcon.Delegate: baseImage = Images.Delegate; break; default: throw new ArgumentOutOfRangeException(nameof(icon), $"TypeIcon.{icon} is not supported!"); } return baseImage; } } private class MemberIconCache : IconCache { public MemberIconCache() { PreloadPublicIconToCache(MemberIcon.Field, Images.Field); PreloadPublicIconToCache(MemberIcon.FieldReadOnly, Images.FieldReadOnly); PreloadPublicIconToCache(MemberIcon.Literal, Images.Literal); PreloadPublicIconToCache(MemberIcon.EnumValue, Images.EnumValue); PreloadPublicIconToCache(MemberIcon.Property, Images.Property); PreloadPublicIconToCache(MemberIcon.Indexer, Images.Indexer); PreloadPublicIconToCache(MemberIcon.Method, Images.Method); PreloadPublicIconToCache(MemberIcon.Constructor, Images.Constructor); PreloadPublicIconToCache(MemberIcon.VirtualMethod, Images.VirtualMethod); PreloadPublicIconToCache(MemberIcon.Operator, Images.Operator); PreloadPublicIconToCache(MemberIcon.ExtensionMethod, Images.ExtensionMethod); PreloadPublicIconToCache(MemberIcon.PInvokeMethod, Images.PInvokeMethod); PreloadPublicIconToCache(MemberIcon.Event, Images.Event); } protected override ImageSource GetBaseImage(MemberIcon icon) { ImageSource baseImage; switch (icon) { case MemberIcon.Field: baseImage = Images.Field; break; case MemberIcon.FieldReadOnly: baseImage = Images.FieldReadOnly; break; case MemberIcon.Literal: baseImage = Images.Literal; break; case MemberIcon.EnumValue: baseImage = Images.EnumValue; break; case MemberIcon.Property: baseImage = Images.Property; break; case MemberIcon.Indexer: baseImage = Images.Indexer; break; case MemberIcon.Method: baseImage = Images.Method; break; case MemberIcon.Constructor: baseImage = Images.Constructor; break; case MemberIcon.VirtualMethod: baseImage = Images.VirtualMethod; break; case MemberIcon.Operator: baseImage = Images.Operator; break; case MemberIcon.ExtensionMethod: baseImage = Images.ExtensionMethod; break; case MemberIcon.PInvokeMethod: baseImage = Images.PInvokeMethod; break; case MemberIcon.Event: baseImage = Images.Event; break; default: throw new ArgumentOutOfRangeException(nameof(icon), $"MemberIcon.{icon} is not supported!"); } return baseImage; } } private abstract class IconCache { private readonly Dictionary<(T, AccessOverlayIcon, bool), ImageSource> cache = new Dictionary<(T, AccessOverlayIcon, bool), ImageSource>(); protected void PreloadPublicIconToCache(T icon, ImageSource image) { var iconKey = (icon, AccessOverlayIcon.Public, false); cache.Add(iconKey, image); } public ImageSource GetIcon(T icon, AccessOverlayIcon overlay, bool isStatic) { var iconKey = (icon, overlay, isStatic); if (cache.ContainsKey(iconKey)) { return cache[iconKey]; } else { ImageSource result = BuildMemberIcon(icon, overlay, isStatic); cache.Add(iconKey, result); return result; } } private ImageSource BuildMemberIcon(T icon, AccessOverlayIcon overlay, bool isStatic) { ImageSource baseImage = GetBaseImage(icon); ImageSource overlayImage = GetOverlayImage(overlay); return CreateOverlayImage(baseImage, overlayImage, isStatic); } protected abstract ImageSource GetBaseImage(T icon); private static ImageSource GetOverlayImage(AccessOverlayIcon overlay) { ImageSource overlayImage; switch (overlay) { case AccessOverlayIcon.Public: overlayImage = null; break; case AccessOverlayIcon.Protected: overlayImage = Images.OverlayProtected; break; case AccessOverlayIcon.Internal: overlayImage = Images.OverlayInternal; break; case AccessOverlayIcon.ProtectedInternal: overlayImage = Images.OverlayProtectedInternal; break; case AccessOverlayIcon.Private: overlayImage = Images.OverlayPrivate; break; case AccessOverlayIcon.PrivateProtected: overlayImage = Images.OverlayPrivateProtected; break; case AccessOverlayIcon.CompilerControlled: overlayImage = Images.OverlayCompilerControlled; break; default: throw new ArgumentOutOfRangeException(nameof(overlay), $"AccessOverlayIcon.{overlay} is not supported!"); } return overlayImage; } } #endregion } } ================================================ FILE: ILSpy/Images/Indexer.xaml ================================================ ================================================ FILE: ILSpy/Images/Interface.xaml ================================================ ================================================ FILE: ILSpy/Images/Library.xaml ================================================ ================================================ FILE: ILSpy/Images/ListFolder.Open.xaml ================================================ ================================================ FILE: ILSpy/Images/ListFolder.xaml ================================================ ================================================ FILE: ILSpy/Images/Literal.xaml ================================================ ================================================ FILE: ILSpy/Images/MemberIcon.cs ================================================ // 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 // 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. namespace ICSharpCode.ILSpy { internal enum MemberIcon { Literal, FieldReadOnly, Field, EnumValue, Property, Indexer, Method, Constructor, VirtualMethod, Operator, ExtensionMethod, PInvokeMethod, Event } } ================================================ FILE: ILSpy/Images/Metadata.xaml ================================================ ================================================ FILE: ILSpy/Images/MetadataFile.xaml ================================================ ================================================ FILE: ILSpy/Images/MetadataTable.xaml ================================================ ================================================ FILE: ILSpy/Images/MetadataTableGroup.xaml ================================================ ================================================ FILE: ILSpy/Images/Method.xaml ================================================ ================================================ FILE: ILSpy/Images/Namespace.xaml ================================================ ================================================ FILE: ILSpy/Images/OK.xaml ================================================ ================================================ FILE: ILSpy/Images/Open.xaml ================================================ ================================================ FILE: ILSpy/Images/Operator.xaml ================================================ ================================================ FILE: ILSpy/Images/OverlayCompilerControlled.xaml ================================================ ================================================ FILE: ILSpy/Images/PInvokeMethod.xaml ================================================ ================================================ FILE: ILSpy/Images/ProgramDebugDatabase.xaml ================================================ ================================================ FILE: ILSpy/Images/Property.xaml ================================================ ================================================ FILE: ILSpy/Images/README.md ================================================ Icons used in ILSpy: -------------------- | | SVG | XAML | Origin | Notes | |---------------------------|-----|------|---------------------------------------------------------------------------------|---------| | Assembly | x | x | VS 2017 Icon Pack (Reference) | | | AssemblyList | x | x | VS 2017 Icon Pack (AddReference) | | | AssemblyListGAC | x | x | based on VS 2017 Icon Pack (AddReference) + "GAC" text | | | AssemblyWarning | x | x | VS 2017 Icon Pack (ReferenceWarning) | | | Back | x | x | VS 2017 Icon Pack (Backward) | | | Class | x | x | VS 2017 Icon Pack (Class) | | | Close | x | x | VS 2017 Icon Pack (Clear) | | | CollapseAll | x | x | VS 2017 Icon Pack (CollapseAll) | | | Constructor | x | x | based on VS 2017 Icon Pack (Method) using a different colour | | | Copy | x | x | VS 2017 Icon Pack (Copy) | | | Delegate | x | x | VS 2017 Icon Pack (Delegate) | | | Delete | x | x | VS 2017 Icon Pack (Remove_color) | | | DictionaryContain | x | x | VS 2017 Icon Pack (DictionaryContain) | | | Enum | x | x | VS 2017 Icon Pack (Enumerator) | | | EnumValue | x | x | VS 2017 Icon Pack (EnumItem) | | | Event | x | x | VS 2017 Icon Pack (Event) | | | ExpandAll | x | x | VS 2017 Icon Pack (ExpandAll) | | | ExportOverlay | x | x | slightly modified VS 2017 Icon Pack (Export) | | | ExtensionMethod | x | x | VS 2017 Icon Pack (ExtensionMethod) | | | Field | x | x | VS 2017 Icon Pack (Field) | | | FieldReadOnly | x | x | VS 2017 Icon Pack (Field) with different color | | | FindAssembly | x | x | based on VS 2017 Icon Pack (Reference + Search) with transparency modifications | | | Folder.Closed | x | x | VS 2017 Icon Pack (Folder) | | | Folder.Open | x | x | VS 2017 Icon Pack (FolderOpen) | | | Forward | x | x | VS 2017 Icon Pack (Forward) | | | Header | x | x | VS 2017 Icon Pack (PageHeader) | | | Heap | x | x | VS 2017 Icon Pack (Datalist) | | | Indexer | x | x | VS 2017 Icon Pack (Indexer) | | | Interface | x | x | VS 2017 Icon Pack (Interface) | | | Library | x | x | VS 2017 Icon Pack (Library) | | | ListFolder | x | x | VS 2017 Icon Pack (ListFolder) | | | ListFolder.Open | x | x | VS 2017 Icon Pack (ListFolderOpen) | | | Literal | x | x | VS 2017 Icon Pack (Literal) | | | Metadata | x | x | VS 2017 Icon Pack (Metadata) | | | MetadataFile | x | x | combined ProgramDebugDatabase + Metadata | | | MetadataTable | x | x | VS 2017 Icon Pack (LinkedTable) | | | MetadataTableGroup | x | x | VS 2017 Icon Pack (LinkedTableGroup) | | | Method | x | x | VS 2017 Icon Pack (Method) | | | Namespace | x | x | VS 2017 Icon Pack (Namespace) | | | OK | x | x | VS 2017 Icon Pack (StatusOK) | | | Open | x | x | VS 2017 Icon Pack (Open) | | | Operator | x | x | VS 2017 Icon Pack (Operator) | | | OverlayCompilerControlled | x | x | based on VS 2017 Icon Pack (StatusBlocked) | | | OverlayInternal | x | x | based on VS 2017 Icon Pack (Friend) | | | OverlayPrivate | x | x | extracted from VS 2017 Icon Pack (ActionPrivate) | | | OverlayPrivateProtected | x | x | combined OverlayPrivate and OverlayProtected | | | OverlayProtected | x | x | extracted from VS 2017 Icon Pack (ActionProtected) | | | OverlayProtectedInternal | x | x | combined OverlayProtected and OverlayInternal | | | OverlayStatic | x | x | custom | | | PInvokeMethod | x | x | based on VS 2017 Icon Pack (ExtensionMethod) with rotated arrow | | | ProgramDebugDatabase | x | x | VS 2017 Icon Pack (ProgramDebugDatabase) | | | Property | x | x | VS 2017 Icon Pack (Property) | | | ReferenceFolder | x | x | combined VS 2017 Icon Pack (Reference) two times | | | ReferenceOverlay | x | x | extracted arrow from VS 2017 Icon Pack (TypeShortcut) | | | Refresh | x | x | VS 2017 Icon Pack (Refresh) | | | Resource | x | x | VS 2017 Icon Pack (Document) | | | ResourceImage | x | x | VS 2017 Icon Pack (Image) | | | ResourceResourcesFile | x | x | VS 2017 Icon Pack (LocalResources) | | | ResourceXml | x | x | VS 2017 Icon Pack (XMLFile) | | | ResourceXsd | x | x | combined VS 2017 Icon Pack (XMLSchema) with the file symbol in ResourceXslt | | | ResourceXsl | x | x | VS 2017 Icon Pack (XMLTransformation) | | | ResultToJSON | x | x | VS 2017 Icon Pack (ResultToJSON) | | | ResourceXslt | x | x | VS 2017 Icon Pack (XSLTTemplate) | | | Save | x | x | VS 2017 Icon Pack (Save) | | | Search | x | x | VS 2017 Icon Pack (Search) | | | SearchMsdn | x | x | based on VS 2017 Icon Pack (Search) + Microsoft Logo | | | ShowAll | x | x | combined PublicOnly, OverlayPrivate, OverlayProtected, OverlayInternal | | | ShowPrivateInternal | x | x | combined OverlayPrivate and OverlayInternal | | | ShowPublicOnly | x | x | VS 2017 Icon Pack (Type) | | | Sort | x | x | VS 2017 Icon Pack (SortAscending) | | | Struct | x | x | VS 2017 Icon Pack (Structure) | | | SubTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated +90° | | | SuperTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated -90° | | | SwitchSourceOrTarget | x | x | VS 2017 Icon Pack (SwitchSourceOrTarget) | | | ViewCode | x | x | VS 2017 Icon Pack (GoToSourceCode) | | | VirtualMethod | x | x | combined VS 2017 Icon Pack (Method) two times | | | Warning | x | x | VS 2017 Icon Pack (StatusWarning) | | Note: All XAML icons from VS 2017 Icon Pack are modified to not include a `Viewbox` XAML root element. We always use a `Drawing`-derived root element. Note: When changing an icon, start with SVG and use https://github.com/BerndK/SvgToXaml to generate the XAML. The result is much better XAML than what Inkscape produces. ================================================ FILE: ILSpy/Images/ReferenceOverlay.xaml ================================================ ================================================ FILE: ILSpy/Images/Refresh.xaml ================================================ ================================================ FILE: ILSpy/Images/Resource.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceImage.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceResourcesFile.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceXml.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceXsd.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceXsl.xaml ================================================ ================================================ FILE: ILSpy/Images/ResourceXslt.xaml ================================================ ================================================ FILE: ILSpy/Images/ResultToJSON.xaml ================================================ ================================================ FILE: ILSpy/Images/Save.xaml ================================================ ================================================ FILE: ILSpy/Images/Search.xaml ================================================ ================================================ FILE: ILSpy/Images/SearchMsdn.xaml ================================================ ================================================ FILE: ILSpy/Images/ShowPublicOnly.xaml ================================================ ================================================ FILE: ILSpy/Images/Sort.xaml ================================================ ================================================ FILE: ILSpy/Images/Struct.xaml ================================================ ================================================ FILE: ILSpy/Images/SubTypes.xaml ================================================ ================================================ FILE: ILSpy/Images/SuperTypes.xaml ================================================ ================================================ FILE: ILSpy/Images/SwitchSourceOrTarget.xaml ================================================ ================================================ FILE: ILSpy/Images/TypeIcon.cs ================================================ // 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 // 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. namespace ICSharpCode.ILSpy { internal enum TypeIcon { Class, Enum, Struct, Interface, Delegate } } ================================================ FILE: ILSpy/Images/ViewCode.xaml ================================================ ================================================ FILE: ILSpy/Images/VirtualMethod.xaml ================================================ ================================================ FILE: ILSpy/Images/Warning.xaml ================================================ ================================================ FILE: ILSpy/Images/WebAssembly.xaml ================================================ ================================================ FILE: ILSpy/Images/WpfWindowsTreeNodeImagesProvider.cs ================================================ using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy { public class WpfWindowsTreeNodeImagesProvider : ITreeNodeImagesProvider { public object Assembly => Images.Assembly; } } ================================================ FILE: ILSpy/Images/ZoomIn.xaml ================================================ ================================================ FILE: ILSpy/Images/ZoomOut.xaml ================================================ ================================================ FILE: ILSpy/LanguageSettings.cs ================================================ // 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 // 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.Collections.Generic; using System.Xml.Linq; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Settings; using TomsToolbox.Wpf; #nullable enable namespace ICSharpCode.ILSpy { /// /// Represents the filters applied to the tree view. /// public class LanguageSettings : ObservableObjectBase, IChildSettings { /// /// This dictionary is necessary to remember language versions across language changes. For example, /// the user first select C# 10, then switches to IL, then switches back to C#. After that we must be /// able to restore the original selection (i.e., C# 10). /// private readonly Dictionary languageVersionHistory = new Dictionary(); public LanguageSettings(XElement element, ISettingsSection parent) { Parent = parent; this.ShowApiLevel = (ApiVisibility?)(int?)element.Element("ShowAPILevel") ?? ApiVisibility.PublicAndInternal; this.LanguageId = (string?)element.Element("Language"); this.LanguageVersionId = (string?)element.Element("LanguageVersion"); } public ISettingsSection Parent { get; } public XElement SaveAsXml() { return new XElement( "FilterSettings", new XElement("ShowAPILevel", (int)this.ShowApiLevel), new XElement("Language", this.LanguageId), new XElement("LanguageVersion", this.LanguageVersionId) ); } ApiVisibility showApiLevel; /// /// Gets/Sets whether public, internal or all API members should be shown. /// public ApiVisibility ShowApiLevel { get { return showApiLevel; } set { if (showApiLevel != value) { showApiLevel = value; OnPropertyChanged(nameof(ShowApiLevel)); } } } public bool ApiVisPublicOnly { get { return showApiLevel == ApiVisibility.PublicOnly; } set { if (value == (showApiLevel == ApiVisibility.PublicOnly)) return; ShowApiLevel = ApiVisibility.PublicOnly; OnPropertyChanged(nameof(ApiVisPublicOnly)); OnPropertyChanged(nameof(ApiVisPublicAndInternal)); OnPropertyChanged(nameof(ApiVisAll)); } } public bool ApiVisPublicAndInternal { get { return showApiLevel == ApiVisibility.PublicAndInternal; } set { if (value == (showApiLevel == ApiVisibility.PublicAndInternal)) return; ShowApiLevel = ApiVisibility.PublicAndInternal; OnPropertyChanged(nameof(ApiVisPublicOnly)); OnPropertyChanged(nameof(ApiVisPublicAndInternal)); OnPropertyChanged(nameof(ApiVisAll)); } } public bool ApiVisAll { get { return showApiLevel == ApiVisibility.All; } set { if (value == (showApiLevel == ApiVisibility.All)) return; ShowApiLevel = ApiVisibility.All; OnPropertyChanged(nameof(ApiVisPublicOnly)); OnPropertyChanged(nameof(ApiVisPublicAndInternal)); OnPropertyChanged(nameof(ApiVisAll)); } } string? languageId; /// /// Gets/Sets the current language. /// /// /// While this isn't related to filtering, having it as part of the FilterSettings /// makes it easy to pass it down into all tree nodes. /// public string? LanguageId { get => languageId; set => SetProperty(ref languageId, value); } string? languageVersionId; /// /// Gets/Sets the current language version. /// /// /// While this isn't related to filtering, having it as part of the FilterSettings /// makes it easy to pass it down into all tree nodes. /// public string? LanguageVersionId { get { return languageVersionId; } set => SetProperty(ref languageVersionId, value); } // This class has been initially called FilterSettings, but then has been Hijacked to store language settings as well. // While the filter settings were some sort of local, the language settings are global. This is a bit of a mess. // There has been a lot of workarounds cloning the FilterSettings to pass them down to the tree nodes, without messing up the global language settings. // Finally, this filtering was not used at all, so this SearchTerm is just a placeholder to make the filtering code compile, in case someone wants to reactivate filtering in the future. public string SearchTerm => string.Empty; public bool SearchTermMatches(string value) { return true; } } } ================================================ FILE: ILSpy/Languages/CSharpBracketSearcher.cs ================================================ // 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 ICSharpCode.AvalonEdit.Document; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy { /// /// Searches matching brackets for C#. /// class CSharpBracketSearcher : IBracketSearcher { string openingBrackets = "([{"; string closingBrackets = ")]}"; public BracketSearchResult SearchBracket(IDocument document, int offset) { if (offset > 0) { char c = document.GetCharAt(offset - 1); int index = openingBrackets.IndexOf(c); int otherOffset = -1; if (index > -1) otherOffset = SearchBracketForward(document, offset, openingBrackets[index], closingBrackets[index]); index = closingBrackets.IndexOf(c); if (index > -1) otherOffset = SearchBracketBackward(document, offset - 2, openingBrackets[index], closingBrackets[index]); if (otherOffset > -1) { var result = new BracketSearchResult(Math.Min(offset - 1, otherOffset), 1, Math.Max(offset - 1, otherOffset), 1); return result; } } return null; } #region SearchBracket helper functions static int ScanLineStart(IDocument document, int offset) { for (int i = offset - 1; i > 0; --i) { if (document.GetCharAt(i) == '\n') return i + 1; } return 0; } /// /// Gets the type of code at offset.
/// 0 = Code,
/// 1 = Comment,
/// 2 = String
/// Block comments and multiline strings are not supported. ///
static int GetStartType(IDocument document, int linestart, int offset) { bool inString = false; bool inChar = false; bool verbatim = false; int result = 0; for (int i = linestart; i < offset; i++) { switch (document.GetCharAt(i)) { case '/': if (!inString && !inChar && i + 1 < document.TextLength) { if (document.GetCharAt(i + 1) == '/') { result = 1; } } break; case '"': if (!inChar) { if (inString && verbatim) { if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { ++i; // skip escaped quote inString = false; // let the string go on } else { verbatim = false; } } else if (!inString && i > 0 && document.GetCharAt(i - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\'': if (!inString) inChar = !inChar; break; case '\\': if ((inString && !verbatim) || inChar) ++i; // skip next character break; } } return (inString || inChar) ? 2 : result; } #endregion #region SearchBracketBackward int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) { if (offset + 1 >= document.TextLength) return -1; // this method parses a c# document backwards to find the matching bracket // first try "quick find" - find the matching bracket if there is no string/comment in the way int quickResult = QuickSearchBracketBackward(document, offset, openBracket, closingBracket); if (quickResult >= 0) return quickResult; // we need to parse the line from the beginning, so get the line start position int linestart = ScanLineStart(document, offset + 1); // we need to know where offset is - in a string/comment or in normal code? // ignore cases where offset is in a block comment int starttype = GetStartType(document, linestart, offset + 1); if (starttype == 1) { return -1; // start position is in a comment } // I don't see any possibility to parse a C# document backwards... // We have to do it forwards and push all bracket positions on a stack. Stack bracketStack = new Stack(); bool blockComment = false; bool lineComment = false; bool inChar = false; bool inString = false; bool verbatim = false; for (int i = 0; i <= offset; ++i) { char ch = document.GetCharAt(i); switch (ch) { case '\r': case '\n': lineComment = false; inChar = false; if (!verbatim) inString = false; break; case '/': if (blockComment) { Debug.Assert(i > 0); if (document.GetCharAt(i - 1) == '*') { blockComment = false; } } if (!inString && !inChar && i + 1 < document.TextLength) { if (!blockComment && document.GetCharAt(i + 1) == '/') { lineComment = true; } if (!lineComment && document.GetCharAt(i + 1) == '*') { blockComment = true; } } break; case '"': if (!(inChar || lineComment || blockComment)) { if (inString && verbatim) { if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { ++i; // skip escaped quote inString = false; // let the string go } else { verbatim = false; } } else if (!inString && offset > 0 && document.GetCharAt(i - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\'': if (!(inString || lineComment || blockComment)) { inChar = !inChar; } break; case '\\': if ((inString && !verbatim) || inChar) ++i; // skip next character break; default: if (ch == openBracket) { if (!(inString || inChar || lineComment || blockComment)) { bracketStack.Push(i); } } else if (ch == closingBracket) { if (!(inString || inChar || lineComment || blockComment)) { if (bracketStack.Count > 0) bracketStack.Pop(); } } break; } } if (bracketStack.Count > 0) return (int)bracketStack.Pop(); return -1; } #endregion #region SearchBracketForward int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) { bool inString = false; bool inChar = false; bool verbatim = false; bool lineComment = false; bool blockComment = false; if (offset < 0) return -1; // first try "quick find" - find the matching bracket if there is no string/comment in the way int quickResult = QuickSearchBracketForward(document, offset, openBracket, closingBracket); if (quickResult >= 0) return quickResult; // we need to parse the line from the beginning, so get the line start position int linestart = ScanLineStart(document, offset); // we need to know where offset is - in a string/comment or in normal code? // ignore cases where offset is in a block comment int starttype = GetStartType(document, linestart, offset); if (starttype != 0) return -1; // start position is in a comment/string int brackets = 1; while (offset < document.TextLength) { char ch = document.GetCharAt(offset); switch (ch) { case '\r': case '\n': lineComment = false; inChar = false; if (!verbatim) inString = false; break; case '/': if (blockComment) { Debug.Assert(offset > 0); if (document.GetCharAt(offset - 1) == '*') { blockComment = false; } } if (!inString && !inChar && offset + 1 < document.TextLength) { if (!blockComment && document.GetCharAt(offset + 1) == '/') { lineComment = true; } if (!lineComment && document.GetCharAt(offset + 1) == '*') { blockComment = true; } } break; case '"': if (!(inChar || lineComment || blockComment)) { if (inString && verbatim) { if (offset + 1 < document.TextLength && document.GetCharAt(offset + 1) == '"') { ++offset; // skip escaped quote inString = false; // let the string go } else { verbatim = false; } } else if (!inString && offset > 0 && document.GetCharAt(offset - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\'': if (!(inString || lineComment || blockComment)) { inChar = !inChar; } break; case '\\': if ((inString && !verbatim) || inChar) ++offset; // skip next character break; default: if (ch == openBracket) { if (!(inString || inChar || lineComment || blockComment)) { ++brackets; } } else if (ch == closingBracket) { if (!(inString || inChar || lineComment || blockComment)) { --brackets; if (brackets == 0) { return offset; } } } break; } ++offset; } return -1; } #endregion int QuickSearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) { int brackets = -1; // first try "quick find" - find the matching bracket if there is no string/comment in the way for (int i = offset; i >= 0; --i) { char ch = document.GetCharAt(i); if (ch == openBracket) { ++brackets; if (brackets == 0) return i; } else if (ch == closingBracket) { --brackets; } else if (ch == '"') { break; } else if (ch == '\'') { break; } else if (ch == '/' && i > 0) { if (document.GetCharAt(i - 1) == '/') break; if (document.GetCharAt(i - 1) == '*') break; } } return -1; } int QuickSearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) { int brackets = 1; // try "quick find" - find the matching bracket if there is no string/comment in the way for (int i = offset; i < document.TextLength; ++i) { char ch = document.GetCharAt(i); if (ch == openBracket) { ++brackets; } else if (ch == closingBracket) { --brackets; if (brackets == 0) return i; } else if (ch == '"') { break; } else if (ch == '\'') { break; } else if (ch == '/' && i > 0) { if (document.GetCharAt(i - 1) == '/') break; } else if (ch == '*' && i > 0) { if (document.GetCharAt(i - 1) == '/') break; } } return -1; } } } ================================================ FILE: ILSpy/Languages/CSharpHighlightingTokenWriter.cs ================================================ // 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.Collections.Generic; using System.Linq; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX.Extensions; namespace ICSharpCode.ILSpy { class CSharpHighlightingTokenWriter : DecoratingTokenWriter { HighlightingColor visibilityKeywordsColor; HighlightingColor namespaceKeywordsColor; HighlightingColor structureKeywordsColor; HighlightingColor gotoKeywordsColor; HighlightingColor queryKeywordsColor; HighlightingColor exceptionKeywordsColor; HighlightingColor checkedKeywordColor; HighlightingColor unsafeKeywordsColor; HighlightingColor valueTypeKeywordsColor; HighlightingColor referenceTypeKeywordsColor; HighlightingColor operatorKeywordsColor; HighlightingColor parameterModifierColor; HighlightingColor modifiersColor; HighlightingColor accessorKeywordsColor; HighlightingColor attributeKeywordsColor; HighlightingColor referenceTypeColor; HighlightingColor valueTypeColor; HighlightingColor interfaceTypeColor; HighlightingColor enumerationTypeColor; HighlightingColor typeParameterTypeColor; HighlightingColor delegateTypeColor; HighlightingColor methodCallColor; HighlightingColor methodDeclarationColor; HighlightingColor fieldDeclarationColor; HighlightingColor fieldAccessColor; HighlightingColor propertyDeclarationColor; HighlightingColor propertyAccessColor; HighlightingColor eventDeclarationColor; HighlightingColor eventAccessColor; HighlightingColor variableColor; HighlightingColor parameterColor; HighlightingColor valueKeywordColor; HighlightingColor thisKeywordColor; HighlightingColor trueKeywordColor; HighlightingColor typeKeywordsColor; public RichTextModel HighlightingModel { get; } = new RichTextModel(); public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutput textOutput = null, ILocatable locatable = null) : base(decoratedWriter) { var highlighting = HighlightingManager.Instance.GetDefinition("C#"); this.locatable = locatable; this.textOutput = textOutput; this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility"); this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords"); this.structureKeywordsColor = highlighting.GetNamedColor("Keywords"); this.gotoKeywordsColor = highlighting.GetNamedColor("GotoKeywords"); this.queryKeywordsColor = highlighting.GetNamedColor("QueryKeywords"); this.exceptionKeywordsColor = highlighting.GetNamedColor("ExceptionKeywords"); this.checkedKeywordColor = highlighting.GetNamedColor("CheckedKeyword"); this.unsafeKeywordsColor = highlighting.GetNamedColor("UnsafeKeywords"); this.valueTypeKeywordsColor = highlighting.GetNamedColor("ValueTypeKeywords"); this.referenceTypeKeywordsColor = highlighting.GetNamedColor("ReferenceTypeKeywords"); this.operatorKeywordsColor = highlighting.GetNamedColor("OperatorKeywords"); this.parameterModifierColor = highlighting.GetNamedColor("ParameterModifiers"); this.modifiersColor = highlighting.GetNamedColor("Modifiers"); this.accessorKeywordsColor = highlighting.GetNamedColor("GetSetAddRemove"); this.referenceTypeColor = highlighting.GetNamedColor("ReferenceTypes"); this.valueTypeColor = highlighting.GetNamedColor("ValueTypes"); this.interfaceTypeColor = highlighting.GetNamedColor("InterfaceTypes"); this.enumerationTypeColor = highlighting.GetNamedColor("EnumTypes"); this.typeParameterTypeColor = highlighting.GetNamedColor("TypeParameters"); this.delegateTypeColor = highlighting.GetNamedColor("DelegateTypes"); this.methodDeclarationColor = highlighting.GetNamedColor("MethodDeclaration"); this.methodCallColor = highlighting.GetNamedColor("MethodCall"); this.fieldDeclarationColor = highlighting.GetNamedColor("FieldDeclaration"); this.fieldAccessColor = highlighting.GetNamedColor("FieldAccess"); this.propertyDeclarationColor = highlighting.GetNamedColor("PropertyDeclaration"); this.propertyAccessColor = highlighting.GetNamedColor("PropertyAccess"); this.eventDeclarationColor = highlighting.GetNamedColor("EventDeclaration"); this.eventAccessColor = highlighting.GetNamedColor("EventAccess"); this.variableColor = highlighting.GetNamedColor("Variable"); this.parameterColor = highlighting.GetNamedColor("Parameter"); this.valueKeywordColor = highlighting.GetNamedColor("NullOrValueKeywords"); this.thisKeywordColor = highlighting.GetNamedColor("ThisOrBaseReference"); this.trueKeywordColor = highlighting.GetNamedColor("TrueFalse"); this.typeKeywordsColor = highlighting.GetNamedColor("TypeKeywords"); this.attributeKeywordsColor = highlighting.GetNamedColor("AttributeKeywords"); //this.externAliasKeywordColor = ...; } public override void WriteKeyword(Role role, string keyword) { HighlightingColor color = null; switch (keyword) { case "namespace": case "using": if (role == UsingStatement.UsingKeywordRole) color = structureKeywordsColor; else color = namespaceKeywordsColor; break; case "this": case "base": color = thisKeywordColor; break; case "true": case "false": color = trueKeywordColor; break; case "public": case "internal": case "protected": case "private": color = visibilityKeywordsColor; break; case "if": case "else": case "switch": case "case": case "default": case "while": case "do": case "for": case "foreach": case "lock": case "await": color = structureKeywordsColor; break; case "where": if (nodeStack.PeekOrDefault() is QueryClause) color = queryKeywordsColor; else color = structureKeywordsColor; break; case "in": if (nodeStack.PeekOrDefault() is ForeachStatement) color = structureKeywordsColor; else if (nodeStack.PeekOrDefault() is QueryClause) color = queryKeywordsColor; else color = parameterModifierColor; break; case "as": case "is": case "new": case "sizeof": case "typeof": case "nameof": case "stackalloc": color = typeKeywordsColor; break; case "with": if (role == WithInitializerExpression.WithKeywordRole) color = typeKeywordsColor; break; case "try": case "throw": case "catch": case "finally": color = exceptionKeywordsColor; break; case "when": if (role == CatchClause.WhenKeywordRole) color = exceptionKeywordsColor; break; case "get": case "set": case "add": case "remove": case "init": if (role == PropertyDeclaration.GetKeywordRole || role == PropertyDeclaration.SetKeywordRole || role == PropertyDeclaration.InitKeywordRole || role == CustomEventDeclaration.AddKeywordRole || role == CustomEventDeclaration.RemoveKeywordRole) color = accessorKeywordsColor; break; case "abstract": case "const": case "event": case "extern": case "override": case "sealed": case "static": case "virtual": case "volatile": case "async": case "partial": color = modifiersColor; break; case "readonly": if (role == ComposedType.ReadonlyRole) color = parameterModifierColor; else color = modifiersColor; break; case "checked": case "unchecked": color = checkedKeywordColor; break; case "fixed": case "unsafe": color = unsafeKeywordsColor; break; case "enum": case "struct": color = valueTypeKeywordsColor; break; case "class": case "interface": case "delegate": case "extension": color = referenceTypeKeywordsColor; break; case "record": color = role == Roles.RecordKeyword ? referenceTypeKeywordsColor : valueTypeKeywordsColor; break; case "select": case "group": case "by": case "into": case "from": case "orderby": case "let": case "join": case "on": case "equals": if (nodeStack.PeekOrDefault() is QueryClause) color = queryKeywordsColor; break; case "ascending": case "descending": if (nodeStack.PeekOrDefault() is QueryOrdering) color = queryKeywordsColor; break; case "explicit": case "implicit": case "operator": color = operatorKeywordsColor; break; case "params": case "ref": case "out": case "scoped": color = parameterModifierColor; break; case "break": case "continue": case "goto": case "yield": case "return": color = gotoKeywordsColor; break; } if (nodeStack.PeekOrDefault() is AttributeSection) color = attributeKeywordsColor; if (color != null) { BeginSpan(color); } base.WriteKeyword(role, keyword); if (color != null) { EndSpan(); } } public override void WritePrimitiveType(string type) { HighlightingColor color = null; switch (type) { case "new": case "notnull": // Not sure if reference type or value type color = referenceTypeKeywordsColor; break; case "bool": case "byte": case "char": case "decimal": case "double": case "enum": case "float": case "int": case "long": case "sbyte": case "short": case "struct": case "uint": case "ushort": case "ulong": case "unmanaged": case "nint": case "nuint": color = valueTypeKeywordsColor; break; case "class": case "object": case "string": case "void": case "dynamic": color = referenceTypeKeywordsColor; break; } if (color != null) { BeginSpan(color); } base.WritePrimitiveType(type); if (color != null) { EndSpan(); } } public override void WriteIdentifier(Identifier identifier) { HighlightingColor color = null; if (identifier.Parent?.GetResolveResult() is ILVariableResolveResult rr) { if (rr.Variable.Kind == VariableKind.Parameter) { if (identifier.Name == "value" && identifier.Ancestors.OfType().FirstOrDefault() is { } accessor && accessor.Role != PropertyDeclaration.GetterRole) { color = valueKeywordColor; } else { color = parameterColor; } } else { color = variableColor; } } if (identifier.Parent is AstType) { switch (identifier.Name) { case "var": color = queryKeywordsColor; break; case "global": color = structureKeywordsColor; break; } } switch (GetCurrentDefinition()) { case ITypeDefinition t: ApplyTypeColor(t, ref color); break; case IMethod: color = methodDeclarationColor; break; case IField: color = fieldDeclarationColor; break; case IProperty: color = propertyDeclarationColor; break; case IEvent: color = eventDeclarationColor; break; } switch (GetCurrentMemberReference()) { case IType t: ApplyTypeColor(t, ref color); break; case IMethod m: color = methodCallColor; if (m.IsConstructor) ApplyTypeColor(m.DeclaringType, ref color); break; case IField: color = fieldAccessColor; break; case IProperty: color = propertyAccessColor; break; case IEvent: color = eventAccessColor; break; } if (color != null) { BeginSpan(color); } base.WriteIdentifier(identifier); if (color != null) { EndSpan(); } } void ApplyTypeColor(IType type, ref HighlightingColor color) { switch (type?.Kind) { case TypeKind.Delegate: color = delegateTypeColor; break; case TypeKind.Class: color = referenceTypeColor; break; case TypeKind.Interface: color = interfaceTypeColor; break; case TypeKind.Enum: color = enumerationTypeColor; break; case TypeKind.Struct: color = valueTypeColor; break; } } public override void WritePrimitiveValue(object value, Decompiler.CSharp.Syntax.LiteralFormat format) { HighlightingColor color = null; if (value is null) { color = valueKeywordColor; } if (value is true || value is false) { color = trueKeywordColor; } if (color != null) { BeginSpan(color); } base.WritePrimitiveValue(value, format); if (color != null) { EndSpan(); } } ISymbol GetCurrentDefinition() { if (nodeStack == null || nodeStack.Count == 0) return null; var node = nodeStack.Peek(); if (node is Identifier) node = node.Parent; if (Decompiler.TextTokenWriter.IsDefinition(ref node)) return node.GetSymbol(); return null; } ISymbol GetCurrentMemberReference() { if (nodeStack == null || nodeStack.Count == 0) return null; AstNode node = nodeStack.Peek(); var symbol = node.GetSymbol(); if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) { symbol = node.Parent.GetSymbol(); } if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) { var ctorSymbol = node.Parent.GetSymbol(); if (ctorSymbol != null) symbol = ctorSymbol; } if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { var declaringType = member.DeclaringType; if (declaringType != null && declaringType.Kind == TypeKind.Delegate) return null; } return symbol; } readonly Stack nodeStack = new Stack(); public override void StartNode(AstNode node) { nodeStack.Push(node); base.StartNode(node); } public override void EndNode(AstNode node) { base.EndNode(node); nodeStack.Pop(); } readonly Stack colorStack = new Stack(); HighlightingColor currentColor = new HighlightingColor(); int currentColorBegin = -1; readonly ILocatable locatable; readonly ISmartTextOutput textOutput; private void BeginSpan(HighlightingColor highlightingColor) { if (textOutput != null) { textOutput.BeginSpan(highlightingColor); return; } if (currentColorBegin > -1) HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor); colorStack.Push(currentColor); currentColor = currentColor.Clone(); currentColorBegin = locatable.Length; currentColor.MergeWith(highlightingColor); currentColor.Freeze(); } private void EndSpan() { if (textOutput != null) { textOutput.EndSpan(); return; } HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor); currentColor = colorStack.Pop(); currentColorBegin = locatable.Length; } } } ================================================ FILE: ILSpy/Languages/CSharpILMixedLanguage.cs ================================================ // 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.Composition; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Windows.Media; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; namespace ICSharpCode.ILSpy { using SequencePoint = ICSharpCode.Decompiler.DebugInfo.SequencePoint; [Export(typeof(Language))] [Shared] class CSharpILMixedLanguage(SettingsService settingsService, DockWorkspace dockWorkspace) : ILLanguage(dockWorkspace) { public override string Name => "IL with C#"; protected override ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options) { var displaySettings = settingsService.DisplaySettings; return new ReflectionDisassembler(output, new MixedMethodBodyDisassembler(output, options) { DetectControlStructure = detectControlStructure, ShowSequencePoints = options.DecompilerSettings.ShowDebugInfo }, options.CancellationToken) { ShowMetadataTokens = displaySettings.ShowMetadataTokens, ShowMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10, ShowRawRVAOffsetAndBytes = displaySettings.ShowRawOffsetsAndBytesBeforeInstruction, ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions, DecodeCustomAttributeBlobs = displaySettings.DecodeCustomAttributeBlobs }; } static CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilationOptions options) { CSharpDecompiler decompiler = new CSharpDecompiler(module, module.GetAssemblyResolver(), options.DecompilerSettings); decompiler.CancellationToken = options.CancellationToken; return decompiler; } static void WriteCode(TextWriter output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) { syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); TokenWriter tokenWriter = new TextWriterTokenWriter(output) { IndentationString = settings.CSharpFormattingOptions.IndentationString }; tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); } class MixedMethodBodyDisassembler : MethodBodyDisassembler { readonly DecompilationOptions options; // list sorted by IL offset IList sequencePoints; // lines of raw c# source code string[] codeLines; public MixedMethodBodyDisassembler(ITextOutput output, DecompilationOptions options) : base(output, options.CancellationToken) { this.options = options; } public override void Disassemble(MetadataFile module, MethodDefinitionHandle handle) { try { var csharpOutput = new StringWriter(); CSharpDecompiler decompiler = CreateDecompiler(module, options); var st = decompiler.Decompile(handle); WriteCode(csharpOutput, options.DecompilerSettings, st, decompiler.TypeSystem); var mapping = decompiler.CreateSequencePoints(st).FirstOrDefault(kvp => (kvp.Key.MoveNextMethod ?? kvp.Key.Method)?.MetadataToken == handle); this.sequencePoints = mapping.Value ?? (IList)EmptyList.Instance; this.codeLines = csharpOutput.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None); base.Disassemble(module, handle); } finally { this.sequencePoints = null; this.codeLines = null; } } protected override void WriteInstruction(ITextOutput output, MetadataFile metadata, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) { int index = sequencePoints.BinarySearch(blob.Offset, seq => seq.Offset); if (index >= 0) { var info = sequencePoints[index]; var highlightingOutput = output as ISmartTextOutput; if (!info.IsHidden) { for (int line = info.StartLine; line <= info.EndLine; line++) { if (highlightingOutput != null) { string text = codeLines[line - 1]; int startColumn = 1; int endColumn = text.Length + 1; if (line == info.StartLine) startColumn = info.StartColumn; if (line == info.EndLine) endColumn = info.EndColumn; WriteHighlightedCommentLine(highlightingOutput, text, startColumn - 1, endColumn - 1, info.StartLine == info.EndLine); } else WriteCommentLine(output, codeLines[line - 1]); } } else { output.Write("// "); highlightingOutput?.BeginSpan(gray); output.WriteLine("(no C# code)"); highlightingOutput?.EndSpan(); } } base.WriteInstruction(output, metadata, methodHandle, ref blob, methodRva); } HighlightingColor gray = new HighlightingColor { Foreground = new SimpleHighlightingBrush(Colors.DarkGray) }; void WriteHighlightedCommentLine(ISmartTextOutput output, string text, int startColumn, int endColumn, bool isSingleLine) { if (startColumn > text.Length) { Debug.Fail("startColumn is invalid"); startColumn = text.Length; } if (endColumn > text.Length) { Debug.Fail("endColumn is invalid"); endColumn = text.Length; } output.Write("// "); output.BeginSpan(gray); if (isSingleLine) output.Write(text.Substring(0, startColumn).TrimStart()); else output.Write(text.Substring(0, startColumn)); output.EndSpan(); output.Write(text.Substring(startColumn, endColumn - startColumn)); output.BeginSpan(gray); output.Write(text.Substring(endColumn)); output.EndSpan(); output.WriteLine(); } void WriteCommentLine(ITextOutput output, string text) { output.WriteLine("// " + text); } } } } ================================================ FILE: ILSpy/Languages/CSharpLanguage.cs ================================================ // 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 // 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.Composition; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Windows; using System.Windows.Controls; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX; using LanguageVersion = ICSharpCode.ILSpyX.LanguageVersion; namespace ICSharpCode.ILSpy { /// /// C# decompiler integration into ILSpy. /// Note: if you're interested in using the decompiler without the ILSpy UI, /// please directly use the CSharpDecompiler class. /// [Export(typeof(Language))] [Shared] public class CSharpLanguage : Language { string name = "C#"; bool showAllMembers = false; int transformCount = int.MaxValue; #if DEBUG internal static IEnumerable GetDebugLanguages() { string lastTransformName = "no transforms"; int transformCount = 0; foreach (var transform in CSharpDecompiler.GetAstTransforms()) { yield return new() { transformCount = transformCount, name = "C# - " + lastTransformName, showAllMembers = true }; lastTransformName = "after " + transform.GetType().Name; transformCount++; } yield return new() { name = "C# - " + lastTransformName, showAllMembers = true }; } #endif public override string Name { get { return name; } } public override string FileExtension { get { return ".cs"; } } public override string ProjectFileExtension { get { return ".csproj"; } } IReadOnlyList versions; public override IReadOnlyList LanguageVersions { get { if (versions == null) { versions = new List() { new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp1.ToString(), "C# 1.0 / VS .NET"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp2.ToString(), "C# 2.0 / VS 2005"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp3.ToString(), "C# 3.0 / VS 2008"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp4.ToString(), "C# 4.0 / VS 2010"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp5.ToString(), "C# 5.0 / VS 2012"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp6.ToString(), "C# 6.0 / VS 2015"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7.ToString(), "C# 7.0 / VS 2017"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_1.ToString(), "C# 7.1 / VS 2017.3"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_2.ToString(), "C# 7.2 / VS 2017.4"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_3.ToString(), "C# 7.3 / VS 2017.7"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp8_0.ToString(), "C# 8.0 / VS 2019"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp9_0.ToString(), "C# 9.0 / VS 2019.8"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp10_0.ToString(), "C# 10.0 / VS 2022"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp11_0.ToString(), "C# 11.0 / VS 2022.4"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp12_0.ToString(), "C# 12.0 / VS 2022.8"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp13_0.ToString(), "C# 13.0 / VS 2022.12"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp14_0.ToString(), "C# 14.0 / VS 2026"), }; } return versions; } } CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilationOptions options) { CSharpDecompiler decompiler = new CSharpDecompiler(module, module.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences), options.DecompilerSettings); decompiler.CancellationToken = options.CancellationToken; decompiler.DebugInfoProvider = module.GetDebugInfoOrNull(); while (decompiler.AstTransforms.Count > transformCount) decompiler.AstTransforms.RemoveAt(decompiler.AstTransforms.Count - 1); if (options.EscapeInvalidIdentifiers) { decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); } return decompiler; } void WriteCode(ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) { syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); output.IndentationString = settings.CSharpFormattingOptions.IndentationString; TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem); if (output is ISmartTextOutput highlightingOutput) { tokenWriter = new CSharpHighlightingTokenWriter(tokenWriter, highlightingOutput); } syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); } public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { MetadataFile assembly = method.ParentModule.MetadataFile; CSharpDecompiler decompiler = CreateDecompiler(assembly, options); AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); WriteCommentLine(output, TypeToString(method.DeclaringType)); var methodDefinition = decompiler.TypeSystem.MainModule.ResolveEntity(method.MetadataToken) as IMethod; if (methodDefinition.IsConstructor && methodDefinition.DeclaringType.IsReferenceType != false) { var members = CollectFieldsAndCtors(methodDefinition.DeclaringTypeDefinition, methodDefinition.IsStatic); decompiler.AstTransforms.Add(new SelectCtorTransform(methodDefinition)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); } else { WriteCode(output, options.DecompilerSettings, decompiler.Decompile(method.MetadataToken), decompiler.TypeSystem); } } class SelectCtorTransform : IAstTransform { readonly IMethod ctor; readonly HashSet removedSymbols = new HashSet(); public SelectCtorTransform(IMethod ctor) { this.ctor = ctor; } public void Run(AstNode rootNode, TransformContext context) { ConstructorDeclaration ctorDecl = null; foreach (var node in rootNode.Children) { switch (node) { case ConstructorDeclaration ctor: if (ctor.GetSymbol() == this.ctor) { ctorDecl = ctor; } else { // remove other ctors ctor.Remove(); removedSymbols.Add(ctor.GetSymbol()); } break; case FieldDeclaration fd: // Remove any fields without initializers if (fd.Variables.All(v => v.Initializer.IsNull)) { fd.Remove(); removedSymbols.Add(fd.GetSymbol()); } break; case EventDeclaration ed: // Remove any events without initializers if (ed.Variables.All(v => v.Initializer.IsNull)) { ed.Remove(); removedSymbols.Add(ed.GetSymbol()); } break; case PropertyDeclaration pd: // Remove any properties without initializers if (pd.Initializer.IsNull) { pd.Remove(); removedSymbols.Add(pd.GetSymbol()); } break; case CustomEventDeclaration ced: case IndexerDeclaration id: node.Remove(); removedSymbols.Add(node.GetSymbol()); break; } } if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { // remove all non-constructor declarations foreach (var node in rootNode.Children) { if (node is not ConstructorDeclaration) { node.Remove(); removedSymbols.Add(node.GetSymbol()); } } } foreach (var node in rootNode.Children) { if (node is Comment && removedSymbols.Contains(node.GetSymbol())) node.Remove(); } } } public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) { MetadataFile assembly = property.ParentModule.MetadataFile; CSharpDecompiler decompiler = CreateDecompiler(assembly, options); AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); WriteCommentLine(output, TypeToString(property.DeclaringType)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(property.MetadataToken), decompiler.TypeSystem); } public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) { MetadataFile assembly = field.ParentModule.MetadataFile; CSharpDecompiler decompiler = CreateDecompiler(assembly, options); AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); WriteCommentLine(output, TypeToString(field.DeclaringType)); if (field.IsConst) { WriteCode(output, options.DecompilerSettings, decompiler.Decompile(field.MetadataToken), decompiler.TypeSystem); } else { var members = CollectFieldsAndCtors(field.DeclaringTypeDefinition, field.IsStatic); var resolvedField = decompiler.TypeSystem.MainModule.GetDefinition((FieldDefinitionHandle)field.MetadataToken); decompiler.AstTransforms.Add(new SelectFieldTransform(resolvedField)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); } } static List CollectFieldsAndCtors(ITypeDefinition type, bool isStatic) { var members = new List(); foreach (var field in type.Fields) { if (!field.MetadataToken.IsNil && field.IsStatic == isStatic) members.Add(field.MetadataToken); } foreach (var e in type.Events) { if (!e.MetadataToken.IsNil && e.IsStatic == isStatic) members.Add(e.MetadataToken); } foreach (var p in type.Properties) { if (!p.MetadataToken.IsNil && p.IsStatic == isStatic && !p.IsIndexer) members.Add(p.MetadataToken); } foreach (var ctor in type.Methods) { if (!ctor.MetadataToken.IsNil && ctor.IsConstructor && ctor.IsStatic == isStatic) members.Add(ctor.MetadataToken); } return members; } /// /// Removes all top-level members except for the specified fields. /// sealed class SelectFieldTransform : IAstTransform { readonly IField field; public SelectFieldTransform(IField field) { this.field = field; } public void Run(AstNode rootNode, TransformContext context) { foreach (var node in rootNode.Children) { switch (node) { case EntityDeclaration ed: if (node.GetSymbol() != field) node.Remove(); break; case Comment c: if (c.GetSymbol() != field) node.Remove(); break; } } } } public override void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options) { MetadataFile assembly = @event.ParentModule.MetadataFile; CSharpDecompiler decompiler = CreateDecompiler(assembly, options); AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); WriteCommentLine(output, TypeToString(@event.DeclaringType)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(@event.MetadataToken), decompiler.TypeSystem); } public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) { MetadataFile assembly = type.ParentModule.MetadataFile; CSharpDecompiler decompiler = CreateDecompiler(assembly, options); AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); WriteCommentLine(output, TypeToString(type, ConversionFlags.UseFullyQualifiedTypeNames | ConversionFlags.UseFullyQualifiedEntityNames)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(type.MetadataToken), decompiler.TypeSystem); } void AddReferenceWarningMessage(MetadataFile module, ITextOutput output) { var loadedAssembly = AssemblyTreeModel.AssemblyList.GetAssemblies().FirstOrDefault(la => la.GetMetadataFileOrNull() == module); if (loadedAssembly == null || !loadedAssembly.LoadedAssemblyReferencesInfo.HasErrors) return; string line1 = Properties.Resources.WarningSomeAssemblyReference; string line2 = Properties.Resources.PropertyManuallyMissingReferencesListLoadedAssemblies; AddWarningMessage(module, output, line1, line2, Properties.Resources.ShowAssemblyLoad, Images.ViewCode, delegate { ILSpyTreeNode assemblyNode = AssemblyTreeModel.FindTreeNode(module); assemblyNode.EnsureLazyChildren(); AssemblyTreeModel.SelectNode(assemblyNode.Children.OfType().Single()); }); } void AddReferenceAssemblyWarningMessage(MetadataFile module, ITextOutput output) { var metadata = module.Metadata; if (!metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly)) return; string line1 = Properties.Resources.WarningAsmMarkedRef; AddWarningMessage(module, output, line1); } void AddWarningMessage(MetadataFile module, ITextOutput output, string line1, string line2 = null, string buttonText = null, System.Windows.Media.ImageSource buttonImage = null, RoutedEventHandler buttonClickHandler = null) { if (output is ISmartTextOutput fancyOutput) { string text = line1; if (!string.IsNullOrEmpty(line2)) text += Environment.NewLine + line2; fancyOutput.AddUIElement(() => new StackPanel { Margin = new Thickness(5), Orientation = Orientation.Horizontal, Children = { new Image { Width = 32, Height = 32, Source = Images.Load(this, "Images/Warning") }, new TextBlock { Margin = new Thickness(5, 0, 0, 0), Text = text } } }); fancyOutput.WriteLine(); if (buttonText != null && buttonClickHandler != null) { fancyOutput.AddButton(buttonImage, buttonText, buttonClickHandler); fancyOutput.WriteLine(); } } else { WriteCommentLine(output, line1); if (!string.IsNullOrEmpty(line2)) WriteCommentLine(output, line2); } } public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { var module = assembly.GetMetadataFileOrNull(); if (module == null) { return null; } if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { if (!WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module)) { options.DecompilerSettings.UseSdkStyleProjectFormat = false; } var decompiler = new ILSpyWholeProjectDecompiler(assembly, options) { ProgressIndicator = options.Progress }; return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else { AddReferenceAssemblyWarningMessage(module, output); AddReferenceWarningMessage(module, output); output.WriteLine(); base.DecompileAssembly(assembly, output, options); // don't automatically load additional assemblies when an assembly node is selected in the tree view IAssemblyResolver assemblyResolver = assembly.GetAssemblyResolver(loadOnDemand: options.FullDecompilation && options.DecompilerSettings.AutoLoadAssemblyReferences); var typeSystem = new DecompilerTypeSystem(module, assemblyResolver, options.DecompilerSettings); var globalType = typeSystem.MainModule.TypeDefinitions.FirstOrDefault(); if (globalType != null) { output.Write("// Global type: "); output.WriteReference(globalType, ILAmbience.EscapeName(globalType.FullName)); output.WriteLine(); } var metadata = module.Metadata; var corHeader = module.CorHeader; if (module is PEFile peFile && corHeader != null) { var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress); if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition) { var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new Decompiler.TypeSystem.GenericContext()); if (entrypoint != null) { output.Write("// Entry point: "); output.WriteReference(entrypoint, ILAmbience.EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name)); output.WriteLine(); } } output.WriteLine("// Architecture: " + GetPlatformDisplayName(peFile)); if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0) { output.WriteLine("// This assembly contains unmanaged code."); } string runtimeName = GetRuntimeDisplayName(module); if (runtimeName != null) { output.WriteLine("// Runtime: " + runtimeName); } if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.StrongNameSigned) != 0) { output.WriteLine("// This assembly is signed with a strong name key."); } if (peFile.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible)) { output.WriteLine("// This assembly was compiled using the /deterministic option."); } if (module.Metadata.MetadataKind != MetadataKind.Ecma335) { output.WriteLine("// This assembly was loaded with Windows Runtime projections applied."); } } else { string runtimeName = GetRuntimeDisplayName(module); if (runtimeName != null) { output.WriteLine("// Runtime: " + runtimeName); } } if (metadata.IsAssembly) { var asm = metadata.GetAssemblyDefinition(); if (asm.HashAlgorithm != AssemblyHashAlgorithm.None) output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper()); if (!asm.PublicKey.IsNil) { output.Write("// Public key: "); var reader = metadata.GetBlobReader(asm.PublicKey); while (reader.RemainingBytes > 0) output.Write(reader.ReadByte().ToString("x2")); output.WriteLine(); } } var debugInfo = assembly.GetDebugInfoOrNull(); if (debugInfo != null) { output.WriteLine("// Debug info: " + debugInfo.Description); } output.WriteLine(); CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings); decompiler.CancellationToken = options.CancellationToken; if (options.EscapeInvalidIdentifiers) { decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); } SyntaxTree st; if (options.FullDecompilation) { st = decompiler.DecompileWholeModuleAsSingleFile(); } else { st = decompiler.DecompileModuleAndAssemblyAttributes(); } WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); return null; } } class ILSpyWholeProjectDecompiler : WholeProjectDecompiler { readonly LoadedAssembly assembly; readonly DecompilationOptions options; public ILSpyWholeProjectDecompiler(LoadedAssembly assembly, DecompilationOptions options) : base(options.DecompilerSettings, assembly.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences, options.DecompilerSettings.ApplyWindowsRuntimeProjections), null, assembly.GetAssemblyReferenceClassifier(options.DecompilerSettings.ApplyWindowsRuntimeProjections), assembly.GetDebugInfoOrNull()) { this.assembly = assembly; this.options = options; } protected override IEnumerable WriteResourceToFile(string fileName, string resourceName, Stream entryStream) { var context = new ResourceFileHandlerContext(options); foreach (var handler in ResourceFileHandlers) { if (handler.CanHandle(fileName, context)) { entryStream.Position = 0; fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, context); return new[] { new ProjectItemInfo(handler.EntryType, fileName) { PartialTypes = context.PartialTypes }.With(context.AdditionalProperties) }; } } return base.WriteResourceToFile(fileName, resourceName, entryStream); } } CSharpAmbience CreateAmbience() { CSharpAmbience ambience = new CSharpAmbience(); // Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewTypeFlags, if this ever changes. ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList; var decompilerSettings = SettingsService.DecompilerSettings.Clone(); if (!Enum.TryParse(AssemblyTreeModel.CurrentLanguageVersion?.Version, out Decompiler.CSharp.LanguageVersion languageVersion)) languageVersion = Decompiler.CSharp.LanguageVersion.Latest; decompilerSettings.SetLanguageVersion(languageVersion); if (decompilerSettings.LiftNullables) { ambience.ConversionFlags |= ConversionFlags.UseNullableSpecifierForValueTypes; } if (decompilerSettings.IntroducePrivateProtectedAccessibility) { ambience.ConversionFlags |= ConversionFlags.UsePrivateProtectedAccessibility; } return ambience; } public override string EntityToString(IEntity entity, ConversionFlags conversionFlags) { // Do not forget to update CSharpAmbienceTests, if this ever changes. var ambience = CreateAmbience(); ambience.ConversionFlags |= conversionFlags | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; return ambience.ConvertSymbol(entity); } public override string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames) { if (type == null) throw new ArgumentNullException(nameof(type)); var ambience = CreateAmbience(); // Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewFlags, if this ever changes. ambience.ConversionFlags |= conversionFlags; if (type is ITypeDefinition definition) { return ambience.ConvertSymbol(definition); } else { return ambience.ConvertType(type); } } static string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName, bool omitGenerics) { var currentTypeDefHandle = handle; var typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); List builder = new List(); while (!currentTypeDefHandle.IsNil) { if (builder.Count > 0) builder.Add("."); typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); var part = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(typeDef.Name), out int typeParamCount); var genericParams = typeDef.GetGenericParameters(); if (!omitGenerics && genericParams.Count > 0) { builder.Add(">"); int firstIndex = genericParams.Count - typeParamCount; for (int i = genericParams.Count - 1; i >= genericParams.Count - typeParamCount; i--) { builder.Add(metadata.GetString(metadata.GetGenericParameter(genericParams[i]).Name)); builder.Add(i == firstIndex ? "<" : ","); } } builder.Add(part); currentTypeDefHandle = typeDef.GetDeclaringType(); if (!fullName) break; } if (fullName && !typeDef.Namespace.IsNil) { builder.Add("."); builder.Add(metadata.GetString(typeDef.Namespace)); } switch (builder.Count) { case 0: return string.Empty; case 1: return builder[0]; case 2: return builder[1] + builder[0]; case 3: return builder[2] + builder[1] + builder[0]; case 4: return builder[3] + builder[2] + builder[1] + builder[0]; default: builder.Reverse(); return string.Concat(builder); } } public override string GetEntityName(MetadataFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName, omitGenerics); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); var declaringType = fd.GetDeclaringType(); if (fullName) return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(fd.Name); return metadata.GetString(fd.Name); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); declaringType = md.GetDeclaringType(); string methodName = metadata.GetString(md.Name); switch (methodName) { case ".ctor": case ".cctor": var td = metadata.GetTypeDefinition(declaringType); methodName = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(td.Name)); break; case "Finalize": const MethodAttributes finalizerAttributes = (MethodAttributes.Virtual | MethodAttributes.Family | MethodAttributes.HideBySig); if ((md.Attributes & finalizerAttributes) != finalizerAttributes) goto default; MethodSignature methodSignature = md.DecodeSignature(MetadataExtensions.MinimalSignatureTypeProvider, default); if (methodSignature.GenericParameterCount != 0 || methodSignature.ParameterTypes.Length != 0) goto default; td = metadata.GetTypeDefinition(declaringType); methodName = "~" + ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(td.Name)); break; default: var genericParams = md.GetGenericParameters(); if (!omitGenerics && genericParams.Count > 0) { methodName += "<"; int i = 0; foreach (var h in genericParams) { if (i > 0) methodName += ","; var gp = metadata.GetGenericParameter(h); methodName += metadata.GetString(gp.Name); } methodName += ">"; } break; } if (fullName) return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + methodName; return methodName; case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName && !declaringType.IsNil) return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(ed.Name); return metadata.GetString(ed.Name); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName && !declaringType.IsNil) return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(pd.Name); return metadata.GetString(pd.Name); default: return null; } } public override bool ShowMember(IEntity member) { MetadataFile assembly = member.ParentModule.MetadataFile; var decompilerSettings = SettingsService.DecompilerSettings.Clone(); if (!Enum.TryParse(AssemblyTreeModel.CurrentLanguageVersion?.Version, out Decompiler.CSharp.LanguageVersion languageVersion)) languageVersion = Decompiler.CSharp.LanguageVersion.Latest; decompilerSettings.SetLanguageVersion(languageVersion); return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, decompilerSettings); } public override RichText GetRichTextTooltip(IEntity entity) { var flags = ConversionFlags.All & ~(ConversionFlags.ShowBody | ConversionFlags.PlaceReturnTypeAfterParameterList); var output = new StringWriter(); var decoratedWriter = new TextWriterTokenWriter(output); var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter); var settings = SettingsService.DecompilerSettings.Clone(); if (!Enum.TryParse(AssemblyTreeModel.CurrentLanguageVersion?.Version, out Decompiler.CSharp.LanguageVersion languageVersion)) languageVersion = Decompiler.CSharp.LanguageVersion.Latest; settings.SetLanguageVersion(languageVersion); if (!settings.LiftNullables) { flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes; } if (settings.RecordClasses) { flags |= ConversionFlags.SupportRecordClasses; } if (settings.RecordStructs) { flags |= ConversionFlags.SupportRecordStructs; } if (settings.UnsignedRightShift) { flags |= ConversionFlags.SupportUnsignedRightShift; } if (settings.CheckedOperators) { flags |= ConversionFlags.SupportOperatorChecked; } if (settings.InitAccessors) { flags |= ConversionFlags.SupportInitAccessors; } if (settings.IntroducePrivateProtectedAccessibility) { flags |= ConversionFlags.UsePrivateProtectedAccessibility; } if (entity is IMethod m && m.IsLocalFunction) { writer.WriteIdentifier(Identifier.Create("(local)")); } new CSharpAmbience() { ConversionFlags = flags, }.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions); return new RichText(output.ToString(), writer.HighlightingModel); } public override CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member) { return CSharpDecompiler.GetCodeMappingInfo(module, member); } CSharpBracketSearcher bracketSearcher = new CSharpBracketSearcher(); public override IBracketSearcher BracketSearcher => bracketSearcher; } } ================================================ FILE: ILSpy/Languages/ILAstLanguage.cs ================================================ // 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 // 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 ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using SRM = System.Reflection.Metadata; namespace ICSharpCode.ILSpy { #if DEBUG /// /// Represents the ILAst "language" used for debugging purposes. /// abstract class ILAstLanguage : Language { public event EventHandler StepperUpdated; readonly string name; protected ILAstLanguage(string name) { this.name = name; } protected virtual void OnStepperUpdated(EventArgs e = null) { StepperUpdated?.Invoke(this, e ?? new EventArgs()); } public Stepper Stepper { get; set; } = new Stepper(); public override string Name { get { return name; } } internal static IEnumerable GetDebugLanguages(DockWorkspace dockWorkspace) { yield return new TypedIL(); yield return new BlockIL(CSharpDecompiler.GetILTransforms(), dockWorkspace); } public override string FileExtension { get { return ".il"; } } public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { base.DecompileMethod(method, output, options); new ReflectionDisassembler(output, options.CancellationToken) .DisassembleMethodHeader(method.ParentModule.MetadataFile, (SRM.MethodDefinitionHandle)method.MetadataToken); output.WriteLine(); output.WriteLine(); } class TypedIL() : ILAstLanguage("Typed IL") { public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { base.DecompileMethod(method, output, options); var module = method.ParentModule.MetadataFile; var methodDef = module.Metadata.GetMethodDefinition((SRM.MethodDefinitionHandle)method.MetadataToken); if (!methodDef.HasBody()) return; var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); ILReader reader = new ILReader(typeSystem.MainModule); var methodBody = module.GetMethodBody(methodDef.RelativeVirtualAddress); reader.WriteTypedIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, output, cancellationToken: options.CancellationToken); } } class BlockIL(IReadOnlyList transforms, DockWorkspace dockWorkspace) : ILAstLanguage("ILAst") { public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { base.DecompileMethod(method, output, options); var module = method.ParentModule.MetadataFile; var metadata = module.Metadata; var methodDef = metadata.GetMethodDefinition((SRM.MethodDefinitionHandle)method.MetadataToken); if (!methodDef.HasBody()) return; IAssemblyResolver assemblyResolver = module.GetAssemblyResolver(); var typeSystem = new DecompilerTypeSystem(module, assemblyResolver); var reader = new ILReader(typeSystem.MainModule); reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols; reader.UseRefLocalsForAccurateOrderOfEvaluation = options.DecompilerSettings.UseRefLocalsForAccurateOrderOfEvaluation; var methodBody = module.GetMethodBody(methodDef.RelativeVirtualAddress); ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken); var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken }; ILTransformContext context = decompiler.CreateILTransformContext(il); context.Stepper.StepLimit = options.StepLimit; context.Stepper.IsDebug = options.IsDebug; try { il.RunTransforms(transforms, context); } catch (StepLimitReachedException) { } catch (Exception ex) { output.WriteLine(ex.ToString()); output.WriteLine(); output.WriteLine("ILAst after the crash:"); } finally { // update stepper even if a transform crashed unexpectedly if (options.StepLimit == int.MaxValue) { Stepper = context.Stepper; OnStepperUpdated(new EventArgs()); } } (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate { dockWorkspace.ShowToolPane(DebugStepsPaneModel.PaneContentId); }); output.WriteLine(); il.WriteTo(output, DebugSteps.Options); } } } #endif } ================================================ FILE: ILSpy/Languages/ILLanguage.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy { /// /// IL language support. /// /// /// Currently comes in two versions: /// flat IL (detectControlStructure=false) and structured IL (detectControlStructure=true). /// [Export(typeof(Language))] [Shared] public class ILLanguage(DockWorkspace dockWorkspace) : Language { protected bool detectControlStructure = true; public override string Name { get { return "IL"; } } public override string FileExtension { get { return ".il"; } } protected virtual ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options) { var displaySettings = SettingsService.DisplaySettings; output.IndentationString = options.DecompilerSettings.CSharpFormattingOptions.IndentationString; return new ReflectionDisassembler(output, options.CancellationToken) { DetectControlStructure = detectControlStructure, ShowSequencePoints = options.DecompilerSettings.ShowDebugInfo, ShowMetadataTokens = displaySettings.ShowMetadataTokens, ShowMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10, ShowRawRVAOffsetAndBytes = displaySettings.ShowRawOffsetsAndBytesBeforeInstruction, ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions, DecodeCustomAttributeBlobs = displaySettings.DecodeCustomAttributeBlobs }; } public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = method.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleMethod(module, (MethodDefinitionHandle)method.MetadataToken); } public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = field.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleField(module, (FieldDefinitionHandle)field.MetadataToken); } public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = property.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleProperty(module, (PropertyDefinitionHandle)property.MetadataToken); var pd = module.Metadata.GetPropertyDefinition((PropertyDefinitionHandle)property.MetadataToken); var accessors = pd.GetAccessors(); if (!accessors.Getter.IsNil) { output.WriteLine(); dis.DisassembleMethod(module, accessors.Getter); } if (!accessors.Setter.IsNil) { output.WriteLine(); dis.DisassembleMethod(module, accessors.Setter); } /*foreach (var m in property.OtherMethods) { output.WriteLine(); dis.DisassembleMethod(m); }*/ } public override void DecompileEvent(IEvent ev, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = ev.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleEvent(module, (EventDefinitionHandle)ev.MetadataToken); var ed = ((MetadataReader)module.Metadata).GetEventDefinition((EventDefinitionHandle)ev.MetadataToken); var accessors = ed.GetAccessors(); if (!accessors.Adder.IsNil) { output.WriteLine(); dis.DisassembleMethod(module, accessors.Adder); } if (!accessors.Remover.IsNil) { output.WriteLine(); dis.DisassembleMethod(module, accessors.Remover); } if (!accessors.Raiser.IsNil) { output.WriteLine(); dis.DisassembleMethod(module, accessors.Raiser); } /*foreach (var m in ev.OtherMethods) { output.WriteLine(); dis.DisassembleMethod(m); }*/ } public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = type.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleType(module, (TypeDefinitionHandle)type.MetadataToken); } public override void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) { var dis = CreateDisassembler(output, options); MetadataFile module = types.FirstOrDefault()?.ParentModule.MetadataFile; dis.AssemblyResolver = module.GetAssemblyResolver(); dis.DebugInfo = module.GetDebugInfoOrNull(); dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); } public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { output.WriteLine("// " + assembly.FileName); output.WriteLine(); var module = assembly.GetMetadataFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { throw new NotSupportedException($"Language '{Name}' does not support exporting assemblies as projects!"); } var metadata = module.Metadata; var dis = CreateDisassembler(output, options); // don't automatically load additional assemblies when an assembly node is selected in the tree view dis.AssemblyResolver = module.GetAssemblyResolver(loadOnDemand: options.FullDecompilation); dis.DebugInfo = module.GetDebugInfoOrNull(); if (options.FullDecompilation) dis.WriteAssemblyReferences(metadata); if (metadata.IsAssembly) dis.WriteAssemblyHeader(module); output.WriteLine(); dis.WriteModuleHeader(module); if (options.FullDecompilation) { output.WriteLine(); output.WriteLine(); dis.WriteModuleContents(module); } return null; } public override RichText GetRichTextTooltip(IEntity entity) { var output = new AvalonEditTextOutput() { IgnoreNewLineAndIndent = true }; var disasm = CreateDisassembler(output, dockWorkspace.ActiveTabPage.CreateDecompilationOptions()); MetadataFile module = entity.ParentModule?.MetadataFile; if (module == null) { return null; } switch (entity.SymbolKind) { case SymbolKind.TypeDefinition: disasm.DisassembleTypeHeader(module, (TypeDefinitionHandle)entity.MetadataToken); break; case SymbolKind.Field: disasm.DisassembleFieldHeader(module, (FieldDefinitionHandle)entity.MetadataToken); break; case SymbolKind.Property: case SymbolKind.Indexer: disasm.DisassemblePropertyHeader(module, (PropertyDefinitionHandle)entity.MetadataToken); break; case SymbolKind.Event: disasm.DisassembleEventHeader(module, (EventDefinitionHandle)entity.MetadataToken); break; case SymbolKind.Method: case SymbolKind.Operator: case SymbolKind.Constructor: case SymbolKind.Destructor: case SymbolKind.Accessor: disasm.DisassembleMethodHeader(module, (MethodDefinitionHandle)entity.MetadataToken); break; default: output.Write(GetDisplayName(entity, true, true, true)); break; } return new DocumentHighlighter(output.GetDocument(), base.SyntaxHighlighting).HighlightLine(1).ToRichText(); } } } ================================================ FILE: ILSpy/Languages/IResourceFileHandler.cs ================================================ // 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 // 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.Collections.Generic; using System.IO; using ICSharpCode.Decompiler; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy { public interface IResourceFileHandler { string EntryType { get; } bool CanHandle(string name, ResourceFileHandlerContext context); string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, ResourceFileHandlerContext context); } public class ResourceFileHandlerContext { readonly List partialTypes = new(); internal List PartialTypes => partialTypes; readonly Dictionary additionalProperties = new(); public Dictionary AdditionalProperties => additionalProperties; public DecompilationOptions DecompilationOptions { get; } public ResourceFileHandlerContext(DecompilationOptions options) { this.DecompilationOptions = options; } public void AddPartialTypeInfo(PartialTypeInfo info) { this.PartialTypes.Add(info); } } } ================================================ FILE: ILSpy/Languages/Language.cs ================================================ // 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 // 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; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy { /// /// Base class for language-specific decompiler implementations. /// /// /// Implementations of this class must be thread-safe. /// public abstract class Language : ILanguage { protected static SettingsService SettingsService { get; } = App.ExportProvider.GetExportedValue(); protected static AssemblyTreeModel AssemblyTreeModel { get; } = App.ExportProvider.GetExportedValue(); protected static ICollection ResourceFileHandlers { get; } = App.ExportProvider.GetExportedValues().ToArray(); /// /// Gets the name of the language (as shown in the UI) /// public abstract string Name { get; } /// /// Gets the file extension used by source code files in this language. /// public abstract string FileExtension { get; } public virtual string ProjectFileExtension { get { return null; } } public virtual IReadOnlyList LanguageVersions { get { return EmptyList.Instance; } } public bool HasLanguageVersions => LanguageVersions.Count > 0; /// /// Gets the syntax highlighting used for this language. /// public virtual IHighlightingDefinition SyntaxHighlighting { get { return HighlightingManager.Instance.GetDefinitionByExtension(FileExtension); } } public virtual TextView.IBracketSearcher BracketSearcher { get { return TextView.DefaultBracketSearcher.DefaultInstance; } } public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition) + "." + method.Name); } public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition) + "." + property.Name); } public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition) + "." + field.Name); } public virtual void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(@event.DeclaringTypeDefinition) + "." + @event.Name); } public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(type)); } public virtual void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, nameSpace); } public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); var asm = assembly.GetMetadataFileOrNull(); if (asm == null) return null; if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { throw new NotSupportedException($"Language '{Name}' does not support exporting assemblies as projects!"); } var metadata = asm.Metadata; if (metadata.IsAssembly) { var name = metadata.GetAssemblyDefinition(); if ((name.Flags & System.Reflection.AssemblyFlags.WindowsRuntime) != 0) { WriteCommentLine(output, metadata.GetString(name.Name) + " [WinRT]"); } else if (metadata.TryGetFullAssemblyName(out string assemblyName)) { WriteCommentLine(output, assemblyName); } else { WriteCommentLine(output, "ERR: Could not read assembly name"); } } else { WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); } return null; } public virtual void WriteCommentLine(ITextOutput output, string comment) { output.WriteLine("// " + comment); } #region TypeToString /// /// Converts a type definition, reference or specification into a string. This method is used by tree nodes and search results. /// public virtual string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedTypeNames | ConversionFlags.UseFullyQualifiedEntityNames) { return new ILAmbience() { ConversionFlags = conversionFlags }.ConvertType(type); } #endregion /// /// Converts a member signature to a string. /// This is used for displaying the tooltip on a member reference. /// public virtual string GetTooltip(IEntity entity) { return GetDisplayName(entity, true, true, true); } /// /// Converts a member signature to a string. /// This is used for displaying the tooltip on a member reference. /// public virtual RichText GetRichTextTooltip(IEntity entity) { return GetTooltip(entity); } [Obsolete("Use EntityToString instead")] public virtual string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (field == null) throw new ArgumentNullException(nameof(field)); var flags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; if (includeDeclaringTypeName) flags |= ConversionFlags.ShowDeclaringType; if (includeNamespace) flags |= ConversionFlags.UseFullyQualifiedTypeNames; if (includeNamespaceOfDeclaringTypeName) flags |= ConversionFlags.UseFullyQualifiedEntityNames; return EntityToString(field, flags); } [Obsolete("Use EntityToString instead")] public virtual string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (property == null) throw new ArgumentNullException(nameof(property)); var flags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; if (includeDeclaringTypeName) flags |= ConversionFlags.ShowDeclaringType; if (includeNamespace) flags |= ConversionFlags.UseFullyQualifiedTypeNames; if (includeNamespaceOfDeclaringTypeName) flags |= ConversionFlags.UseFullyQualifiedEntityNames; return EntityToString(property, flags); } [Obsolete("Use EntityToString instead")] public virtual string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (method == null) throw new ArgumentNullException(nameof(method)); var flags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; if (includeDeclaringTypeName) flags |= ConversionFlags.ShowDeclaringType; if (includeNamespace) flags |= ConversionFlags.UseFullyQualifiedTypeNames; if (includeNamespaceOfDeclaringTypeName) flags |= ConversionFlags.UseFullyQualifiedEntityNames; return EntityToString(method, flags); } [Obsolete("Use EntityToString instead")] public virtual string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (@event == null) throw new ArgumentNullException(nameof(@event)); var flags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; if (includeDeclaringTypeName) flags |= ConversionFlags.ShowDeclaringType; if (includeNamespace) flags |= ConversionFlags.UseFullyQualifiedTypeNames; if (includeNamespaceOfDeclaringTypeName) flags |= ConversionFlags.UseFullyQualifiedEntityNames; return EntityToString(@event, flags); } public virtual string EntityToString(IEntity entity, ConversionFlags conversionFlags) { var ambience = new ILAmbience(); ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList | ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers | conversionFlags; return ambience.ConvertSymbol(entity); } protected string GetDisplayName(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { string entityName; if (entity is ITypeDefinition t && !t.MetadataToken.IsNil) { MetadataReader metadata = t.ParentModule.MetadataFile.Metadata; var typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)t.MetadataToken); entityName = ILAmbience.EscapeName(metadata.GetString(typeDef.Name)); } else { entityName = ILAmbience.EscapeName(entity.Name); } if (includeNamespace || includeDeclaringTypeName) { if (entity.DeclaringTypeDefinition != null) return TypeToString(entity.DeclaringTypeDefinition, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames) + "." + entityName; return ILAmbience.EscapeName(entity.Namespace) + "." + entityName; } else { return entityName; } } /// /// Used for WPF keyboard navigation. /// public override string ToString() { return Name; } public virtual bool ShowMember(IEntity member) { return true; } /// /// This should produce a string representation of the entity for search to match search strings against. /// public virtual string GetEntityName(MetadataFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: if (fullName) return ILAmbience.EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics)); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle); return ILAmbience.EscapeName(metadata.GetString(td.Name)); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); if (fullName) return ILAmbience.EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name)); return ILAmbience.EscapeName(metadata.GetString(fd.Name)); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); string methodName = metadata.GetString(md.Name); if (!omitGenerics) { int genericParamCount = md.GetGenericParameters().Count; if (genericParamCount > 0) methodName += "``" + genericParamCount; } if (fullName) return ILAmbience.EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName); return ILAmbience.EscapeName(methodName); case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) return ILAmbience.EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name)); return ILAmbience.EscapeName(metadata.GetString(ed.Name)); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) return ILAmbience.EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); return ILAmbience.EscapeName(metadata.GetString(pd.Name)); default: return null; } } public virtual CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member) { var declaringType = (TypeDefinitionHandle)member.GetDeclaringType(module.Metadata); if (declaringType.IsNil && member.Kind == HandleKind.TypeDefinition) { declaringType = (TypeDefinitionHandle)member; } return new CodeMappingInfo(module, declaringType); } static readonly IReadOnlyDictionary osMachineLookup = new Dictionary { { (Machine)0x4644, "MacOS" }, { (Machine)0x7b79, "Linux" }, { (Machine)0xadc4, "FreeBSD" }, { (Machine)0x1993, "NetBSD" }, { (Machine)0x1992, "Sun" }, }; public static string GetPlatformDisplayName(PEFile module) { var headers = module.Reader.PEHeaders; var architecture = headers.CoffHeader.Machine; var characteristics = headers.CoffHeader.Characteristics; var corflags = headers.CorHeader.Flags; var modifier = string.Empty; if (!Enum.IsDefined(architecture)) { foreach (var (osEnum, osText) in osMachineLookup) { var candidate = architecture ^ osEnum; if (Enum.IsDefined(candidate)) { modifier = osText + " "; architecture = candidate; break; } } } switch (architecture) { case Machine.I386: if ((corflags & CorFlags.Prefers32Bit) != 0) return modifier + "AnyCPU (32-bit preferred)"; if ((corflags & CorFlags.Requires32Bit) != 0) return modifier + "x86"; // According to ECMA-335, II.25.3.3.1 CorFlags.Requires32Bit and Characteristics.Bit32Machine must be in sync // for assemblies containing managed code. However, this is not true for C++/CLI assemblies. if ((corflags & CorFlags.ILOnly) == 0 && (characteristics & Characteristics.Bit32Machine) != 0) return modifier + "x86"; return modifier + "AnyCPU (64-bit preferred)"; case Machine.Amd64: return modifier + "x64"; case Machine.IA64: return modifier + "Itanium"; default: return architecture.ToString(); } } public static string GetRuntimeDisplayName(MetadataFile module) { return module.Metadata.MetadataVersion; } } } ================================================ FILE: ILSpy/Languages/LanguageService.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Composition; using System.Linq; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpyX; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy { [Export] [Shared] public class LanguageService : ObservableObjectBase { private readonly SettingsService settingsService; private readonly LanguageSettings languageSettings; public LanguageService(IEnumerable languages, SettingsService settingsService, DockWorkspace dockWorkspace) { this.settingsService = settingsService; languageSettings = settingsService.SessionSettings.LanguageSettings; var sortedLanguages = languages.ToList(); sortedLanguages.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); #if DEBUG sortedLanguages.AddRange(ILAstLanguage.GetDebugLanguages(dockWorkspace)); sortedLanguages.AddRange(CSharpLanguage.GetDebugLanguages()); #endif AllLanguages = sortedLanguages.AsReadOnly(); this.language = GetLanguage(languageSettings.LanguageId); this.languageVersion = Language.LanguageVersions.FirstOrDefault(v => v.Version == languageSettings.LanguageVersionId) ?? Language.LanguageVersions.LastOrDefault(); } /// /// A list of all languages. /// public ReadOnlyCollection AllLanguages { get; } /// /// Gets a language using its name. /// If the language is not found, C# is returned instead. /// public Language GetLanguage(string? name) { return AllLanguages.FirstOrDefault(l => l.Name == name) ?? AllLanguages.First(); } ILLanguage? ilLanguage; public ILLanguage ILLanguage => ilLanguage ??= (ILLanguage)GetLanguage("IL"); /// /// This dictionary is necessary to remember language versions across language changes. For example, /// the user first select C# 10, then switches to IL, then switches back to C#. After that we must be /// able to restore the original selection (i.e., C# 10). /// private readonly Dictionary languageVersionHistory = new(); Language language; /// /// Gets/Sets the current language. /// /// /// While this isn't related to filtering, having it as part of the FilterSettings /// makes it easy to pass it down into all tree nodes. /// public Language Language { get => language; set { if (language == value) return; if (language is { HasLanguageVersions: true }) { languageVersionHistory[language] = languageVersion; } language = value; OnPropertyChanged(); languageSettings.LanguageId = language.Name; if (language.HasLanguageVersions) { LanguageVersion = languageVersionHistory.TryGetValue(value, out var version) ? version : Language.LanguageVersions.Last(); } else { LanguageVersion = default; } } } LanguageVersion? languageVersion; /// /// Gets/Sets the current language version. /// /// /// While this isn't related to filtering, having it as part of the FilterSettings /// makes it easy to pass it down into all tree nodes. /// public LanguageVersion? LanguageVersion { get { return languageVersion; } set { if (languageVersion == value) return; languageVersion = value; OnPropertyChanged(); if (Language.HasLanguageVersions) { languageVersionHistory[Language] = languageVersion; } languageSettings.LanguageVersionId = languageVersion?.Version; } } } } ================================================ FILE: ILSpy/MainWindow.xaml ================================================ ================================================ FILE: ILSpy/MainWindow.xaml.cs ================================================ // 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 // 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.ComponentModel; using System.Composition; using System.Drawing; using System.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Threading; using Screen = System.Windows.Forms.Screen; namespace ICSharpCode.ILSpy { /// /// The main window of the application. /// [Export] [Shared] #pragma warning disable MEF003 // Main window is a singleton partial class MainWindow { private readonly SettingsService settingsService; public MainWindow(MainWindowViewModel mainWindowViewModel, SettingsService settingsService) { this.settingsService = settingsService; // Make sure Images are initialized on the UI thread. this.Icon = Images.ILSpyIcon; this.DataContext = mainWindowViewModel; InitializeComponent(); Dispatcher.BeginInvoke(DispatcherPriority.Background, () => { mainWindowViewModel.Workspace.InitializeLayout(); MessageBus.Send(this, new MainWindowLoadedEventArgs()); }); } void SetWindowBounds(Rect bounds) { this.Left = bounds.Left; this.Top = bounds.Top; this.Width = bounds.Width; this.Height = bounds.Height; } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var source = PresentationSource.FromVisual(this); var sessionSettings = settingsService.SessionSettings; // Validate and Set Window Bounds var windowBounds = Rect.Transform(sessionSettings.WindowBounds, source?.CompositionTarget?.TransformToDevice ?? Matrix.Identity); var boundsRect = new Rectangle((int)windowBounds.Left, (int)windowBounds.Top, (int)windowBounds.Width, (int)windowBounds.Height); bool areBoundsValid = Screen.AllScreens.Any(screen => Rectangle.Intersect(boundsRect, screen.WorkingArea) is { Width: > 10, Height: > 10 }); SetWindowBounds(areBoundsValid ? sessionSettings.WindowBounds : SessionSettings.DefaultWindowBounds); this.WindowState = sessionSettings.WindowState; } protected override void OnStateChanged(EventArgs e) { base.OnStateChanged(e); // store window state in settings only if it's not minimized if (this.WindowState != WindowState.Minimized) settingsService.SessionSettings.WindowState = this.WindowState; } protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); var snapshot = settingsService.CreateSnapshot(); var sessionSettings = snapshot.GetSettings(); MessageBus.Send(this, new ApplySessionSettingsEventArgs(sessionSettings)); sessionSettings.WindowBounds = this.RestoreBounds; // store window state in settings only if it's not minimized if (this.WindowState != WindowState.Minimized) sessionSettings.WindowState = this.WindowState; sessionSettings.DockLayout.Serialize(new(DockManager)); snapshot.Save(); } } } ================================================ FILE: ILSpy/MainWindowViewModel.cs ================================================ // Copyright (c) 2021 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.Composition; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpyX; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy { [Export] [Shared] public class MainWindowViewModel(SettingsService settingsService, LanguageService languageService, DockWorkspace dockWorkspace) : ObservableObjectBase { public DockWorkspace Workspace { get; } = dockWorkspace; public SessionSettings SessionSettings => settingsService.SessionSettings; public LanguageService LanguageService => languageService; public AssemblyListManager AssemblyListManager => settingsService.AssemblyListManager; } } ================================================ FILE: ILSpy/Metadata/CoffHeaderTreeNode.cs ================================================ // 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Data; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX.Extensions; using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy.Metadata { class CoffHeaderTreeNode : ILSpyTreeNode { private PEFile module; public CoffHeaderTreeNode(PEFile module) { this.module = module; } public override object Text => "COFF Header"; public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.Header; public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); dataGrid.RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector("Characteristics"); dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed; dataGrid.Columns.Clear(); dataGrid.AutoGenerateColumns = false; dataGrid.Columns.AddRange( new[] { new DataGridTextColumn { IsReadOnly = true, Header = "Member", Binding = new Binding("Member") }, new DataGridTextColumn { IsReadOnly = true, Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8" } }, new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") }, new DataGridTextColumn { IsReadOnly = true, Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance } }, new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") } } ); var headers = module.Reader.PEHeaders; var header = headers.CoffHeader; var linkerDateTime = DateTimeOffset.FromUnixTimeSeconds(unchecked((uint)header.TimeDateStamp)).DateTime; var entries = new List(); entries.Add(new Entry(headers.CoffHeaderStartOffset, (int)header.Machine, 2, "Machine", header.Machine.ToString())); entries.Add(new Entry(headers.CoffHeaderStartOffset + 2, (int)header.NumberOfSections, 2, "Number of Sections", "Number of sections; indicates size of the Section Table, which immediately follows the headers.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 4, header.TimeDateStamp, 4, "Time/Date Stamp", $"{linkerDateTime} (UTC) / {linkerDateTime.ToLocalTime()} - Time and date the file was created in seconds since January 1st 1970 00:00:00 or 0. Note that for deterministic builds this value is meaningless.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 8, header.PointerToSymbolTable, 4, "Pointer to Symbol Table", "Always 0 in .NET executables.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 12, header.NumberOfSymbols, 4, "Number of Symbols", "Always 0 in .NET executables.")); entries.Add(new Entry(headers.CoffHeaderStartOffset + 16, (int)header.SizeOfOptionalHeader, 2, "Optional Header Size", "Size of the optional header.")); Entry characteristics; entries.Add(characteristics = new Entry(headers.CoffHeaderStartOffset + 18, (int)header.Characteristics, 2, "Characteristics", "Flags indicating attributes of the file.", new[] { new BitEntry(((int)header.Characteristics & 0x0001) != 0, "<0001> Relocation info stripped from file"), new BitEntry(((int)header.Characteristics & 0x0002) != 0, "<0002> File is executable"), new BitEntry(((int)header.Characteristics & 0x0004) != 0, "<0004> Line numbers stripped from file"), new BitEntry(((int)header.Characteristics & 0x0008) != 0, "<0008> Local symbols stripped from file"), new BitEntry(((int)header.Characteristics & 0x0010) != 0, "<0010> Aggressively trim working set"), new BitEntry(((int)header.Characteristics & 0x0020) != 0, "<0020> Large address aware"), new BitEntry(((int)header.Characteristics & 0x0040) != 0, "<0040> Reserved"), new BitEntry(((int)header.Characteristics & 0x0080) != 0, "<0080> Bytes of machine words are reversed (Low)"), new BitEntry(((int)header.Characteristics & 0x0100) != 0, "<0100> 32-bit word machine"), new BitEntry(((int)header.Characteristics & 0x0200) != 0, "<0200> Debugging info stripped from file in .DBG file"), new BitEntry(((int)header.Characteristics & 0x0400) != 0, "<0400> If image is on removable media, copy and run from the swap file"), new BitEntry(((int)header.Characteristics & 0x0800) != 0, "<0800> If image is on Net, copy and run from the swap file"), new BitEntry(((int)header.Characteristics & 0x1000) != 0, "<1000> System"), new BitEntry(((int)header.Characteristics & 0x2000) != 0, "<2000> DLL"), new BitEntry(((int)header.Characteristics & 0x4000) != 0, "<4000> File should only be run on a UP machine"), new BitEntry(((int)header.Characteristics & 0x8000) != 0, "<8000> Bytes of machine words are reversed (High)"), })); dataGrid.ItemsSource = entries; dataGrid.SetDetailsVisibilityForItem(characteristics, Visibility.Visible); tabPage.Content = dataGrid; return true; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { } } public class CharacteristicsDataTemplateSelector : DataTemplateSelector { string detailsFieldName; public CharacteristicsDataTemplateSelector(string detailsFieldName) { this.detailsFieldName = detailsFieldName; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (((Entry)item).Member == detailsFieldName) return (DataTemplate)MetadataTableViews.Instance["HeaderFlagsDetailsDataGrid"]; return null; } } } ================================================ FILE: ILSpy/Metadata/CorTables/AssemblyRefTableTreeNode.cs ================================================ // 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 // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class AssemblyRefTableTreeNode : MetadataTableTreeNode { public AssemblyRefTableTreeNode(MetadataFile metadataFile) : base(TableIndex.AssemblyRef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.AssemblyReferences) { list.Add(new AssemblyRefEntry(metadataFile, row)); } return list; } internal struct AssemblyRefEntry { readonly MetadataFile metadataFile; readonly AssemblyReferenceHandle handle; readonly System.Reflection.Metadata.AssemblyReference assemblyRef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.AssemblyRef) + metadataFile.Metadata.GetTableRowSize(TableIndex.AssemblyRef) * (RID - 1); public Version Version => assemblyRef.Version; [ColumnInfo("X8", Kind = ColumnKind.Other)] public AssemblyFlags Flags => assemblyRef.Flags; public object FlagsTooltip => new FlagsTooltip((int)assemblyRef.Flags, null) { FlagGroup.CreateMultipleChoiceGroup(typeof(AssemblyFlags), selectedValue: (int)assemblyRef.Flags, includeAll: false) }; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int PublicKeyOrToken => MetadataTokens.GetHeapOffset(assemblyRef.PublicKeyOrToken); public string PublicKeyOrTokenTooltip { get { if (assemblyRef.PublicKeyOrToken.IsNil) return null; System.Collections.Immutable.ImmutableArray token = metadataFile.Metadata.GetBlobContent(assemblyRef.PublicKeyOrToken); return token.ToHexString(token.Length); } } public string NameTooltip => $"{MetadataTokens.GetHeapOffset(assemblyRef.Name):X} \"{Name}\""; public string Name => metadataFile.Metadata.GetString(assemblyRef.Name); public string CultureTooltip => $"{MetadataTokens.GetHeapOffset(assemblyRef.Culture):X} \"{Culture}\""; public string Culture => metadataFile.Metadata.GetString(assemblyRef.Culture); public AssemblyRefEntry(MetadataFile metadataFile, AssemblyReferenceHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.assemblyRef = metadataFile.Metadata.GetAssemblyReference(handle); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/AssemblyTableTreeNode.cs ================================================ // 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 // 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.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class AssemblyTableTreeNode : MetadataTableTreeNode { public AssemblyTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Assembly, metadataFile) { } protected override IReadOnlyList LoadTable() { if (metadataFile.Metadata.IsAssembly) { return [new AssemblyEntry(metadataFile.Metadata, metadataFile.MetadataOffset)]; } else { return []; } } internal readonly struct AssemblyEntry { readonly int metadataOffset; readonly MetadataReader metadata; readonly AssemblyDefinition assembly; public int RID => MetadataTokens.GetRowNumber(EntityHandle.AssemblyDefinition); public int Token => MetadataTokens.GetToken(EntityHandle.AssemblyDefinition); public int Offset => metadataOffset + metadata.GetTableMetadataOffset(TableIndex.Assembly) + metadata.GetTableRowSize(TableIndex.Assembly) * (RID - 1); [ColumnInfo("X4", Kind = ColumnKind.Other)] public AssemblyHashAlgorithm HashAlgorithm => assembly.HashAlgorithm; public object HashAlgorithmTooltip => new FlagsTooltip() { FlagGroup.CreateSingleChoiceGroup(typeof(AssemblyHashAlgorithm), selectedValue: (int)assembly.HashAlgorithm, defaultFlag: new Flag("None (0000)", 0, false), includeAny: false) }; [ColumnInfo("X4", Kind = ColumnKind.Other)] public AssemblyFlags Flags => assembly.Flags; public object FlagsTooltip => new FlagsTooltip() { FlagGroup.CreateMultipleChoiceGroup(typeof(AssemblyFlags), selectedValue: (int)assembly.Flags, includeAll: false) }; public Version Version => assembly.Version; public string NameTooltip => $"{MetadataTokens.GetHeapOffset(assembly.Name):X} \"{Name}\""; public string Name => metadata.GetString(assembly.Name); public string CultureTooltip => $"{MetadataTokens.GetHeapOffset(assembly.Culture):X} \"{Culture}\""; public string Culture => metadata.GetString(assembly.Culture); public AssemblyEntry(MetadataReader metadata, int metadataOffset) { this.metadata = metadata; this.metadataOffset = metadataOffset; this.assembly = metadata.GetAssemblyDefinition(); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs ================================================ // 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 // 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.Buffers.Binary; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ClassLayoutTableTreeNode : MetadataTableTreeNode { public ClassLayoutTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ClassLayout, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var length = metadataFile.Metadata.GetTableRowCount(TableIndex.ClassLayout); ReadOnlySpan ptr = metadataFile.Metadata.AsReadOnlySpan(); for (int rid = 1; rid <= length; rid++) { list.Add(new ClassLayoutEntry(metadataFile, ptr, rid)); } return list; } readonly struct ClassLayout { public readonly ushort PackingSize; public readonly EntityHandle Parent; public readonly uint ClassSize; public ClassLayout(ReadOnlySpan ptr, int typeDefSize) { PackingSize = BinaryPrimitives.ReadUInt16LittleEndian(ptr); ClassSize = BinaryPrimitives.ReadUInt32LittleEndian(ptr.Slice(2, 4)); Parent = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(6, typeDefSize))); } } internal struct ClassLayoutEntry { readonly MetadataFile metadataFile; readonly ClassLayout classLayout; public int RID { get; } public int Token => 0x0F000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(classLayout.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference("metadata", classLayout.Parent))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, classLayout.Parent); [ColumnInfo("X4", Kind = ColumnKind.Other)] public ushort PackingSize => classLayout.PackingSize; [ColumnInfo("X8", Kind = ColumnKind.Other)] public uint ClassSize => classLayout.ClassSize; public ClassLayoutEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row) { this.metadataFile = metadataFile; this.RID = row; var metadata = metadataFile.Metadata; var rowOffset = metadata.GetTableMetadataOffset(TableIndex.ClassLayout) + metadata.GetTableRowSize(TableIndex.ClassLayout) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.classLayout = new ClassLayout(ptr.Slice(rowOffset), metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4); this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ConstantTableTreeNode : MetadataTableTreeNode { public ConstantTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Constant, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.Constant); row++) { list.Add(new ConstantEntry(metadataFile, MetadataTokens.ConstantHandle(row))); } return list; } internal struct ConstantEntry { readonly MetadataFile metadataFile; readonly EntityHandle handle; readonly Constant constant; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Constant) + metadataFile.Metadata.GetTableRowSize(TableIndex.Constant) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public ConstantTypeCode Type => constant.TypeCode; public string TypeTooltip => constant.TypeCode.ToString(); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(constant.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, constant.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, constant.Parent); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(constant.Value); public string ValueTooltip { get { return null; } } public ConstantEntry(MetadataFile metadataFile, ConstantHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.constant = metadataFile.Metadata.GetConstant(handle); this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class CustomAttributeTableTreeNode : MetadataTableTreeNode { public CustomAttributeTableTreeNode(MetadataFile metadataFile) : base(TableIndex.CustomAttribute, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.CustomAttributes) { list.Add(new CustomAttributeEntry(metadataFile, row)); } return list; } internal struct CustomAttributeEntry { readonly MetadataFile metadataFile; readonly CustomAttributeHandle handle; readonly CustomAttribute customAttr; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.CustomAttribute) + metadataFile.Metadata.GetTableRowSize(TableIndex.CustomAttribute) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(customAttr.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, customAttr.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, customAttr.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Constructor => MetadataTokens.GetToken(customAttr.Constructor); public void OnConstructorClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, customAttr.Constructor, protocol: "metadata"))); } string constructorTooltip; public string ConstructorTooltip => GenerateTooltip(ref constructorTooltip, metadataFile, customAttr.Constructor); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(customAttr.Value); public string ValueTooltip { get { return null; } } public CustomAttributeEntry(MetadataFile metadataFile, CustomAttributeHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.customAttr = metadataFile.Metadata.GetCustomAttribute(handle); this.parentTooltip = null; this.constructorTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class DeclSecurityTableTreeNode : MetadataTableTreeNode { public DeclSecurityTableTreeNode(MetadataFile metadataFile) : base(TableIndex.DeclSecurity, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.DeclarativeSecurityAttributes) { list.Add(new DeclSecurityEntry(metadataFile, row)); } return list; } internal struct DeclSecurityEntry { readonly MetadataFile metadataFile; readonly DeclarativeSecurityAttributeHandle handle; readonly DeclarativeSecurityAttribute declSecAttr; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.DeclSecurity) + metadataFile.Metadata.GetTableRowSize(TableIndex.DeclSecurity) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(declSecAttr.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, declSecAttr.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, declSecAttr.Parent); [ColumnInfo("X8", Kind = ColumnKind.Other)] public DeclarativeSecurityAction Action => declSecAttr.Action; public string ActionTooltip { get { return declSecAttr.Action.ToString(); } } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int PermissionSet => MetadataTokens.GetHeapOffset(declSecAttr.PermissionSet); public string PermissionSetTooltip { get { return null; } } public DeclSecurityEntry(MetadataFile metadataFile, DeclarativeSecurityAttributeHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.declSecAttr = metadataFile.Metadata.GetDeclarativeSecurityAttribute(handle); this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class EventMapTableTreeNode : MetadataTableTreeNode { public EventMapTableTreeNode(MetadataFile metadataFile) : base(TableIndex.EventMap, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.EventMap); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; int eventDefSize = metadata.GetTableRowCount(TableIndex.Event) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new EventMapEntry(metadataFile, ptr, rid, typeDefSize, eventDefSize)); } return list; } readonly struct EventMap { public readonly TypeDefinitionHandle Parent; public readonly EventDefinitionHandle EventList; public EventMap(ReadOnlySpan ptr, int typeDefSize, int eventDefSize) { Parent = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(0, typeDefSize))); EventList = MetadataTokens.EventDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(typeDefSize, eventDefSize))); } } internal struct EventMapEntry { readonly MetadataFile metadataFile; readonly EventMap eventMap; public int RID { get; } public int Token => 0x12000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(eventMap.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventMap.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, eventMap.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EventList => MetadataTokens.GetToken(eventMap.EventList); public void OnEventListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventMap.EventList, protocol: "metadata"))); } string eventListTooltip; public string EventListTooltip => GenerateTooltip(ref eventListTooltip, metadataFile, eventMap.EventList); public EventMapEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row, int typeDefSize, int eventDefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.EventMap) + metadataFile.Metadata.GetTableRowSize(TableIndex.EventMap) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.eventMap = new EventMap(ptr.Slice(rowOffset), typeDefSize, eventDefSize); this.parentTooltip = null; this.eventListTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/EventTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal class EventTableTreeNode : MetadataTableTreeNode { public EventTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Event, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.EventDefinitions) { list.Add(new EventDefEntry(metadataFile, row)); } return list; } internal struct EventDefEntry : IMemberTreeNode { readonly MetadataFile metadataFile; readonly EventDefinitionHandle handle; readonly EventDefinition eventDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Event) + metadataFile.Metadata.GetTableRowSize(TableIndex.Event) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public EventAttributes Attributes => eventDef.Attributes; public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateMultipleChoiceGroup(typeof(EventAttributes), selectedValue: (int)eventDef.Attributes, includeAll: false), }; public string NameTooltip => $"{MetadataTokens.GetHeapOffset(eventDef.Name):X} \"{Name}\""; public string Name => metadataFile.Metadata.GetString(eventDef.Name); IEntity IMemberTreeNode.Member { get { return ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)?.MainModule)?.GetDefinition(handle); } } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(eventDef.Type); public void OnTypeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventDef.Type, protocol: "metadata"))); } string typeTooltip; public string TypeTooltip => GenerateTooltip(ref typeTooltip, metadataFile, eventDef.Type); public EventDefEntry(MetadataFile metadataFile, EventDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.eventDef = metadataFile.Metadata.GetEventDefinition(handle); this.typeTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ExportedTypeTableTreeNode : MetadataTableTreeNode { public ExportedTypeTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ExportedType, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; foreach (var row in metadata.ExportedTypes) { list.Add(new ExportedTypeEntry(metadataFile, row, metadata.GetExportedType(row))); } return list; } internal struct ExportedTypeEntry { readonly MetadataFile metadataFile; readonly ExportedTypeHandle handle; readonly ExportedType type; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ExportedType) + metadataFile.Metadata.GetTableRowSize(TableIndex.ExportedType) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public TypeAttributes Attributes => type.Attributes; const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Visibility: ", (int)TypeAttributes.VisibilityMask, (int)(type.Attributes & TypeAttributes.VisibilityMask), new Flag("NotPublic (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class layout: ", (int)TypeAttributes.LayoutMask, (int)(type.Attributes & TypeAttributes.LayoutMask), new Flag("AutoLayout (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class semantics: ", (int)TypeAttributes.ClassSemanticsMask, (int)(type.Attributes & TypeAttributes.ClassSemanticsMask), new Flag("Class (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "String format: ", (int)TypeAttributes.StringFormatMask, (int)(type.Attributes & TypeAttributes.StringFormatMask), new Flag("AnsiClass (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Custom format: ", (int)TypeAttributes.CustomFormatMask, (int)(type.Attributes & TypeAttributes.CustomFormatMask), new Flag("Value0 (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(TypeAttributes), "Flags:", (int)otherFlagsMask, (int)(type.Attributes & otherFlagsMask), includeAll: false), }; public int TypeDefId => type.GetTypeDefinitionId(); public string TypeNameTooltip => $"{MetadataTokens.GetHeapOffset(type.Name):X} \"{TypeName}\""; public string TypeName => metadataFile.Metadata.GetString(type.Name); public string TypeNamespaceTooltip => $"{MetadataTokens.GetHeapOffset(type.Namespace):X} \"{TypeNamespace}\""; public string TypeNamespace => metadataFile.Metadata.GetString(type.Namespace); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Implementation => MetadataTokens.GetToken(type.Implementation); public void OnImplementationClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, type.Implementation, protocol: "metadata"))); } string implementationTooltip; public string ImplementationTooltip => GenerateTooltip(ref implementationTooltip, metadataFile, type.Implementation); public ExportedTypeEntry(MetadataFile metadataFile, ExportedTypeHandle handle, ExportedType type) { this.metadataFile = metadataFile; this.handle = handle; this.type = type; this.implementationTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs ================================================ // 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 // 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.Buffers.Binary; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class FieldLayoutTableTreeNode : MetadataTableTreeNode { public FieldLayoutTableTreeNode(MetadataFile metadataFile) : base(TableIndex.FieldLayout, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.FieldLayout); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new FieldLayoutEntry(metadataFile, ptr, rid, fieldDefSize)); } return list; } readonly struct FieldLayout { public readonly int Offset; public readonly FieldDefinitionHandle Field; public FieldLayout(ReadOnlySpan ptr, int fieldDefSize) { Offset = BinaryPrimitives.ReadInt32LittleEndian(ptr); Field = MetadataTokens.FieldDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(4, fieldDefSize))); } } internal struct FieldLayoutEntry { readonly MetadataFile metadataFile; readonly FieldLayout fieldLayout; public int RID { get; } public int Token => 0x10000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Field => MetadataTokens.GetToken(fieldLayout.Field); public void OnFieldClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldLayout.Field, protocol: "metadata"))); } string fieldTooltip; public string FieldTooltip => GenerateTooltip(ref fieldTooltip, metadataFile, fieldLayout.Field); [ColumnInfo("X8", Kind = ColumnKind.Other)] public int FieldOffset => fieldLayout.Offset; public FieldLayoutEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row, int fieldDefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.FieldLayout) + metadataFile.Metadata.GetTableRowSize(TableIndex.FieldLayout) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.fieldLayout = new FieldLayout(ptr.Slice(rowOffset), fieldDefSize); this.fieldTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class FieldMarshalTableTreeNode : MetadataTableTreeNode { public FieldMarshalTableTreeNode(MetadataFile metadataFile) : base(TableIndex.FieldMarshal, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.FieldMarshal); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int hasFieldMarshalRefSize = metadata.ComputeCodedTokenSize(32768, TableMask.Field | TableMask.Param); int blobHeapSize = metadata.GetHeapSize(HeapIndex.Blob) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new FieldMarshalEntry(metadataFile, ptr, rid, blobHeapSize, hasFieldMarshalRefSize)); } return list; } readonly struct FieldMarshal { public readonly BlobHandle NativeType; public readonly EntityHandle Parent; public FieldMarshal(ReadOnlySpan ptr, int blobHeapSize, int hasFieldMarshalRefSize) { Parent = Helpers.FromHasFieldMarshalTag((uint)Helpers.GetValueLittleEndian(ptr, hasFieldMarshalRefSize)); NativeType = MetadataTokens.BlobHandle(Helpers.GetValueLittleEndian(ptr.Slice(hasFieldMarshalRefSize, blobHeapSize))); } } internal struct FieldMarshalEntry { readonly MetadataFile metadataFile; readonly FieldMarshal fieldMarshal; public int RID { get; } public int Token => 0x0D000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(fieldMarshal.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldMarshal.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, fieldMarshal.Parent); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int NativeType => MetadataTokens.GetHeapOffset(fieldMarshal.NativeType); public FieldMarshalEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row, int blobHeapSize, int hasFieldMarshalRefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.FieldMarshal) + metadataFile.Metadata.GetTableRowSize(TableIndex.FieldMarshal) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.fieldMarshal = new FieldMarshal(ptr.Slice(rowOffset), blobHeapSize, hasFieldMarshalRefSize); this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs ================================================ // 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 // 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.Buffers.Binary; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class FieldRVATableTreeNode : MetadataTableTreeNode { public FieldRVATableTreeNode(MetadataFile metadataFile) : base(TableIndex.FieldRva, metadataFile) { } public override object Text => $"1D FieldRVA ({metadataFile.Metadata.GetTableRowCount(TableIndex.FieldRva)})"; protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.FieldRva); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int metadataOffset = metadataFile.MetadataOffset; int fieldDefSize = metadata.GetTableRowCount(TableIndex.Field) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new FieldRVAEntry(metadataFile, metadataOffset, ptr, rid, fieldDefSize)); } return list; } readonly struct FieldRVA { public readonly int Offset; public readonly FieldDefinitionHandle Field; public FieldRVA(ReadOnlySpan ptr, int fieldDefSize) { Offset = BinaryPrimitives.ReadInt32LittleEndian(ptr); Field = MetadataTokens.FieldDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(4, fieldDefSize))); } } internal struct FieldRVAEntry { readonly MetadataFile metadataFile; readonly FieldRVA fieldRVA; public int RID { get; } public int Token => 0x1D000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Field => MetadataTokens.GetToken(fieldRVA.Field); public void OnFieldClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldRVA.Field, protocol: "metadata"))); } string fieldTooltip; public string FieldTooltip => GenerateTooltip(ref fieldTooltip, metadataFile, fieldRVA.Field); [ColumnInfo("X8", Kind = ColumnKind.Other)] public int FieldOffset => fieldRVA.Offset; public FieldRVAEntry(MetadataFile metadataFile, int metadataOffset, ReadOnlySpan ptr, int row, int fieldDefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.FieldRva) + metadataFile.Metadata.GetTableRowSize(TableIndex.FieldRva) * (row - 1); this.Offset = metadataOffset + rowOffset; this.fieldRVA = new FieldRVA(ptr.Slice(rowOffset), fieldDefSize); this.fieldTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/FieldTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal class FieldTableTreeNode : MetadataTableTreeNode { public FieldTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Field, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.FieldDefinitions) { list.Add(new FieldDefEntry(metadataFile, row)); } return list; } internal struct FieldDefEntry : IMemberTreeNode { readonly MetadataFile metadataFile; readonly FieldDefinitionHandle handle; readonly FieldDefinition fieldDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Field) + metadataFile.Metadata.GetTableRowSize(TableIndex.Field) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public FieldAttributes Attributes => fieldDef.Attributes; const FieldAttributes otherFlagsMask = ~(FieldAttributes.FieldAccessMask); public object AttributesTooltip => new FlagsTooltip() { FlagGroup.CreateSingleChoiceGroup(typeof(FieldAttributes), "Field access: ", (int)FieldAttributes.FieldAccessMask, (int)(fieldDef.Attributes & FieldAttributes.FieldAccessMask), new Flag("CompilerControlled (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(FieldAttributes), "Flags:", (int)otherFlagsMask, (int)(fieldDef.Attributes & otherFlagsMask), includeAll: false), }; public string Name => metadataFile.Metadata.GetString(fieldDef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(fieldDef.Name):X} \"{Name}\""; IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)?.MainModule)?.GetDefinition(handle); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); string signatureTooltip; public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, metadataFile, handle); public FieldDefEntry(MetadataFile metadataFile, FieldDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.fieldDef = metadataFile.Metadata.GetFieldDefinition(handle); this.signatureTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/FileTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class FileTableTreeNode : MetadataTableTreeNode { public FileTableTreeNode(MetadataFile metadataFile) : base(TableIndex.File, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.AssemblyFiles) { list.Add(new FileEntry(metadataFile, row)); } return list; } internal struct FileEntry { readonly MetadataFile metadataFile; readonly AssemblyFileHandle handle; readonly AssemblyFile assemblyFile; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.File) + metadataFile.Metadata.GetTableRowSize(TableIndex.File) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public int Attributes => assemblyFile.ContainsMetadata ? 1 : 0; public string AttributesTooltip => assemblyFile.ContainsMetadata ? "ContainsMetaData" : "ContainsNoMetaData"; public string Name => metadataFile.Metadata.GetString(assemblyFile.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(assemblyFile.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int HashValue => MetadataTokens.GetHeapOffset(assemblyFile.HashValue); public string HashValueTooltip { get { if (assemblyFile.HashValue.IsNil) return null; System.Collections.Immutable.ImmutableArray token = metadataFile.Metadata.GetBlobContent(assemblyFile.HashValue); return token.ToHexString(token.Length); } } public FileEntry(MetadataFile metadataFile, AssemblyFileHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.assemblyFile = metadataFile.Metadata.GetAssemblyFile(handle); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class GenericParamConstraintTableTreeNode : MetadataTableTreeNode { public GenericParamConstraintTableTreeNode(MetadataFile metadataFile) : base(TableIndex.GenericParamConstraint, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; for (int row = 1; row <= metadata.GetTableRowCount(TableIndex.GenericParamConstraint); row++) { list.Add(new GenericParamConstraintEntry(metadataFile, MetadataTokens.GenericParameterConstraintHandle(row))); } return list; } internal struct GenericParamConstraintEntry { readonly MetadataFile metadataFile; readonly GenericParameterConstraintHandle handle; readonly GenericParameterConstraint genericParamConstraint; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.GenericParamConstraint) + metadataFile.Metadata.GetTableRowSize(TableIndex.GenericParamConstraint) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Owner => MetadataTokens.GetToken(genericParamConstraint.Parameter); public void OnOwnerClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParamConstraint.Parameter, protocol: "metadata"))); } string ownerTooltip; public string OwnerTooltip { get { if (ownerTooltip == null) { ITextOutput output = new PlainTextOutput(); var p = metadataFile.Metadata.GetGenericParameter(genericParamConstraint.Parameter); output.Write("parameter " + p.Index + (p.Name.IsNil ? "" : " (" + metadataFile.Metadata.GetString(p.Name) + ")") + " of "); p.Parent.WriteTo(metadataFile, output, default); ownerTooltip = output.ToString(); } return ownerTooltip; } } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(genericParamConstraint.Type); public void OnTypeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParamConstraint.Type, protocol: "metadata"))); } string typeTooltip; public string TypeTooltip => GenerateTooltip(ref typeTooltip, metadataFile, genericParamConstraint.Type); public GenericParamConstraintEntry(MetadataFile metadataFile, GenericParameterConstraintHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.genericParamConstraint = metadataFile.Metadata.GetGenericParameterConstraint(handle); this.ownerTooltip = null; this.typeTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class GenericParamTableTreeNode : MetadataTableTreeNode { public GenericParamTableTreeNode(MetadataFile metadataFile) : base(TableIndex.GenericParam, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.GenericParam); row++) { list.Add(new GenericParamEntry(metadataFile, MetadataTokens.GenericParameterHandle(row))); } return list; } internal struct GenericParamEntry { readonly MetadataFile metadataFile; readonly GenericParameterHandle handle; readonly GenericParameter genericParam; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.GenericParam) + metadataFile.Metadata.GetTableRowSize(TableIndex.GenericParam) * (RID - 1); public int Number => genericParam.Index; [ColumnInfo("X8", Kind = ColumnKind.Other)] public GenericParameterAttributes Attributes => genericParam.Attributes; public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(GenericParameterAttributes), "Variance: ", (int)GenericParameterAttributes.VarianceMask, (int)(genericParam.Attributes & GenericParameterAttributes.VarianceMask), new Flag("None (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(GenericParameterAttributes), "Special Constraint: ", (int)GenericParameterAttributes.SpecialConstraintMask, (int)(genericParam.Attributes & GenericParameterAttributes.SpecialConstraintMask), includeAll: false), }; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Owner => MetadataTokens.GetToken(genericParam.Parent); public void OnOwnerClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParam.Parent, protocol: "metadata"))); } string ownerTooltip; public string OwnerTooltip => GenerateTooltip(ref ownerTooltip, metadataFile, genericParam.Parent); public string Name => metadataFile.Metadata.GetString(genericParam.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(genericParam.Name):X} \"{Name}\""; public GenericParamEntry(MetadataFile metadataFile, GenericParameterHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.genericParam = metadataFile.Metadata.GetGenericParameter(handle); this.ownerTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs ================================================ // 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 // 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.Buffers.Binary; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using Mono.Cecil; namespace ICSharpCode.ILSpy.Metadata { class ImplMapTableTreeNode : MetadataTableTreeNode { public ImplMapTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ImplMap, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.ImplMap); var span = metadata.AsReadOnlySpan(); int moduleRefSize = metadata.GetTableRowCount(TableIndex.ModuleRef) < ushort.MaxValue ? 2 : 4; int memberForwardedTagRefSize = metadata.ComputeCodedTokenSize(32768, TableMask.MethodDef | TableMask.Field); int stringHandleSize = metadata.GetHeapSize(HeapIndex.String) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new ImplMapEntry(metadataFile, span, rid, moduleRefSize, memberForwardedTagRefSize, stringHandleSize)); } return list; } readonly struct ImplMap { public readonly PInvokeAttributes MappingFlags; public readonly EntityHandle MemberForwarded; public readonly StringHandle ImportName; public readonly ModuleReferenceHandle ImportScope; public ImplMap(ReadOnlySpan span, int moduleRefSize, int memberForwardedTagRefSize, int stringHandleSize) { MappingFlags = (PInvokeAttributes)BinaryPrimitives.ReadUInt16LittleEndian(span); MemberForwarded = Helpers.FromMemberForwardedTag((uint)Helpers.GetValueLittleEndian(span.Slice(2, memberForwardedTagRefSize))); ImportName = MetadataTokens.StringHandle(Helpers.GetValueLittleEndian(span.Slice(2 + memberForwardedTagRefSize, stringHandleSize))); ImportScope = MetadataTokens.ModuleReferenceHandle(Helpers.GetValueLittleEndian(span.Slice(2 + memberForwardedTagRefSize + stringHandleSize, moduleRefSize))); } } internal struct ImplMapEntry { readonly MetadataFile metadataFile; readonly ImplMap implMap; public int RID { get; } public int Token => 0x1C000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Other)] public PInvokeAttributes MappingFlags => implMap.MappingFlags; const PInvokeAttributes otherFlagsMask = ~(PInvokeAttributes.CallConvMask | PInvokeAttributes.CharSetMask); public object MappingFlagsTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(PInvokeAttributes), "Character set: ", (int)PInvokeAttributes.CharSetMask, (int)(implMap.MappingFlags & PInvokeAttributes.CharSetMask), new Flag("CharSetNotSpec (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(PInvokeAttributes), "Calling convention: ", (int)PInvokeAttributes.CallConvMask, (int)(implMap.MappingFlags & PInvokeAttributes.CallConvMask), new Flag("Invalid (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(PInvokeAttributes), "Flags:", (int)otherFlagsMask, (int)(implMap.MappingFlags & otherFlagsMask), includeAll: false), }; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MemberForwarded => MetadataTokens.GetToken(implMap.MemberForwarded); public void OnMemberForwardedClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, implMap.MemberForwarded, protocol: "metadata"))); } string memberForwardedTooltip; public string MemberForwardedTooltip => GenerateTooltip(ref memberForwardedTooltip, metadataFile, implMap.MemberForwarded); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(implMap.ImportScope); public void OnImportScopeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, implMap.ImportScope, protocol: "metadata"))); } string importScopeTooltip; public string ImportScopeTooltip => GenerateTooltip(ref importScopeTooltip, metadataFile, implMap.ImportScope); public string ImportName => metadataFile.Metadata.GetString(implMap.ImportName); public string ImportNameTooltip => $"{MetadataTokens.GetHeapOffset(implMap.ImportName):X} \"{ImportName}\""; public ImplMapEntry(MetadataFile metadataFile, ReadOnlySpan span, int row, int moduleRefSize, int memberForwardedTagRefSize, int stringHandleSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ImplMap) + metadataFile.Metadata.GetTableRowSize(TableIndex.ImplMap) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.implMap = new ImplMap(span.Slice(rowOffset), moduleRefSize, memberForwardedTagRefSize, stringHandleSize); this.importScopeTooltip = null; this.memberForwardedTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class InterfaceImplTableTreeNode : MetadataTableTreeNode { public InterfaceImplTableTreeNode(MetadataFile metadataFile) : base(TableIndex.InterfaceImpl, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var length = metadataFile.Metadata.GetTableRowCount(TableIndex.InterfaceImpl); ReadOnlySpan ptr = metadataFile.Metadata.AsReadOnlySpan(); for (int rid = 1; rid <= length; rid++) { list.Add(new InterfaceImplEntry(metadataFile, ptr, rid)); } return list; } readonly struct InterfaceImpl { public readonly EntityHandle Class; public readonly EntityHandle Interface; public InterfaceImpl(ReadOnlySpan ptr, int classSize, int interfaceSize) { Class = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr, classSize)); Interface = Helpers.FromTypeDefOrRefTag((uint)Helpers.GetValueLittleEndian(ptr.Slice(classSize, interfaceSize))); } } internal struct InterfaceImplEntry { readonly MetadataFile metadataFile; readonly InterfaceImpl interfaceImpl; public int RID { get; } public int Token => 0x09000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Class => MetadataTokens.GetToken(interfaceImpl.Class); public void OnClassClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, interfaceImpl.Class, protocol: "metadata"))); } string classTooltip; public string ClassTooltip => GenerateTooltip(ref classTooltip, metadataFile, interfaceImpl.Class); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Interface => MetadataTokens.GetToken(interfaceImpl.Interface); public void OnInterfaceClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, interfaceImpl.Interface, protocol: "metadata"))); } string interfaceTooltip; public string InterfaceTooltip => GenerateTooltip(ref interfaceTooltip, metadataFile, interfaceImpl.Interface); public InterfaceImplEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.InterfaceImpl) + metadataFile.Metadata.GetTableRowSize(TableIndex.InterfaceImpl) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.interfaceImpl = new InterfaceImpl(ptr.Slice(rowOffset), metadataFile.Metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4, metadataFile.Metadata.ComputeCodedTokenSize(16384, TableMask.TypeDef | TableMask.TypeRef | TableMask.TypeSpec)); this.interfaceTooltip = null; this.classTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class ManifestResourceTableTreeNode : MetadataTableTreeNode { public ManifestResourceTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ManifestResource, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; foreach (var row in metadata.ManifestResources) { list.Add(new ManifestResourceEntry(metadataFile, row)); } return list; } internal struct ManifestResourceEntry { readonly MetadataFile metadataFile; readonly ManifestResourceHandle handle; readonly ManifestResource manifestResource; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ManifestResource) + metadataFile.Metadata.GetTableRowSize(TableIndex.ManifestResource) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public ManifestResourceAttributes Attributes => manifestResource.Attributes; public object AttributesTooltip => manifestResource.Attributes.ToString(); public string Name => metadataFile.Metadata.GetString(manifestResource.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(manifestResource.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Implementation => MetadataTokens.GetToken(manifestResource.Implementation); public void OnImplementationClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, manifestResource.Implementation, protocol: "metadata"))); } string implementationTooltip; public string ImplementationTooltip => GenerateTooltip(ref implementationTooltip, metadataFile, manifestResource.Implementation); public ManifestResourceEntry(MetadataFile metadataFile, ManifestResourceHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.manifestResource = metadataFile.Metadata.GetManifestResource(handle); this.implementationTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class MemberRefTableTreeNode : MetadataTableTreeNode { public MemberRefTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MemberRef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.MemberReferences) { list.Add(new MemberRefEntry(metadataFile, row)); } return list; } internal struct MemberRefEntry { readonly MetadataFile metadataFile; readonly MemberReferenceHandle handle; readonly MemberReference memberRef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MemberRef) + metadataFile.Metadata.GetTableRowSize(TableIndex.MemberRef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(memberRef.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, memberRef.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, memberRef.Parent); public string Name => metadataFile.Metadata.GetString(memberRef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(memberRef.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(memberRef.Signature); string signatureTooltip; public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, metadataFile, handle); public MemberRefEntry(MetadataFile metadataFile, MemberReferenceHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.memberRef = metadataFile.Metadata.GetMemberReference(handle); this.signatureTooltip = null; this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class MethodImplTableTreeNode : MetadataTableTreeNode { public MethodImplTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MethodImpl, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.MethodImpl); row++) { list.Add(new MethodImplEntry(metadataFile, MetadataTokens.MethodImplementationHandle(row))); } return list; } internal struct MethodImplEntry { readonly MetadataFile metadataFile; readonly MethodImplementationHandle handle; readonly MethodImplementation methodImpl; public int RID => MetadataTokens.GetToken(handle) & 0xFFFFFF; public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadataFile.Metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodDeclaration => MetadataTokens.GetToken(methodImpl.MethodDeclaration); public void OnMethodDeclarationClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.MethodDeclaration, protocol: "metadata"))); } string methodDeclarationTooltip; public string MethodDeclarationTooltip => GenerateTooltip(ref methodDeclarationTooltip, metadataFile, methodImpl.MethodDeclaration); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodBody => MetadataTokens.GetToken(methodImpl.MethodBody); public void OnMethodBodyClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.MethodBody, protocol: "metadata"))); } string methodBodyTooltip; public string MethodBodyTooltip => GenerateTooltip(ref methodBodyTooltip, metadataFile, methodImpl.MethodBody); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Type => MetadataTokens.GetToken(methodImpl.Type); public void OnTypeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.Type, protocol: "metadata"))); } string typeTooltip; public string TypeTooltip => GenerateTooltip(ref typeTooltip, metadataFile, methodImpl.Type); public MethodImplEntry(MetadataFile metadataFile, MethodImplementationHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.methodImpl = metadataFile.Metadata.GetMethodImplementation(handle); this.typeTooltip = null; this.methodBodyTooltip = null; this.methodDeclarationTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class MethodSemanticsTableTreeNode : MetadataTableTreeNode { public MethodSemanticsTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MethodSemantics, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.GetMethodSemantics()) { list.Add(new MethodSemanticsEntry(metadataFile, row.Handle, row.Semantics, row.Method, row.Association)); } return list; } internal struct MethodSemanticsEntry { readonly MetadataFile metadataFile; readonly Handle handle; readonly MethodSemanticsAttributes semantics; readonly MethodDefinitionHandle method; readonly EntityHandle association; public int RID => MetadataTokens.GetToken(handle) & 0xFFFFFF; public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadataFile.Metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodSemanticsAttributes Semantics => semantics; public string SemanticsTooltip => semantics.ToString(); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(method); public void OnMethodClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, method, protocol: "metadata"))); } string methodTooltip; public string MethodTooltip => GenerateTooltip(ref methodTooltip, metadataFile, method); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Association => MetadataTokens.GetToken(association); public void OnAssociationClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, association, protocol: "metadata"))); } string associationTooltip; public string AssociationTooltip => GenerateTooltip(ref associationTooltip, metadataFile, association); public MethodSemanticsEntry(MetadataFile metadataFile, Handle handle, MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) { this.metadataFile = metadataFile; this.handle = handle; this.semantics = semantics; this.method = method; this.association = association; this.methodTooltip = null; this.associationTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class MethodSpecTableTreeNode : MetadataTableTreeNode { public MethodSpecTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MethodSpec, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.GetMethodSpecifications()) { list.Add(new MethodSpecEntry(metadataFile, row)); } return list; } internal struct MethodSpecEntry { readonly MetadataFile metadataFile; readonly MethodSpecificationHandle handle; readonly MethodSpecification methodSpec; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodSpec) + metadataFile.Metadata.GetTableRowSize(TableIndex.MethodSpec) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(methodSpec.Method); public void OnMethodClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodSpec.Method, protocol: "metadata"))); } string methodTooltip; public string MethodTooltip => GenerateTooltip(ref methodTooltip, metadataFile, methodSpec.Method); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(methodSpec.Signature); public string SignatureTooltip { get { ITextOutput output = new PlainTextOutput(); var signature = methodSpec.DecodeSignature(new DisassemblerSignatureTypeProvider(metadataFile, output), default); bool first = true; foreach (var type in signature) { if (first) first = false; else output.Write(", "); type(ILNameSyntax.TypeName); } return output.ToString(); } } public MethodSpecEntry(MetadataFile metadataFile, MethodSpecificationHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.methodSpec = metadataFile.Metadata.GetMethodSpecification(handle); this.methodTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/MethodTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal class MethodTableTreeNode : MetadataTableTreeNode { public MethodTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MethodDef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.MethodDefinitions) { list.Add(new MethodDefEntry(metadataFile, row)); } return list; } internal struct MethodDefEntry : IMemberTreeNode { readonly MetadataFile metadataFile; readonly MethodDefinitionHandle handle; readonly MethodDefinition methodDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodDef) + metadataFile.Metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodAttributes Attributes => methodDef.Attributes; const MethodAttributes otherFlagsMask = ~(MethodAttributes.MemberAccessMask | MethodAttributes.VtableLayoutMask); public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(MethodAttributes), "Member access: ", (int)MethodAttributes.MemberAccessMask, (int)(methodDef.Attributes & MethodAttributes.MemberAccessMask), new Flag("CompilerControlled (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(MethodAttributes), "Vtable layout: ", (int)MethodAttributes.VtableLayoutMask, (int)(methodDef.Attributes & MethodAttributes.VtableLayoutMask), new Flag("ReuseSlot (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(MethodAttributes), "Flags:", (int)otherFlagsMask, (int)(methodDef.Attributes & otherFlagsMask), includeAll: false), }; [ColumnInfo("X8", Kind = ColumnKind.Other)] public MethodImplAttributes ImplAttributes => methodDef.ImplAttributes; public object ImplAttributesTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(MethodImplAttributes), "Code type: ", (int)MethodImplAttributes.CodeTypeMask, (int)(methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask), new Flag("IL (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(MethodImplAttributes), "Managed type: ", (int)MethodImplAttributes.ManagedMask, (int)(methodDef.ImplAttributes & MethodImplAttributes.ManagedMask), new Flag("Managed (0000)", 0, false), includeAny: false), }; public int RVA => methodDef.RelativeVirtualAddress; public string Name => metadataFile.Metadata.GetString(methodDef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(methodDef.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(methodDef.Signature); string signatureTooltip; public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, metadataFile, handle); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ParamList => MetadataTokens.GetToken(methodDef.GetParameters().FirstOrDefault()); public void OnParamListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodDef.GetParameters().FirstOrDefault(), protocol: "metadata"))); } string paramListTooltip; public string ParamListTooltip { get { var param = methodDef.GetParameters().FirstOrDefault(); if (param.IsNil) return null; return GenerateTooltip(ref paramListTooltip, metadataFile, param); } } IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)?.MainModule)?.GetDefinition(handle); public MethodDefEntry(MetadataFile metadataFile, MethodDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.methodDef = metadataFile.Metadata.GetMethodDefinition(handle); this.signatureTooltip = null; this.paramListTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ModuleRefTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ModuleRefTableTreeNode : MetadataTableTreeNode { public ModuleRefTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ModuleRef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.GetModuleReferences()) { list.Add(new ModuleRefEntry(metadataFile, row)); } return list; } internal struct ModuleRefEntry { readonly MetadataFile metadataFile; readonly ModuleReferenceHandle handle; readonly ModuleReference moduleRef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ModuleRef) + metadataFile.Metadata.GetTableRowSize(TableIndex.ModuleRef) * (RID - 1); public string Name => metadataFile.Metadata.GetString(moduleRef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(moduleRef.Name):X} \"{Name}\""; public ModuleRefEntry(MetadataFile metadataFile, ModuleReferenceHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.moduleRef = metadataFile.Metadata.GetModuleReference(handle); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ModuleTableTreeNode : MetadataTableTreeNode { public ModuleTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Module, metadataFile) { } protected override IReadOnlyList LoadTable() { return [new ModuleEntry(metadataFile, EntityHandle.ModuleDefinition)]; } internal struct ModuleEntry { readonly MetadataFile metadataFile; readonly ModuleDefinitionHandle handle; readonly ModuleDefinition moduleDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Module) + metadataFile.Metadata.GetTableRowSize(TableIndex.Module) * (RID - 1); public int Generation => moduleDef.Generation; public string Name => metadataFile.Metadata.GetString(moduleDef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(moduleDef.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Mvid => MetadataTokens.GetHeapOffset(moduleDef.Mvid); public string MvidTooltip => metadataFile.Metadata.GetGuid(moduleDef.Mvid).ToString(); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int GenerationId => MetadataTokens.GetHeapOffset(moduleDef.GenerationId); public string GenerationIdTooltip => moduleDef.GenerationId.IsNil ? null : metadataFile.Metadata.GetGuid(moduleDef.GenerationId).ToString(); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int BaseGenerationId => MetadataTokens.GetHeapOffset(moduleDef.BaseGenerationId); public string BaseGenerationIdTooltip => moduleDef.BaseGenerationId.IsNil ? null : metadataFile.Metadata.GetGuid(moduleDef.BaseGenerationId).ToString(); public ModuleEntry(MetadataFile metadataFile, ModuleDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.moduleDef = metadataFile.Metadata.GetModuleDefinition(); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class NestedClassTableTreeNode : MetadataTableTreeNode { public NestedClassTableTreeNode(MetadataFile metadataFile) : base(TableIndex.NestedClass, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.NestedClass); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int typeDefSize = metadataFile.Metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new NestedClassEntry(metadataFile, ptr, rid, typeDefSize)); } return list; } readonly struct NestedClass { public readonly TypeDefinitionHandle Nested; public readonly TypeDefinitionHandle Enclosing; public NestedClass(ReadOnlySpan ptr, int typeDefSize) { Nested = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr, typeDefSize)); Enclosing = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(typeDefSize), typeDefSize)); } } internal struct NestedClassEntry { readonly MetadataFile metadataFile; readonly NestedClass nestedClass; public int RID { get; } public int Token => 0x29000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int NestedClass => MetadataTokens.GetToken(nestedClass.Nested); public void OnNestedClassClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, nestedClass.Nested, protocol: "metadata"))); } string nestedClassTooltip; public string NestedClassTooltip => GenerateTooltip(ref nestedClassTooltip, metadataFile, nestedClass.Nested); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int EnclosingClass => MetadataTokens.GetToken(nestedClass.Enclosing); public void OnEnclosingClassClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, nestedClass.Enclosing, protocol: "metadata"))); } string enclosingClassTooltip; public string EnclosingClassTooltip => GenerateTooltip(ref enclosingClassTooltip, metadataFile, nestedClass.Enclosing); public NestedClassEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row, int typeDefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.NestedClass) + metadataFile.Metadata.GetTableRowSize(TableIndex.NestedClass) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.nestedClass = new NestedClass(ptr.Slice(rowOffset), typeDefSize); this.nestedClassTooltip = null; this.enclosingClassTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/ParamTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ParamTableTreeNode : MetadataTableTreeNode { public ParamTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Param, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.Param); row++) { list.Add(new ParamEntry(metadataFile, MetadataTokens.ParameterHandle(row))); } return list; } internal struct ParamEntry { readonly MetadataFile metadataFile; readonly ParameterHandle handle; readonly Parameter param; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Param) + metadataFile.Metadata.GetTableRowSize(TableIndex.Param) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public ParameterAttributes Attributes => param.Attributes; public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateMultipleChoiceGroup(typeof(ParameterAttributes), selectedValue: (int)param.Attributes, includeAll: false) }; public string Name => metadataFile.Metadata.GetString(param.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(param.Name):X} \"{Name}\""; public int Sequence => param.SequenceNumber; public ParamEntry(MetadataFile metadataFile, ParameterHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.param = metadataFile.Metadata.GetParameter(handle); } } } } ================================================ FILE: ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class PropertyMapTableTreeNode : MetadataTableTreeNode { public PropertyMapTableTreeNode(MetadataFile metadataFile) : base(TableIndex.PropertyMap, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(TableIndex.PropertyMap); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int typeDefSize = metadata.GetTableRowCount(TableIndex.TypeDef) < ushort.MaxValue ? 2 : 4; int propertyDefSize = metadata.GetTableRowCount(TableIndex.Property) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new PropertyMapEntry(metadataFile, ptr, rid, typeDefSize, propertyDefSize)); } return list; } readonly struct PropertyMap { public readonly TypeDefinitionHandle Parent; public readonly PropertyDefinitionHandle PropertyList; public PropertyMap(ReadOnlySpan ptr, int typeDefSize, int propertyDefSize) { Parent = MetadataTokens.TypeDefinitionHandle(Helpers.GetValueLittleEndian(ptr, typeDefSize)); PropertyList = MetadataTokens.PropertyDefinitionHandle(Helpers.GetValueLittleEndian(ptr.Slice(typeDefSize, propertyDefSize))); } } internal struct PropertyMapEntry { readonly MetadataFile metadataFile; readonly PropertyMap propertyMap; public int RID { get; } public int Token => 0x15000000 | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(propertyMap.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, propertyMap.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, propertyMap.Parent); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int PropertyList => MetadataTokens.GetToken(propertyMap.PropertyList); public void OnPropertyListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, propertyMap.PropertyList, protocol: "metadata"))); } string propertyListTooltip; public string PropertyListTooltip => GenerateTooltip(ref propertyListTooltip, metadataFile, propertyMap.PropertyList); public PropertyMapEntry(MetadataFile metadataFile, ReadOnlySpan ptr, int row, int typeDefSize, int propertyDefSize) { this.metadataFile = metadataFile; this.RID = row; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.PropertyMap) + metadataFile.Metadata.GetTableRowSize(TableIndex.PropertyMap) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.propertyMap = new PropertyMap(ptr.Slice(rowOffset), typeDefSize, propertyDefSize); this.propertyListTooltip = null; this.parentTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/PropertyTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal class PropertyTableTreeNode : MetadataTableTreeNode { public PropertyTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Property, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.PropertyDefinitions) { list.Add(new PropertyDefEntry(metadataFile, row)); } return list; } internal struct PropertyDefEntry : IMemberTreeNode { readonly MetadataFile metadataFile; readonly PropertyDefinitionHandle handle; readonly PropertyDefinition propertyDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Property) + metadataFile.Metadata.GetTableRowSize(TableIndex.Property) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public PropertyAttributes Attributes => propertyDef.Attributes; public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateMultipleChoiceGroup(typeof(PropertyAttributes), selectedValue: (int)propertyDef.Attributes, includeAll: false), }; public string Name => metadataFile.Metadata.GetString(propertyDef.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(propertyDef.Name):X} \"{Name}\""; IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)?.MainModule)?.GetDefinition(handle); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(propertyDef.Signature); string signatureTooltip; public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, metadataFile, handle); public PropertyDefEntry(MetadataFile metadataFile, PropertyDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.propertyDef = metadataFile.Metadata.GetPropertyDefinition(handle); this.signatureTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/PtrTableTreeNode.cs ================================================ // Copyright (c) 2024 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class PtrTableTreeNode : MetadataTableTreeNode { readonly TableIndex referencedTableKind; public PtrTableTreeNode(TableIndex kind, MetadataFile metadataFile) : base(kind, metadataFile) { if (kind is not (TableIndex.EventPtr or TableIndex.FieldPtr or TableIndex.MethodPtr or TableIndex.ParamPtr or TableIndex.PropertyPtr)) { throw new ArgumentOutOfRangeException("PtrTable does not support " + kind); } this.referencedTableKind = kind switch { TableIndex.EventPtr => TableIndex.Event, TableIndex.FieldPtr => TableIndex.Field, TableIndex.MethodPtr => TableIndex.MethodDef, TableIndex.ParamPtr => TableIndex.Param, TableIndex.PropertyPtr => TableIndex.Property, _ => throw new NotImplementedException(), // unreachable }; } protected override IReadOnlyList LoadTable() { var list = new List(); var metadata = metadataFile.Metadata; var length = metadata.GetTableRowCount(Kind); ReadOnlySpan ptr = metadata.AsReadOnlySpan(); int handleDefSize = metadataFile.Metadata.GetTableRowCount(referencedTableKind) < ushort.MaxValue ? 2 : 4; for (int rid = 1; rid <= length; rid++) { list.Add(new PtrEntry(metadataFile, Kind, referencedTableKind, handleDefSize, ptr, rid)); } return list; } readonly struct HandlePtr { public readonly EntityHandle Handle; public HandlePtr(ReadOnlySpan ptr, TableIndex kind, int handleSize) { Handle = MetadataTokens.EntityHandle(((int)kind << 24) | Helpers.GetValueLittleEndian(ptr, handleSize)); } } internal struct PtrEntry { readonly MetadataFile metadataFile; readonly HandlePtr handlePtr; readonly TableIndex kind; public int RID { get; } public int Token => ((int)kind << 24) | RID; public int Offset { get; } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Handle => MetadataTokens.GetToken(handlePtr.Handle); public void OnHandleClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, handlePtr.Handle, protocol: "metadata"))); } string handleTooltip; public string HandleTooltip => GenerateTooltip(ref handleTooltip, metadataFile, handlePtr.Handle); public PtrEntry(MetadataFile metadataFile, TableIndex kind, TableIndex handleKind, int handleDefSize, ReadOnlySpan ptr, int row) { this.metadataFile = metadataFile; this.RID = row; this.kind = kind; var rowOffset = metadataFile.Metadata.GetTableMetadataOffset(kind) + metadataFile.Metadata.GetTableRowSize(kind) * (row - 1); this.Offset = metadataFile.MetadataOffset + rowOffset; this.handlePtr = new HandlePtr(ptr.Slice(rowOffset), handleKind, handleDefSize); this.handleTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { class StandAloneSigTableTreeNode : MetadataTableTreeNode { public StandAloneSigTableTreeNode(MetadataFile metadataFile) : base(TableIndex.StandAloneSig, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); for (int row = 1; row <= metadataFile.Metadata.GetTableRowCount(TableIndex.StandAloneSig); row++) { list.Add(new StandAloneSigEntry(metadataFile, MetadataTokens.StandaloneSignatureHandle(row))); } return list; } internal struct StandAloneSigEntry { readonly MetadataFile metadataFile; readonly StandaloneSignatureHandle handle; readonly StandaloneSignature standaloneSig; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.StandAloneSig) + metadataFile.Metadata.GetTableRowSize(TableIndex.StandAloneSig) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(standaloneSig.Signature); string signatureTooltip; public string SignatureTooltip => GenerateTooltip(ref signatureTooltip, metadataFile, handle); public StandAloneSigEntry(MetadataFile metadataFile, StandaloneSignatureHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.standaloneSig = metadataFile.Metadata.GetStandaloneSignature(handle); this.signatureTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal class TypeDefTableTreeNode : MetadataTableTreeNode { public TypeDefTableTreeNode(MetadataFile metadataFile) : base(TableIndex.TypeDef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.TypeDefinitions) { list.Add(new TypeDefEntry(metadataFile, row)); } return list; } internal struct TypeDefEntry : IMemberTreeNode { readonly MetadataFile metadataFile; readonly TypeDefinitionHandle handle; readonly TypeDefinition typeDef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.TypeDef) + metadataFile.Metadata.GetTableRowSize(TableIndex.TypeDef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Other)] public TypeAttributes Attributes => typeDef.Attributes; const TypeAttributes otherFlagsMask = ~(TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | TypeAttributes.StringFormatMask | TypeAttributes.CustomFormatMask); public object AttributesTooltip => new FlagsTooltip { FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Visibility: ", (int)TypeAttributes.VisibilityMask, (int)(typeDef.Attributes & TypeAttributes.VisibilityMask), new Flag("NotPublic (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class layout: ", (int)TypeAttributes.LayoutMask, (int)(typeDef.Attributes & TypeAttributes.LayoutMask), new Flag("AutoLayout (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Class semantics: ", (int)TypeAttributes.ClassSemanticsMask, (int)(typeDef.Attributes & TypeAttributes.ClassSemanticsMask), new Flag("Class (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "String format: ", (int)TypeAttributes.StringFormatMask, (int)(typeDef.Attributes & TypeAttributes.StringFormatMask), new Flag("AnsiClass (0000)", 0, false), includeAny: false), FlagGroup.CreateSingleChoiceGroup(typeof(TypeAttributes), "Custom format: ", (int)TypeAttributes.CustomFormatMask, (int)(typeDef.Attributes & TypeAttributes.CustomFormatMask), new Flag("Value0 (0000)", 0, false), includeAny: false), FlagGroup.CreateMultipleChoiceGroup(typeof(TypeAttributes), "Flags:", (int)otherFlagsMask, (int)(typeDef.Attributes & otherFlagsMask), includeAll: false), }; public string NameTooltip => $"{MetadataTokens.GetHeapOffset(typeDef.Name):X} \"{Name}\""; public string Name => metadataFile.Metadata.GetString(typeDef.Name); public string NamespaceTooltip => $"{MetadataTokens.GetHeapOffset(typeDef.Namespace):X} \"{Namespace}\""; public string Namespace => metadataFile.Metadata.GetString(typeDef.Namespace); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int BaseType => MetadataTokens.GetToken(typeDef.BaseType); public void OnBaseTypeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.BaseType, protocol: "metadata"))); } public string BaseTypeTooltip { get { var output = new PlainTextOutput(); var provider = new DisassemblerSignatureTypeProvider(metadataFile, output); if (typeDef.BaseType.IsNil) return null; switch (typeDef.BaseType.Kind) { case HandleKind.TypeDefinition: provider.GetTypeFromDefinition(metadataFile.Metadata, (TypeDefinitionHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); return output.ToString(); case HandleKind.TypeReference: provider.GetTypeFromReference(metadataFile.Metadata, (TypeReferenceHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); return output.ToString(); case HandleKind.TypeSpecification: provider.GetTypeFromSpecification(metadataFile.Metadata, new Decompiler.Metadata.MetadataGenericContext(default(TypeDefinitionHandle), metadataFile.Metadata), (TypeSpecificationHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); return output.ToString(); default: return null; } } } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int FieldList => MetadataTokens.GetToken(typeDef.GetFields().FirstOrDefault()); public void OnFieldListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.GetFields().FirstOrDefault(), protocol: "metadata"))); } string fieldListTooltip; public string FieldListTooltip { get { var @field = typeDef.GetFields().FirstOrDefault(); if (@field.IsNil) return null; return GenerateTooltip(ref fieldListTooltip, metadataFile, @field); } } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MethodList => MetadataTokens.GetToken(typeDef.GetMethods().FirstOrDefault()); public void OnMethodListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.GetMethods().FirstOrDefault(), protocol: "metadata"))); } string methodListTooltip; public string MethodListTooltip { get { var method = typeDef.GetMethods().FirstOrDefault(); if (method.IsNil) return null; return GenerateTooltip(ref methodListTooltip, metadataFile, method); } } IEntity IMemberTreeNode.Member => ((MetadataModule)metadataFile.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)?.MainModule)?.GetDefinition(handle); public TypeDefEntry(MetadataFile metadataFile, TypeDefinitionHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.typeDef = metadataFile.Metadata.GetTypeDefinition(handle); this.methodListTooltip = null; this.fieldListTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class TypeRefTableTreeNode : MetadataTableTreeNode { public TypeRefTableTreeNode(MetadataFile metadataFile) : base(TableIndex.TypeRef, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.TypeReferences) { list.Add(new TypeRefEntry(metadataFile, row)); } return list; } internal struct TypeRefEntry { readonly MetadataFile metadataFile; readonly TypeReferenceHandle handle; readonly TypeReference typeRef; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataFile.MetadataOffset + metadataFile.Metadata.GetTableMetadataOffset(TableIndex.TypeRef) + metadataFile.Metadata.GetTableRowSize(TableIndex.TypeRef) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ResolutionScope => MetadataTokens.GetToken(typeRef.ResolutionScope); public void OnResolutionScopeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeRef.ResolutionScope, protocol: "metadata"))); } string resolutionScopeTooltip; public string ResolutionScopeTooltip => GenerateTooltip(ref resolutionScopeTooltip, metadataFile, typeRef.ResolutionScope); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(typeRef.Name):X} \"{Name}\""; public string Name => metadataFile.Metadata.GetString(typeRef.Name); public string NamespaceTooltip => $"{MetadataTokens.GetHeapOffset(typeRef.Namespace):X} \"{Namespace}\""; public string Namespace => metadataFile.Metadata.GetString(typeRef.Namespace); public TypeRefEntry(MetadataFile metadataFile, TypeReferenceHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.typeRef = metadataFile.Metadata.GetTypeReference(handle); this.resolutionScopeTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/CorTables/TypeSpecTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class TypeSpecTableTreeNode : MetadataTableTreeNode { public TypeSpecTableTreeNode(MetadataFile metadataFile) : base(TableIndex.TypeSpec, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.GetTypeSpecifications()) { list.Add(new TypeSpecEntry(metadataFile, row)); } return list; } internal struct TypeSpecEntry { readonly int metadataOffset; readonly MetadataFile module; readonly MetadataReader metadata; readonly TypeSpecificationHandle handle; readonly TypeSpecification typeSpec; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public int Offset => metadataOffset + metadata.GetTableMetadataOffset(TableIndex.TypeSpec) + metadata.GetTableRowSize(TableIndex.TypeSpec) * (RID - 1); [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(typeSpec.Signature); public string SignatureTooltip { get { ITextOutput output = new PlainTextOutput(); typeSpec.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), default)(ILNameSyntax.Signature); return output.ToString(); } } public TypeSpecEntry(MetadataFile metadataFile, TypeSpecificationHandle handle) { this.module = metadataFile; this.metadataOffset = metadataFile.MetadataOffset; this.metadata = metadataFile.Metadata; this.handle = handle; this.typeSpec = metadata.GetTypeSpecification(handle); } } } } ================================================ FILE: ILSpy/Metadata/DataDirectoriesTreeNode.cs ================================================ // 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 // 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.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { class DataDirectoriesTreeNode : ILSpyTreeNode { private PEFile module; public DataDirectoriesTreeNode(PEFile module) { this.module = module; } public override object Text => "Data Directories"; public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.ListFolder; public override object ExpandedIcon => Images.ListFolderOpen; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); //dataGrid.AutoGenerateColumns = false; //dataGrid.Columns.Add(new DataGridTextColumn { IsReadOnly = true, Header = "Name", Binding = new Binding("Name") }); //dataGrid.Columns.Add(new DataGridTextColumn { IsReadOnly = true, Header = "RVA", Binding = new Binding("RVA") { StringFormat = "X8" } }); //dataGrid.Columns.Add(new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") { StringFormat = "X8" } }); //dataGrid.Columns.Add(new DataGridTextColumn { IsReadOnly = true, Header = "Section", Binding = new Binding("Section") }); var headers = module.Reader.PEHeaders; var header = headers.PEHeader; var entries = new DataDirectoryEntry[] { new DataDirectoryEntry(headers, "Export Table", header.ExportTableDirectory), new DataDirectoryEntry(headers, "Import Table", header.ImportTableDirectory), new DataDirectoryEntry(headers, "Resource Table", header.ResourceTableDirectory), new DataDirectoryEntry(headers, "Exception Table", header.ExceptionTableDirectory), new DataDirectoryEntry(headers, "Certificate Table", header.CertificateTableDirectory), new DataDirectoryEntry(headers, "Base Relocation Table", header.BaseRelocationTableDirectory), new DataDirectoryEntry(headers, "Debug Table", header.DebugTableDirectory), new DataDirectoryEntry(headers, "Copyright Table", header.CopyrightTableDirectory), new DataDirectoryEntry(headers, "Global Pointer Table", header.GlobalPointerTableDirectory), new DataDirectoryEntry(headers, "Thread Local Storage Table", header.ThreadLocalStorageTableDirectory), new DataDirectoryEntry(headers, "Load Config", header.LoadConfigTableDirectory), new DataDirectoryEntry(headers, "Bound Import", header.BoundImportTableDirectory), new DataDirectoryEntry(headers, "Import Address Table", header.ImportAddressTableDirectory), new DataDirectoryEntry(headers, "Delay Import Descriptor", header.DelayImportTableDirectory), new DataDirectoryEntry(headers, "CLI Header", header.CorHeaderTableDirectory), }; dataGrid.ItemsSource = entries; tabPage.Content = dataGrid; return true; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { } class DataDirectoryEntry { public string Name { get; set; } public int RVA { get; set; } public int Size { get; set; } public string Section { get; set; } public DataDirectoryEntry(string name, int rva, int size, string section) { this.Name = name; this.RVA = rva; this.Size = size; this.Section = section; } public DataDirectoryEntry(PEHeaders headers, string name, DirectoryEntry entry) : this(name, entry.RelativeVirtualAddress, entry.Size, (headers.GetContainingSectionIndex(entry.RelativeVirtualAddress) >= 0) ? headers.SectionHeaders[headers.GetContainingSectionIndex(entry.RelativeVirtualAddress)].Name : "") { } } } } ================================================ FILE: ILSpy/Metadata/DebugDirectory/CodeViewTreeNode.cs ================================================ // 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 // 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. #nullable enable using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { sealed class CodeViewTreeNode : ILSpyTreeNode { readonly CodeViewDebugDirectoryData entry; public CodeViewTreeNode(CodeViewDebugDirectoryData entry) { this.entry = entry; } override public object Text => nameof(DebugDirectoryEntryType.CodeView); public override object ToolTip => "Associated PDB file description."; public override object Icon => Images.MetadataTable; public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); dataGrid.ItemsSource = new[] { entry }; tabPage.Content = dataGrid; return true; } sealed class PdbChecksumDebugDirectoryDataEntry { readonly CodeViewDebugDirectoryData entry; public PdbChecksumDebugDirectoryDataEntry(CodeViewDebugDirectoryData entry) { this.entry = entry; } } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, Text.ToString()); language.WriteCommentLine(output, $"GUID: {entry.Guid}"); language.WriteCommentLine(output, $"Age: {entry.Age}"); language.WriteCommentLine(output, $"Path: {entry.Path}"); } } } ================================================ FILE: ILSpy/Metadata/DebugDirectory/DebugDirectoryEntryTreeNode.cs ================================================ // 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 // 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. #nullable enable using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { sealed class DebugDirectoryEntryTreeNode : ILSpyTreeNode { readonly PEFile module; readonly PEReader reader; readonly DebugDirectoryEntry entry; public DebugDirectoryEntryTreeNode(PEFile module, DebugDirectoryEntry entry) { this.module = module; this.reader = module.Reader; this.entry = entry; } override public object Text => entry.Type.ToString(); public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.MetadataTable; public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, Text.ToString()); if (entry.DataSize > 0) { language.WriteCommentLine(output, $"Raw Data ({entry.DataSize}):"); int dataOffset = module.Reader.IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer; var data = module.Reader.GetEntireImage().GetContent(dataOffset, entry.DataSize); language.WriteCommentLine(output, data.ToHexString(data.Length)); } else { language.WriteCommentLine(output, $"(no data)"); } } } } ================================================ FILE: ILSpy/Metadata/DebugDirectory/PdbChecksumTreeNode.cs ================================================ // 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 // 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. #nullable enable using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { sealed class PdbChecksumTreeNode : ILSpyTreeNode { readonly PdbChecksumDebugDirectoryData entry; public PdbChecksumTreeNode(PdbChecksumDebugDirectoryData entry) { this.entry = entry; } override public object Text => nameof(DebugDirectoryEntryType.PdbChecksum); public override object ToolTip => "The entry stores a crypto hash of the content of the symbol file the PE/COFF\n" + "file was built with. The hash can be used to validate that a given PDB file was\n" + "built with the PE/COFF file and not altered in any way. More than one entry can\n" + "be present if multiple PDBs were produced during the build of the PE/COFF file\n" + "(for example, private and public symbols)."; public override object Icon => Images.MetadataTable; public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); dataGrid.ItemsSource = new[] { new PdbChecksumDebugDirectoryDataEntry(entry) }; tabPage.Content = dataGrid; return true; } sealed class PdbChecksumDebugDirectoryDataEntry { readonly PdbChecksumDebugDirectoryData entry; public PdbChecksumDebugDirectoryDataEntry(PdbChecksumDebugDirectoryData entry) { this.entry = entry; } public string AlgorithmName => entry.AlgorithmName; public string Checksum => entry.Checksum.ToHexString(entry.Checksum.Length); } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, Text.ToString()); language.WriteCommentLine(output, $"AlgorithmName: {entry.AlgorithmName}"); language.WriteCommentLine(output, $"Checksum: {entry.Checksum.ToHexString(entry.Checksum.Length)}"); } } } ================================================ FILE: ILSpy/Metadata/DebugDirectoryTreeNode.cs ================================================ // 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 // 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. #nullable enable using System; using System.Collections.Generic; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { class DebugDirectoryTreeNode : ILSpyTreeNode { private PEFile module; public DebugDirectoryTreeNode(PEFile module) { this.module = module; this.LazyLoading = true; } public override object Text => "Debug Directory"; public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.ListFolder; public override object ExpandedIcon => Images.ListFolderOpen; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); var entries = new List(); foreach (var entry in module.Reader.ReadDebugDirectory()) { int dataOffset = module.Reader.IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer; var data = module.Reader.GetEntireImage().GetContent(dataOffset, entry.DataSize); entries.Add(new DebugDirectoryEntryView(entry, data.ToHexString(data.Length))); } dataGrid.ItemsSource = entries.ToArray(); tabPage.Content = dataGrid; return true; } protected override void LoadChildren() { foreach (var entry in module.Reader.ReadDebugDirectory()) { try { switch (entry.Type) { case DebugDirectoryEntryType.CodeView: var codeViewData = module.Reader.ReadCodeViewDebugDirectoryData(entry); this.Children.Add(new CodeViewTreeNode(codeViewData)); break; case DebugDirectoryEntryType.EmbeddedPortablePdb: var embeddedPortablePdbProvider = module.Reader.ReadEmbeddedPortablePdbDebugDirectoryData(entry); var embeddedPortablePdbMetadataFile = new MetadataFile(MetadataFile.MetadataFileKind.ProgramDebugDatabase, module.FileName, embeddedPortablePdbProvider, isEmbedded: true); this.Children.Add(new MetadataTreeNode(embeddedPortablePdbMetadataFile, "Debug Metadata (Embedded)")); break; case DebugDirectoryEntryType.PdbChecksum: var pdbChecksumData = module.Reader.ReadPdbChecksumDebugDirectoryData(entry); this.Children.Add(new PdbChecksumTreeNode(pdbChecksumData)); break; case DebugDirectoryEntryType.Unknown: case DebugDirectoryEntryType.Coff: case DebugDirectoryEntryType.Reproducible: default: this.Children.Add(new DebugDirectoryEntryTreeNode(module, entry)); break; } } catch (BadImageFormatException ex) { this.Children.Add(new ErrorTreeNode(ex)); } } } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "Data Directories"); } class DebugDirectoryEntryView { public uint Timestamp { get; set; } public ushort MajorVersion { get; set; } public ushort MinorVersion { get; set; } public string Type { get; set; } public int SizeOfRawData { get; set; } [ColumnInfo("X8")] public int AddressOfRawData { get; set; } [ColumnInfo("X8")] public int PointerToRawData { get; set; } public string RawData { get; set; } public DebugDirectoryEntryView(DebugDirectoryEntry entry, string data) { this.RawData = data; this.Timestamp = entry.Stamp; this.MajorVersion = entry.MajorVersion; this.MinorVersion = entry.MinorVersion; this.Type = entry.Type.ToString(); this.SizeOfRawData = entry.DataSize; this.AddressOfRawData = entry.DataRelativeVirtualAddress; this.PointerToRawData = entry.DataPointer; } } } } ================================================ FILE: ILSpy/Metadata/DebugMetadataTablesTreeNode.cs ================================================ // Copyright (c) 2021 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 // 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.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { class DebugMetadataTablesTreeNode : ILSpyTreeNode { private MetadataFile metadataFile; public DebugMetadataTablesTreeNode(MetadataFile metadataFile) { this.metadataFile = metadataFile; this.LazyLoading = true; } public override object Text => "Tables"; public override object NavigationText => $"{Text} ({metadataFile.Name})"; public override object Icon => Images.MetadataTableGroup; protected override void LoadChildren() { if (ShowTable(TableIndex.Document)) this.Children.Add(new DocumentTableTreeNode(metadataFile)); if (ShowTable(TableIndex.MethodDebugInformation)) this.Children.Add(new MethodDebugInformationTableTreeNode(metadataFile)); if (ShowTable(TableIndex.LocalScope)) this.Children.Add(new LocalScopeTableTreeNode(metadataFile)); if (ShowTable(TableIndex.LocalVariable)) this.Children.Add(new LocalVariableTableTreeNode(metadataFile)); if (ShowTable(TableIndex.LocalConstant)) this.Children.Add(new LocalConstantTableTreeNode(metadataFile)); if (ShowTable(TableIndex.ImportScope)) this.Children.Add(new ImportScopeTableTreeNode(metadataFile)); if (ShowTable(TableIndex.StateMachineMethod)) this.Children.Add(new StateMachineMethodTableTreeNode(metadataFile)); if (ShowTable(TableIndex.CustomDebugInformation)) this.Children.Add(new CustomDebugInformationTableTreeNode(metadataFile)); bool ShowTable(TableIndex table) => !SettingsService.DisplaySettings.HideEmptyMetadataTables || metadataFile.Metadata.GetTableRowCount(table) > 0; } public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; return false; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "Metadata Tables"); } } } ================================================ FILE: ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs ================================================ // 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 // 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; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Windows; using System.Windows.Controls; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class CustomDebugInformationTableTreeNode : DebugMetadataTableTreeNode { public CustomDebugInformationTableTreeNode(MetadataFile metadataFile) : base(TableIndex.CustomDebugInformation, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.CustomDebugInformation) { list.Add(new CustomDebugInformationEntry(metadataFile, row)); } return list; } protected override void ConfigureDataGrid(DataGrid view) { view.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected; view.RowDetailsTemplateSelector = new CustomDebugInformationDetailsTemplateSelector(); } class CustomDebugInformationDetailsTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { var entry = (CustomDebugInformationEntry)item; switch (entry.kind) { case CustomDebugInformationEntry.CustomDebugInformationKind.StateMachineHoistedLocalScopes: case CustomDebugInformationEntry.CustomDebugInformationKind.CompilationMetadataReferences: case CustomDebugInformationEntry.CustomDebugInformationKind.CompilationOptions: case CustomDebugInformationEntry.CustomDebugInformationKind.TupleElementNames: return (DataTemplate)MetadataTableViews.Instance["CustomDebugInformationDetailsDataGrid"]; default: return (DataTemplate)MetadataTableViews.Instance["CustomDebugInformationDetailsTextBlob"]; } } } internal struct CustomDebugInformationEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly CustomDebugInformationHandle handle; readonly CustomDebugInformation debugInfo; internal readonly CustomDebugInformationKind kind; internal enum CustomDebugInformationKind { None, Unknown, StateMachineHoistedLocalScopes, DynamicLocalVariables, DefaultNamespaces, EditAndContinueLocalSlotMap, EditAndContinueLambdaAndClosureMap, EncStateMachineStateMap, EmbeddedSource, SourceLink, MethodSteppingInformation, CompilationOptions, CompilationMetadataReferences, TupleElementNames, TypeDefinitionDocuments } static CustomDebugInformationKind GetKind(MetadataReader metadata, GuidHandle h) { if (h.IsNil) return CustomDebugInformationKind.None; var guid = metadata.GetGuid(h); if (KnownGuids.StateMachineHoistedLocalScopes == guid) { return CustomDebugInformationKind.StateMachineHoistedLocalScopes; } if (KnownGuids.DynamicLocalVariables == guid) { return CustomDebugInformationKind.DynamicLocalVariables; } if (KnownGuids.DefaultNamespaces == guid) { return CustomDebugInformationKind.DefaultNamespaces; } if (KnownGuids.EditAndContinueLocalSlotMap == guid) { return CustomDebugInformationKind.EditAndContinueLocalSlotMap; } if (KnownGuids.EditAndContinueLambdaAndClosureMap == guid) { return CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap; } if (KnownGuids.EncStateMachineStateMap == guid) { return CustomDebugInformationKind.EncStateMachineStateMap; } if (KnownGuids.EmbeddedSource == guid) { return CustomDebugInformationKind.EmbeddedSource; } if (KnownGuids.SourceLink == guid) { return CustomDebugInformationKind.SourceLink; } if (KnownGuids.MethodSteppingInformation == guid) { return CustomDebugInformationKind.MethodSteppingInformation; } if (KnownGuids.CompilationOptions == guid) { return CustomDebugInformationKind.CompilationOptions; } if (KnownGuids.CompilationMetadataReferences == guid) { return CustomDebugInformationKind.CompilationMetadataReferences; } if (KnownGuids.TupleElementNames == guid) { return CustomDebugInformationKind.TupleElementNames; } if (KnownGuids.TypeDefinitionDocuments == guid) { return CustomDebugInformationKind.TypeDefinitionDocuments; } return CustomDebugInformationKind.Unknown; } public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : (object)offset; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(debugInfo.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.Parent, protocol: "metadata"))); } string parentTooltip; public string ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, debugInfo.Parent); string kindString; public string Kind { get { if (kindString != null) return kindString; Guid guid; if (kind != CustomDebugInformationKind.None) { guid = metadataFile.Metadata.GetGuid(debugInfo.Kind); } else { guid = Guid.Empty; } kindString = kind switch { CustomDebugInformationKind.None => "", CustomDebugInformationKind.StateMachineHoistedLocalScopes => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - State Machine Hoisted Local Scopes (C# / VB) [{guid}]", CustomDebugInformationKind.DynamicLocalVariables => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Dynamic Local Variables (C#) [{guid}]", CustomDebugInformationKind.DefaultNamespaces => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Default Namespaces (VB) [{guid}]", CustomDebugInformationKind.EditAndContinueLocalSlotMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Local Slot Map (C# / VB) [{guid}]", CustomDebugInformationKind.EditAndContinueLambdaAndClosureMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue Lambda And Closure Map (C# / VB) [{guid}]", CustomDebugInformationKind.EncStateMachineStateMap => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Edit And Continue State Machine State Map (C# / VB) [{guid}]", CustomDebugInformationKind.EmbeddedSource => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Embedded Source (C# / VB) [{guid}]", CustomDebugInformationKind.SourceLink => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Source Link (C# / VB) [{guid}]", CustomDebugInformationKind.MethodSteppingInformation => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Method Stepping Information (C# / VB) [{guid}]", CustomDebugInformationKind.CompilationOptions => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Options (C# / VB) [{guid}]", CustomDebugInformationKind.CompilationMetadataReferences => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Compilation Metadata References (C# / VB) [{guid}]", CustomDebugInformationKind.TupleElementNames => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Tuple Element Names (C#) [{guid}]", CustomDebugInformationKind.TypeDefinitionDocuments => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Type Definition Documents (C# / VB) [{guid}]", _ => $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - Unknown [{guid}]", }; return kindString; } } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Value => MetadataTokens.GetHeapOffset(debugInfo.Value); public string ValueTooltip { get { if (debugInfo.Value.IsNil) return ""; return metadataFile.Metadata.GetBlobReader(debugInfo.Value).ToHexString(); } } object rowDetails; public object RowDetails { get { if (rowDetails != null) return rowDetails; if (debugInfo.Value.IsNil) return null; var reader = metadataFile.Metadata.GetBlobReader(debugInfo.Value); ArrayList list; switch (kind) { case CustomDebugInformationKind.None: return null; case CustomDebugInformationKind.StateMachineHoistedLocalScopes: list = new ArrayList(); while (reader.RemainingBytes > 0) { uint offset = reader.ReadUInt32(); uint length = reader.ReadUInt32(); list.Add(new { StartOffset = offset, Length = length }); } return rowDetails = list; case CustomDebugInformationKind.SourceLink: return reader.ReadUTF8(reader.RemainingBytes); case CustomDebugInformationKind.CompilationOptions: list = new ArrayList(); while (reader.RemainingBytes > 0) { string name = reader.ReadUTF8StringNullTerminated(); string value = reader.ReadUTF8StringNullTerminated(); list.Add(new { Name = name, Value = value }); } return rowDetails = list; case CustomDebugInformationKind.CompilationMetadataReferences: list = new ArrayList(); while (reader.RemainingBytes > 0) { string fileName = reader.ReadUTF8StringNullTerminated(); string aliases = reader.ReadUTF8StringNullTerminated(); byte flags = reader.ReadByte(); uint timestamp = reader.ReadUInt32(); uint fileSize = reader.ReadUInt32(); Guid guid = reader.ReadGuid(); list.Add(new { FileName = fileName, Aliases = aliases, Flags = flags, Timestamp = timestamp, FileSize = fileSize, Guid = guid }); } return rowDetails = list; case CustomDebugInformationKind.TupleElementNames: list = new ArrayList(); while (reader.RemainingBytes > 0) { list.Add(new { ElementName = reader.ReadUTF8StringNullTerminated() }); } return rowDetails = list; default: return reader.ToHexString(); } } } public CustomDebugInformationEntry(MetadataFile metadataFile, CustomDebugInformationHandle handle) { this.metadataFile = metadataFile; this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.CustomDebugInformation) + metadataFile.Metadata.GetTableRowSize(TableIndex.CustomDebugInformation) * (MetadataTokens.GetRowNumber(handle) - 1); this.handle = handle; this.debugInfo = metadataFile.Metadata.GetCustomDebugInformation(handle); this.kind = GetKind(metadataFile.Metadata, debugInfo.Kind); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/DocumentTableTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class DocumentTableTreeNode : DebugMetadataTableTreeNode { public DocumentTableTreeNode(MetadataFile metadataFile) : base(TableIndex.Document, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.Documents) { list.Add(new DocumentEntry(metadataFile, row)); } return list; } internal readonly struct DocumentEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly DocumentHandle handle; readonly Document document; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : offset; public string Name => metadataFile.Metadata.GetString(document.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(document.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int HashAlgorithm => MetadataTokens.GetHeapOffset(document.HashAlgorithm); public string HashAlgorithmTooltip { get { if (document.HashAlgorithm.IsNil) return null; Guid guid = metadataFile.Metadata.GetGuid(document.HashAlgorithm); if (guid == KnownGuids.HashAlgorithmSHA1) return "SHA1 [ff1816ec-aa5e-4d10-87f7-6f4963833460]"; if (guid == KnownGuids.HashAlgorithmSHA256) return "SHA256 [8829d00f-11b8-4213-878b-770e8597ac16]"; return $"Unknown [" + guid + "]"; } } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Hash => MetadataTokens.GetHeapOffset(document.Hash); public string HashTooltip { get { if (document.Hash.IsNil) return null; System.Collections.Immutable.ImmutableArray token = metadataFile.Metadata.GetBlobContent(document.Hash); return token.ToHexString(token.Length); } } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Language => MetadataTokens.GetHeapOffset(document.Language); public string LanguageTooltip { get { if (document.Language.IsNil) return null; Guid guid = metadataFile.Metadata.GetGuid(document.Language); if (guid == KnownGuids.CSharpLanguageGuid) return "Visual C# [3f5162f8-07c6-11d3-9053-00c04fa302a1]"; if (guid == KnownGuids.VBLanguageGuid) return "Visual Basic [3a12d0b8-c26c-11d0-b442-00a0244a1dd2]"; if (guid == KnownGuids.FSharpLanguageGuid) return "Visual F# [ab4f38c9-b6e6-43ba-be3b-58080b2ccce3]"; return $"Unknown [" + guid + "]"; } } public DocumentEntry(MetadataFile metadataFile, DocumentHandle handle) { this.metadataFile = metadataFile; this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.Document) + metadataFile.Metadata.GetTableRowSize(TableIndex.Document) * (MetadataTokens.GetRowNumber(handle) - 1); this.handle = handle; this.document = metadataFile.Metadata.GetDocument(handle); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class ImportScopeTableTreeNode : DebugMetadataTableTreeNode { public ImportScopeTableTreeNode(MetadataFile metadataFile) : base(TableIndex.ImportScope, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.ImportScopes) { list.Add(new ImportScopeEntry(metadataFile, row)); } return list; } internal readonly struct ImportScopeEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly ImportScopeHandle handle; readonly ImportScope localScope; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : offset; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Parent => MetadataTokens.GetToken(localScope.Parent); public void OnParentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.Parent, protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Imports => MetadataTokens.GetHeapOffset(localScope.ImportsBlob); public ImportScopeEntry(MetadataFile metadataFile, ImportScopeHandle handle) { this.metadataFile = metadataFile; this.handle = handle; this.localScope = metadataFile.Metadata.GetImportScope(handle); this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.ImportScope) + metadataFile.Metadata.GetTableRowSize(TableIndex.ImportScope) * (MetadataTokens.GetRowNumber(handle) - 1); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/LocalConstantTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class LocalConstantTableTreeNode : DebugMetadataTableTreeNode { public LocalConstantTableTreeNode(MetadataFile metadataFile) : base(TableIndex.LocalConstant, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.LocalConstants) { list.Add(new LocalConstantEntry(metadataFile, row)); } return list; } internal readonly struct LocalConstantEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly LocalConstantHandle handle; readonly LocalConstant localConst; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : (object)offset; public string Name => metadataFile.Metadata.GetString(localConst.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(localConst.Name):X} \"{Name}\""; [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int Signature => MetadataTokens.GetHeapOffset(localConst.Signature); public LocalConstantEntry(MetadataFile metadataFile, LocalConstantHandle handle) { this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.LocalConstant) + metadataFile.Metadata.GetTableRowSize(TableIndex.LocalConstant) * (MetadataTokens.GetRowNumber(handle) - 1); this.metadataFile = metadataFile; this.handle = handle; this.localConst = metadataFile.Metadata.GetLocalConstant(handle); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class LocalScopeTableTreeNode : DebugMetadataTableTreeNode { public LocalScopeTableTreeNode(MetadataFile metadataFile) : base(TableIndex.LocalScope, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.LocalScopes) { list.Add(new LocalScopeEntry(metadataFile, row)); } return list; } internal struct LocalScopeEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly LocalScopeHandle handle; readonly LocalScope localScope; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : (object)offset; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Method => MetadataTokens.GetToken(localScope.Method); public void OnMethodClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.Method, protocol: "metadata"))); } string methodTooltip; public string MethodTooltip => GenerateTooltip(ref methodTooltip, metadataFile, localScope.Method); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ImportScope => MetadataTokens.GetToken(localScope.ImportScope); public void OnImportScopeClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.ImportScope, protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int VariableList => MetadataTokens.GetToken(localScope.GetLocalVariables().FirstOrDefault()); public void OnVariableListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.GetLocalVariables().FirstOrDefault(), protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int ConstantList => MetadataTokens.GetToken(localScope.GetLocalConstants().FirstOrDefault()); public void OnConstantListClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.GetLocalConstants().FirstOrDefault(), protocol: "metadata"))); } public int StartOffset => localScope.StartOffset; public int Length => localScope.Length; public LocalScopeEntry(MetadataFile metadataFile, LocalScopeHandle handle) { this.metadataFile = metadataFile; this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.LocalScope) + metadataFile.Metadata.GetTableRowSize(TableIndex.LocalScope) * (MetadataTokens.GetRowNumber(handle) - 1); this.handle = handle; this.localScope = metadataFile.Metadata.GetLocalScope(handle); this.methodTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/LocalVariableTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class LocalVariableTableTreeNode : DebugMetadataTableTreeNode { public LocalVariableTableTreeNode(MetadataFile metadataFile) : base(TableIndex.LocalVariable, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.LocalVariables) { list.Add(new LocalVariableEntry(metadataFile, row)); } return list; } internal struct LocalVariableEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly LocalVariableHandle handle; readonly LocalVariable localVar; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : (object)offset; [ColumnInfo("X8", Kind = ColumnKind.Other)] public LocalVariableAttributes Attributes => localVar.Attributes; public object AttributesTooltip => new FlagsTooltip() { FlagGroup.CreateMultipleChoiceGroup(typeof(LocalVariableAttributes)), }; public int Index => localVar.Index; public string Name => metadataFile.Metadata.GetString(localVar.Name); public string NameTooltip => $"{MetadataTokens.GetHeapOffset(localVar.Name):X} \"{Name}\""; public LocalVariableEntry(MetadataFile metadataFile, LocalVariableHandle handle) { this.metadataFile = metadataFile; this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.LocalVariable) + metadataFile.Metadata.GetTableRowSize(TableIndex.LocalVariable) * (MetadataTokens.GetRowNumber(handle) - 1); this.handle = handle; this.localVar = metadataFile.Metadata.GetLocalVariable(handle); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class MethodDebugInformationTableTreeNode : DebugMetadataTableTreeNode { public MethodDebugInformationTableTreeNode(MetadataFile metadataFile) : base(TableIndex.MethodDebugInformation, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); foreach (var row in metadataFile.Metadata.MethodDebugInformation) { list.Add(new MethodDebugInformationEntry(metadataFile, row)); } return list; } internal struct MethodDebugInformationEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly MethodDebugInformationHandle handle; readonly MethodDebugInformation debugInfo; public int RID => MetadataTokens.GetRowNumber(handle); public int Token => MetadataTokens.GetToken(handle); public object Offset => offset == null ? "n/a" : (object)offset; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int Document => MetadataTokens.GetToken(debugInfo.Document); public void OnDocumentClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.Document, protocol: "metadata"))); } public string DocumentTooltip { get { if (debugInfo.Document.IsNil) return null; var document = metadataFile.Metadata.GetDocument(debugInfo.Document); return $"{MetadataTokens.GetHeapOffset(document.Name):X} \"{metadataFile.Metadata.GetString(document.Name)}\""; } } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] public int SequencePoints => MetadataTokens.GetHeapOffset(debugInfo.SequencePointsBlob); public string SequencePointsTooltip { get { if (debugInfo.SequencePointsBlob.IsNil) return null; StringBuilder sb = new StringBuilder(); foreach (var p in debugInfo.GetSequencePoints()) { sb.AppendLine($"document='{MetadataTokens.GetToken(p.Document):X8}', offset={p.Offset}, start={p.StartLine};{p.StartColumn}, end={p.EndLine};{p.EndColumn}, hidden={p.IsHidden}"); } return sb.ToString().TrimEnd(); } } [ColumnInfo("X8", Kind = ColumnKind.Token)] public int LocalSignature => MetadataTokens.GetToken(debugInfo.LocalSignature); public void OnLocalSignatureClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.LocalSignature, protocol: "metadata"))); } public string LocalSignatureTooltip { get { if (debugInfo.LocalSignature.IsNil) return null; ITextOutput output = new PlainTextOutput(); var context = new MetadataGenericContext(default(TypeDefinitionHandle), metadataFile.Metadata); StandaloneSignature localSignature = metadataFile.Metadata.GetStandaloneSignature(debugInfo.LocalSignature); var signatureDecoder = new DisassemblerSignatureTypeProvider(metadataFile, output); int index = 0; foreach (var item in localSignature.DecodeLocalSignature(signatureDecoder, context)) { if (index > 0) output.WriteLine(); output.Write("[{0}] ", index); item(ILNameSyntax.Signature); index++; } return output.ToString(); } } public MethodDebugInformationEntry(MetadataFile metadataFile, MethodDebugInformationHandle handle) { this.metadataFile = metadataFile; this.offset = metadataFile.IsEmbedded ? null : (int?)metadataFile.Metadata.GetTableMetadataOffset(TableIndex.MethodDebugInformation) + metadataFile.Metadata.GetTableRowSize(TableIndex.MethodDebugInformation) * (MetadataTokens.GetRowNumber(handle) - 1); this.handle = handle; this.debugInfo = metadataFile.Metadata.GetMethodDebugInformation(handle); } } } } ================================================ FILE: ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class StateMachineMethodTableTreeNode : DebugMetadataTableTreeNode { public StateMachineMethodTableTreeNode(MetadataFile metadataFile) : base(TableIndex.StateMachineMethod, metadataFile) { } protected override IReadOnlyList LoadTable() { var list = new List(); var length = metadataFile.Metadata.GetTableRowCount(TableIndex.StateMachineMethod); var reader = metadataFile.Metadata.AsBlobReader(); reader.Offset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.StateMachineMethod); for (int rid = 1; rid <= length; rid++) { list.Add(new StateMachineMethodEntry(metadataFile, ref reader, rid)); } return list; } internal struct StateMachineMethodEntry { readonly int? offset; readonly MetadataFile metadataFile; readonly MethodDefinitionHandle moveNextMethod; readonly MethodDefinitionHandle kickoffMethod; public int RID { get; } public int Token => 0x36000000 + RID; public object Offset => offset == null ? "n/a" : (object)offset; [ColumnInfo("X8", Kind = ColumnKind.Token)] public int MoveNextMethod => MetadataTokens.GetToken(moveNextMethod); public void OnMoveNextMethodClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, moveNextMethod, protocol: "metadata"))); } string moveNextMethodTooltip; public string MoveNextMethodTooltip => GenerateTooltip(ref moveNextMethodTooltip, metadataFile, moveNextMethod); [ColumnInfo("X8", Kind = ColumnKind.Token)] public int KickoffMethod => MetadataTokens.GetToken(kickoffMethod); public void OnKickofMethodClick() { MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, kickoffMethod, protocol: "metadata"))); } string kickoffMethodTooltip; public string KickoffMethodTooltip => GenerateTooltip(ref kickoffMethodTooltip, metadataFile, kickoffMethod); public StateMachineMethodEntry(MetadataFile metadataFile, ref BlobReader reader, int row) { this.metadataFile = metadataFile; this.RID = row; int rowOffset = metadataFile.Metadata.GetTableMetadataOffset(TableIndex.StateMachineMethod) + metadataFile.Metadata.GetTableRowSize(TableIndex.StateMachineMethod) * (row - 1); this.offset = metadataFile.IsEmbedded ? null : (int?)rowOffset; int methodDefSize = metadataFile.Metadata.GetTableRowCount(TableIndex.MethodDef) < ushort.MaxValue ? 2 : 4; this.moveNextMethod = MetadataTokens.MethodDefinitionHandle(methodDefSize == 2 ? reader.ReadInt16() : reader.ReadInt32()); this.kickoffMethod = MetadataTokens.MethodDefinitionHandle(methodDefSize == 2 ? reader.ReadInt16() : reader.ReadInt32()); this.kickoffMethodTooltip = null; this.moveNextMethodTooltip = null; } } } } ================================================ FILE: ILSpy/Metadata/DosHeaderTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { class DosHeaderTreeNode : ILSpyTreeNode { private PEFile module; public DosHeaderTreeNode(PEFile module) { this.module = module; } public override object Text => "DOS Header"; public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.Header; public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); var reader = module.Reader.GetEntireImage().GetReader(0, 64); var entries = new List(); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_magic", "Magic Number (MZ)")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cblp", "Bytes on last page of file")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cp", "Pages in file")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_crlc", "Relocations")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cparhdr", "Size of header in paragraphs")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_minalloc", "Minimum extra paragraphs needed")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_maxalloc", "Maximum extra paragraphs needed")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ss", "Initial (relative) SS value")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_sp", "Initial SP value")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_csum", "Checksum")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ip", "Initial IP value")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cs", "Initial (relative) CS value")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_lfarlc", "File address of relocation table")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ovno", "Overlay number")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[0]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[1]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[2]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[3]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_oemid", "OEM identifier (for e_oeminfo)")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_oeminfo", "OEM information; e_oemid specific")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[0]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[1]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[2]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[3]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[4]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[5]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[6]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[7]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[8]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[9]", "Reserved words")); entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "e_lfanew", "File address of new exe header")); view.ItemsSource = entries; tabPage.Content = view; return true; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { } } } ================================================ FILE: ILSpy/Metadata/FlagsFilterControl.xaml ================================================ ================================================ FILE: ILSpy/Metadata/FlagsFilterControl.xaml.cs ================================================ using System; using System.Linq; using System.Windows; using System.Windows.Controls; using DataGridExtensions; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpy.Metadata { /// /// Interaction logic for FlagsFilterControl.xaml /// public partial class FlagsFilterControl { ListBox listBox; public FlagsFilterControl() { InitializeComponent(); } public FlagsContentFilter Filter { get { return (FlagsContentFilter)GetValue(FilterProperty); } set { SetValue(FilterProperty, value); } } public Type FlagsType { get; set; } /// /// Identifies the Filter dependency property /// public static readonly DependencyProperty FilterProperty = DependencyProperty.Register("Filter", typeof(FlagsContentFilter), typeof(FlagsFilterControl), new FrameworkPropertyMetadata(new FlagsContentFilter(-1), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (sender, e) => ((FlagsFilterControl)sender).Filter_Changed())); /// When overridden in a derived class, is invoked whenever application code or internal processes call . public override void OnApplyTemplate() { base.OnApplyTemplate(); listBox = Template.FindName("ListBox", this) as ListBox; if (listBox != null) { listBox.ItemsSource = FlagGroup.GetFlags(FlagsType, neutralItem: ""); } var filter = Filter; if (filter == null || filter.Mask == -1) { listBox?.SelectAll(); } } private void Filter_Changed() { var filter = Filter; if (filter == null || filter.Mask == -1) { listBox?.SelectAll(); return; } if (listBox?.SelectedItems.Count != 0) return; foreach (var item in listBox.Items.Cast()) { if ((item.Value & filter.Mask) != 0 || item.Value == 0) { listBox.SelectedItems.Add(item); } } } private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.RemovedItems?.OfType().Any(f => f.Value == -1) == true) { Filter = new FlagsContentFilter(0); listBox.UnselectAll(); return; } if (e.AddedItems?.OfType().Any(f => f.Value == -1) == true) { Filter = new FlagsContentFilter(-1); listBox.SelectAll(); return; } bool deselectAny = e.RemovedItems?.OfType().Any(f => f.Value != -1) == true; int mask = 0; foreach (var item in listBox.SelectedItems.Cast()) { if (deselectAny && item.Value == -1) continue; mask |= item.Value; } Filter = new FlagsContentFilter(mask); } } public class FlagsContentFilter : IContentFilter { public int Mask { get; } public FlagsContentFilter(int mask) { this.Mask = mask; } public bool IsMatch(object value) { if (value == null) return true; return Mask == -1 || (Mask & (int)value) != 0; } } } ================================================ FILE: ILSpy/Metadata/FlagsTooltip.xaml ================================================ ================================================ FILE: ILSpy/Metadata/FlagsTooltip.xaml.cs ================================================ // 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 // 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; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Data; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpy.Metadata { /// /// Interaction logic for FlagsTooltip.xaml /// public partial class FlagsTooltip : IEnumerable { public FlagsTooltip(int value = 0, Type flagsType = null) { InitializeComponent(); } public void Add(FlagGroup group) { Groups.Add(group); } public IEnumerator GetEnumerator() { return Groups.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Groups.GetEnumerator(); } public List Groups { get; } = new List(); } class FlagActiveConverter : DependencyObject, IValueConverter { public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(FlagActiveConverter), new PropertyMetadata(0)); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return Value == (int)value || ((int)value & Value) != 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public readonly struct Flag { public string Name { get; } public int Value { get; } public bool IsSelected { get; } public Flag(string name, int value, bool isSelected) { this.Name = name; this.Value = value; this.IsSelected = isSelected; } } public abstract class FlagGroup { public static MultipleChoiceGroup CreateMultipleChoiceGroup(Type flagsType, string header = null, int mask = -1, int selectedValue = 0, bool includeAll = true) { MultipleChoiceGroup group = new MultipleChoiceGroup(GetFlags(flagsType, mask, selectedValue, includeAll ? "" : null)); group.Header = header; group.SelectedFlags = selectedValue; return group; } public static SingleChoiceGroup CreateSingleChoiceGroup(Type flagsType, string header = null, int mask = -1, int selectedValue = 0, Flag defaultFlag = default, bool includeAny = true) { var group = new SingleChoiceGroup(GetFlags(flagsType, mask, selectedValue, includeAny ? "" : null)); group.Header = header; group.SelectedFlag = group.Flags.SingleOrDefault(f => f.Value == selectedValue); if (group.SelectedFlag.Name == null) { group.SelectedFlag = defaultFlag; } return group; } public static IEnumerable GetFlags(Type flagsType, int mask = -1, int selectedValues = 0, string neutralItem = null) { if (neutralItem != null) yield return new Flag(neutralItem, -1, false); foreach (var item in flagsType.GetFields(BindingFlags.Static | BindingFlags.Public)) { if (item.Name.EndsWith("Mask", StringComparison.Ordinal)) continue; int value = (int)CSharpPrimitiveCast.Cast(TypeCode.Int32, item.GetRawConstantValue(), false); if ((value & mask) == 0) continue; yield return new Flag($"{item.Name} ({value:X4})", value, (selectedValues & value) != 0); } } public string Header { get; set; } public IList Flags { get; protected set; } } public class MultipleChoiceGroup : FlagGroup { public MultipleChoiceGroup(IEnumerable flags) { this.Flags = flags.ToList(); } public int SelectedFlags { get; set; } } public class SingleChoiceGroup : FlagGroup { public SingleChoiceGroup(IEnumerable flags) { this.Flags = flags.ToList(); } public Flag SelectedFlag { get; set; } } class NullVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return Visibility.Collapsed; return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } } ================================================ FILE: ILSpy/Metadata/GoToTokenCommand.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Reflection; using System.Reflection.Metadata.Ecma335; using System.Windows; using System.Windows.Controls; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.Metadata; using ICSharpCode.ILSpy.Properties; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.GoToToken), Order = 10)] [Shared] class GoToTokenCommand : IContextMenuEntry { public void Execute(TextViewContext context) { int token = GetSelectedToken(context.DataGrid, out MetadataFile module).Value; MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, MetadataTokens.Handle(token), protocol: "metadata"))); } public bool IsEnabled(TextViewContext context) { return true; } public bool IsVisible(TextViewContext context) { return context.DataGrid?.Name == "MetadataView" && GetSelectedToken(context.DataGrid, out _) != null; } private int? GetSelectedToken(DataGrid grid, out MetadataFile module) { module = null; if (grid == null) return null; var cell = grid.CurrentCell; if (!cell.IsValid) return null; Type type = cell.Item.GetType(); var property = type.GetProperty(cell.Column.Header.ToString()); var moduleField = type.GetField("module", BindingFlags.NonPublic | BindingFlags.Instance); if (property == null || property.PropertyType != typeof(int) || !property.GetCustomAttributes(false).Any(a => a is ColumnInfoAttribute { Kind: ColumnKind.Token } c)) return null; module = (MetadataFile)moduleField.GetValue(cell.Item); return (int)property.GetValue(cell.Item); } } [ExportContextMenuEntry(Header = nameof(Resources.Copy), Order = 10)] [Shared] class CopyCommand : IContextMenuEntry { public void Execute(TextViewContext context) { string content = GetSelectedCellContent(context.OriginalSource); Clipboard.SetText(content); } public bool IsEnabled(TextViewContext context) { return true; } public bool IsVisible(TextViewContext context) { return context.DataGrid?.Name == "MetadataView" && GetSelectedCellContent(context.OriginalSource) != null; } private static string GetSelectedCellContent(DependencyObject originalSource) { var cell = originalSource.AncestorsAndSelf().OfType().FirstOrDefault(); return cell?.Column.OnCopyingCellClipboardContent(cell.DataContext).ToString(); } } } ================================================ FILE: ILSpy/Metadata/Heaps/BlobHeapTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class BlobHeapTreeNode : MetadataHeapTreeNode { readonly List list; public BlobHeapTreeNode(MetadataFile metadataFile) : base(HandleKind.Blob, metadataFile) { list = new List(); var metadata = metadataFile.Metadata; BlobHandle handle = MetadataTokens.BlobHandle(0); do { BlobHeapEntry entry = new BlobHeapEntry(metadata, handle); list.Add(entry); handle = metadata.GetNextHandle(handle); } while (!handle.IsNil); } public override object Text => $"Blob Heap ({list.Count})"; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); view.ItemsSource = list; tabPage.Content = view; return true; } class BlobHeapEntry { readonly MetadataReader metadata; readonly BlobHandle handle; public int Offset => metadata.GetHeapOffset(handle); public int Length => metadata.GetBlobReader(handle).Length; public string Value => metadata.GetBlobReader(handle).ToHexString(); public BlobHeapEntry(MetadataReader metadata, BlobHandle handle) { this.metadata = metadata; this.handle = handle; } } } } ================================================ FILE: ILSpy/Metadata/Heaps/GuidHeapTreeNode.cs ================================================ // Copyright (c) 2021 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class GuidHeapTreeNode : MetadataHeapTreeNode { readonly List list; public GuidHeapTreeNode(MetadataFile metadataFile) : base(HandleKind.Guid, metadataFile) { list = new List(); var metadata = metadataFile.Metadata; int count = metadata.GetHeapSize(HeapIndex.Guid) >> 4; for (int i = 1; i <= count; i++) { GuidHeapEntry entry = new GuidHeapEntry(metadata, MetadataTokens.GuidHandle(i)); list.Add(entry); } } public override object Text => $"Guid Heap ({list.Count})"; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); view.ItemsSource = list; tabPage.Content = view; return true; } class GuidHeapEntry { readonly MetadataReader metadata; readonly GuidHandle handle; public int Index => metadata.GetHeapOffset(handle); public int Length => 16; public string Value => metadata.GetGuid(handle).ToString(); public GuidHeapEntry(MetadataReader metadata, GuidHandle handle) { this.metadata = metadata; this.handle = handle; } } } } ================================================ FILE: ILSpy/Metadata/Heaps/StringHeapTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class StringHeapTreeNode : MetadataHeapTreeNode { readonly List list; public StringHeapTreeNode(MetadataFile metadataFile) : base(HandleKind.String, metadataFile) { list = new List(); var metadata = metadataFile.Metadata; StringHandle handle = MetadataTokens.StringHandle(0); do { StringHeapEntry entry = new StringHeapEntry(metadata, handle); list.Add(entry); handle = metadata.GetNextHandle(handle); } while (!handle.IsNil); } public override object Text => $"String Heap ({list.Count})"; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); view.ItemsSource = list; tabPage.Content = view; return true; } class StringHeapEntry { readonly MetadataReader metadata; readonly StringHandle handle; public int Offset => metadata.GetHeapOffset(handle); public int Length => metadata.GetString(handle).Length; public string Value => metadata.GetString(handle); public StringHeapEntry(MetadataReader metadata, StringHandle handle) { this.metadata = metadata; this.handle = handle; } } } } ================================================ FILE: ILSpy/Metadata/Heaps/UserStringHeapTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.Metadata { internal class UserStringHeapTreeNode : MetadataHeapTreeNode { readonly List list; public UserStringHeapTreeNode(MetadataFile metadataFile) : base(HandleKind.UserString, metadataFile) { list = new List(); var metadata = metadataFile.Metadata; UserStringHandle handle = MetadataTokens.UserStringHandle(0); do { UserStringHeapEntry entry = new UserStringHeapEntry(metadata, handle); list.Add(entry); handle = metadata.GetNextHandle(handle); } while (!handle.IsNil); } public override object Text => $"UserString Heap ({list.Count})"; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); view.ItemsSource = list; tabPage.Content = view; return true; } class UserStringHeapEntry { readonly MetadataReader metadata; readonly UserStringHandle handle; public int Offset => metadata.GetHeapOffset(handle); public int Length => metadata.GetUserString(handle).Length; public string Value => metadata.GetUserString(handle); public UserStringHeapEntry(MetadataReader metadata, UserStringHandle handle) { this.metadata = metadata; this.handle = handle; } } } } ================================================ FILE: ILSpy/Metadata/Helpers.cs ================================================ // 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 // 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.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Navigation; using DataGridExtensions; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Controls; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf.Interactivity; namespace ICSharpCode.ILSpy.Metadata { static class Helpers { public static DataGrid PrepareDataGrid(TabPageModel tabPage, ILSpyTreeNode selectedNode) { if (!(tabPage.Content is DataGrid view && view.Name == "MetadataView")) { view = new MetaDataGrid() { Name = "MetadataView", GridLinesVisibility = DataGridGridLinesVisibility.None, CanUserAddRows = false, CanUserDeleteRows = false, CanUserReorderColumns = false, HeadersVisibility = DataGridHeadersVisibility.Column, EnableColumnVirtualization = true, EnableRowVirtualization = true, RowHeight = 20, IsReadOnly = true, SelectionMode = DataGridSelectionMode.Single, SelectionUnit = DataGridSelectionUnit.FullRow, SelectedTreeNode = selectedNode, CellStyle = (Style)MetadataTableViews.Instance["DataGridCellStyle"], }; ContextMenuProvider.Add(view); DataGridFilter.SetIsAutoFilterEnabled(view, true); DataGridFilter.SetContentFilterFactory(view, new RegexContentFilterFactory()); AdvancedScrollWheelBehavior.SetAttach(view, AdvancedScrollWheelMode.WithoutAnimation); } DataGridFilter.GetFilter(view).Clear(); view.RowDetailsTemplateSelector = null; view.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed; view.EnableColumnVirtualization = true; view.EnableRowVirtualization = true; ((MetaDataGrid)view).SelectedTreeNode = selectedNode; if (!view.AutoGenerateColumns) view.Columns.Clear(); view.AutoGenerateColumns = true; view.AutoGeneratingColumn += View_AutoGeneratingColumn; view.AutoGeneratedColumns += View_AutoGeneratedColumns; return view; } internal static void View_AutoGeneratedColumns(object sender, EventArgs e) { ((DataGrid)sender).AutoGeneratedColumns -= View_AutoGeneratedColumns; ((DataGrid)sender).AutoGeneratingColumn -= View_AutoGeneratingColumn; } internal static void View_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { var binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay }; e.Column = GetColumn(); switch (e.PropertyName) { case "RID": case "Meaning": e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]); break; case "Token": case "Offset": case "RVA": case "StartOffset": case "Length": binding.StringFormat = "X8"; e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]); break; case "RowDetails": e.Cancel = true; break; case "Value" when e.PropertyDescriptor is PropertyDescriptor dp && dp.ComponentType == typeof(Entry): binding.Path = new PropertyPath("."); binding.Converter = ByteWidthConverter.Instance; break; default: e.Cancel = e.PropertyName.Contains("Tooltip"); if (!e.Cancel) { e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]); } break; } if (!e.Cancel) { ApplyAttributes((PropertyDescriptor)e.PropertyDescriptor, binding, e.Column); } DataGridColumn GetColumn() { if (e.PropertyType == typeof(bool)) { return new DataGridCheckBoxColumn() { Header = e.PropertyName, SortMemberPath = e.PropertyName, Binding = binding }; } var descriptor = (PropertyDescriptor)e.PropertyDescriptor; if (descriptor.Attributes.OfType().Any(c => c.Kind == ColumnKind.Token || c.LinkToTable)) { return new DataGridTemplateColumn() { Header = e.PropertyName, SortMemberPath = e.PropertyName, CellTemplate = GetOrCreateLinkCellTemplate(e.PropertyName, descriptor, binding) }; } return new DataGridTextColumn() { Header = e.PropertyName, SortMemberPath = e.PropertyName, Binding = binding }; } } static readonly Dictionary linkCellTemplates = new Dictionary(); private static DataTemplate GetOrCreateLinkCellTemplate(string name, PropertyDescriptor descriptor, Binding binding) { if (linkCellTemplates.TryGetValue(name, out var template)) { return template; } var tb = new FrameworkElementFactory(typeof(TextBlock)); var hyper = new FrameworkElementFactory(typeof(Hyperlink)); tb.AppendChild(hyper); hyper.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(Hyperlink_Click)); var run = new FrameworkElementFactory(typeof(Run)); hyper.AppendChild(run); run.SetBinding(Run.TextProperty, binding); DataTemplate dataTemplate = new DataTemplate() { VisualTree = tb }; linkCellTemplates.Add(name, dataTemplate); return dataTemplate; void Hyperlink_Click(object sender, RoutedEventArgs e) { var hyperlink = (Hyperlink)sender; var onClickMethod = descriptor.ComponentType.GetMethod("On" + name + "Click", BindingFlags.Instance | BindingFlags.Public); if (onClickMethod != null) { onClickMethod.Invoke(hyperlink.DataContext, Array.Empty()); } } } static void ApplyAttributes(PropertyDescriptor descriptor, Binding binding, DataGridColumn column) { if (descriptor.PropertyType.IsEnum) { binding.Converter = new UnderlyingEnumValueConverter(); string key = descriptor.PropertyType.Name + "Filter"; column.SetTemplate((ControlTemplate)MetadataTableViews.Instance[key]); } var columnInfo = descriptor.Attributes.OfType().FirstOrDefault(); if (columnInfo != null) { binding.StringFormat = columnInfo.Format; if (!descriptor.PropertyType.IsEnum && columnInfo.Format.StartsWith("X", StringComparison.OrdinalIgnoreCase)) { column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["HexFilter"]); } } } [Obsolete("Use safe GetValueLittleEndian(ReadOnlySpan) or appropriate BinaryPrimitives.Read* method")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe int GetValue(byte* ptr, int size) => GetValueLittleEndian(new ReadOnlySpan(ptr, size)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetValueLittleEndian(ReadOnlySpan ptr, int size) => GetValueLittleEndian(ptr.Slice(0, size)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetValueLittleEndian(ReadOnlySpan ptr) { int result = 0; for (int i = 0; i < ptr.Length; i += 2) { result |= ptr[i] << 8 * i; result |= ptr[i + 1] << 8 * (i + 1); } return result; } static Helpers() { rowCounts = typeof(MetadataReader) .GetField("TableRowCounts", BindingFlags.NonPublic | BindingFlags.Instance); computeCodedTokenSize = typeof(MetadataReader) .GetMethod("ComputeCodedTokenSize", BindingFlags.Instance | BindingFlags.NonPublic); fromTypeDefOrRefTag = typeof(TypeDefinitionHandle).Assembly .GetType("System.Reflection.Metadata.Ecma335.TypeDefOrRefTag") .GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic); fromHasFieldMarshalTag = typeof(TypeDefinitionHandle).Assembly .GetType("System.Reflection.Metadata.Ecma335.HasFieldMarshalTag") .GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic); fromMemberForwardedTag = typeof(TypeDefinitionHandle).Assembly .GetType("System.Reflection.Metadata.Ecma335.MemberForwardedTag") .GetMethod("ConvertToHandle", BindingFlags.Static | BindingFlags.NonPublic); } readonly static FieldInfo rowCounts; readonly static MethodInfo computeCodedTokenSize; readonly static MethodInfo fromTypeDefOrRefTag; readonly static MethodInfo fromHasFieldMarshalTag; readonly static MethodInfo fromMemberForwardedTag; public static EntityHandle FromHasFieldMarshalTag(uint tag) { return (EntityHandle)fromHasFieldMarshalTag.Invoke(null, new object[] { tag }); } public static EntityHandle FromMemberForwardedTag(uint tag) { return (EntityHandle)fromMemberForwardedTag.Invoke(null, new object[] { tag }); } public static EntityHandle FromTypeDefOrRefTag(uint tag) { return (EntityHandle)fromTypeDefOrRefTag.Invoke(null, new object[] { tag }); } public static int ComputeCodedTokenSize(this MetadataReader metadata, int largeRowSize, TableMask mask) { return (int)computeCodedTokenSize.Invoke(metadata, new object[] { largeRowSize, rowCounts.GetValue(metadata), (ulong)mask } ); } class UnderlyingEnumValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var t = value.GetType(); if (t.IsEnum) { return (int)CSharpPrimitiveCast.Cast(TypeCode.Int32, value, false); } return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public static string ReadUTF8StringNullTerminated(this ref BlobReader reader) { int length = reader.IndexOf(0); string s = reader.ReadUTF8(length); reader.ReadByte(); return s; } } enum ColumnKind { HeapOffset, Token, Other } [AttributeUsage(AttributeTargets.Property)] class ColumnInfoAttribute : Attribute { public string Format { get; } public ColumnKind Kind { get; set; } public bool LinkToTable { get; set; } public ColumnInfoAttribute(string format) { this.Format = format; } } [Flags] internal enum TableMask : ulong { Module = 0x1, TypeRef = 0x2, TypeDef = 0x4, FieldPtr = 0x8, Field = 0x10, MethodPtr = 0x20, MethodDef = 0x40, ParamPtr = 0x80, Param = 0x100, InterfaceImpl = 0x200, MemberRef = 0x400, Constant = 0x800, CustomAttribute = 0x1000, FieldMarshal = 0x2000, DeclSecurity = 0x4000, ClassLayout = 0x8000, FieldLayout = 0x10000, StandAloneSig = 0x20000, EventMap = 0x40000, EventPtr = 0x80000, Event = 0x100000, PropertyMap = 0x200000, PropertyPtr = 0x400000, Property = 0x800000, MethodSemantics = 0x1000000, MethodImpl = 0x2000000, ModuleRef = 0x4000000, TypeSpec = 0x8000000, ImplMap = 0x10000000, FieldRva = 0x20000000, EnCLog = 0x40000000, EnCMap = 0x80000000, Assembly = 0x100000000, AssemblyRef = 0x800000000, File = 0x4000000000, ExportedType = 0x8000000000, ManifestResource = 0x10000000000, NestedClass = 0x20000000000, GenericParam = 0x40000000000, MethodSpec = 0x80000000000, GenericParamConstraint = 0x100000000000, Document = 0x1000000000000, MethodDebugInformation = 0x2000000000000, LocalScope = 0x4000000000000, LocalVariable = 0x8000000000000, LocalConstant = 0x10000000000000, ImportScope = 0x20000000000000, StateMachineMethod = 0x40000000000000, CustomDebugInformation = 0x80000000000000, PtrTables = 0x4800A8, EncTables = 0xC0000000, TypeSystemTables = 0x1FC9FFFFFFFF, DebugTables = 0xFF000000000000, AllTables = 0xFF1FC9FFFFFFFF, ValidPortablePdbExternalTables = 0x1FC93FB7FF57 } } ================================================ FILE: ILSpy/Metadata/HexFilterControl.xaml ================================================ ================================================ FILE: ILSpy/Metadata/HexFilterControl.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using DataGridExtensions; namespace ICSharpCode.ILSpy.Metadata { /// /// Interaction logic for HexFilterControl.xaml /// public partial class HexFilterControl { TextBox textBox; public HexFilterControl() { InitializeComponent(); } public override void OnApplyTemplate() { base.OnApplyTemplate(); textBox = Template.FindName("textBox", this) as TextBox; } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { var text = ((TextBox)sender)?.Text; Filter = new ContentFilter(text); } public IContentFilter Filter { get { return (IContentFilter)GetValue(FilterProperty); } set { SetValue(FilterProperty, value); } } /// /// Identifies the Filter dependency property /// public static readonly DependencyProperty FilterProperty = DependencyProperty.Register("Filter", typeof(IContentFilter), typeof(HexFilterControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (o, args) => ((HexFilterControl)o).Filter_Changed(args.NewValue))); void Filter_Changed(object newValue) { var textBox = this.textBox; if (textBox == null) return; textBox.Text = (newValue as ContentFilter)?.Value ?? string.Empty; } class ContentFilter : IContentFilter { readonly string filter; public ContentFilter(string filter) { this.filter = filter; } public bool IsMatch(object value) { if (string.IsNullOrWhiteSpace(filter)) return true; if (value == null) return false; return string.Format("{0:x8}", value).IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0; } public string Value => filter; } } } ================================================ FILE: ILSpy/Metadata/MetaDataGrid.cs ================================================ // 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 // 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.Windows; using System.Windows.Controls; using System.Windows.Media; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { class MetaDataGrid : DataGrid, IHaveState { private readonly MouseHoverLogic hoverLogic; private ToolTip toolTip; public ILSpyTreeNode SelectedTreeNode { get; set; } public MetaDataGrid() { this.hoverLogic = new MouseHoverLogic(this); this.hoverLogic.MouseHover += HoverLogic_MouseHover; this.hoverLogic.MouseHoverStopped += HoverLogic_MouseHoverStopped; } private void HoverLogic_MouseHoverStopped(object sender, System.Windows.Input.MouseEventArgs e) { // Non-popup tooltips get closed as soon as the mouse starts moving again if (toolTip != null) { toolTip.IsOpen = false; e.Handled = true; } } private void HoverLogic_MouseHover(object sender, System.Windows.Input.MouseEventArgs e) { var position = e.GetPosition(this); var hit = VisualTreeHelper.HitTest(this, position); if (hit == null) { return; } var cell = hit.VisualHit.GetParent(); if (cell == null) return; var data = cell.DataContext; var name = (string)cell.Column.Header; if (toolTip == null) { toolTip = new ToolTip(); toolTip.Closed += ToolTipClosed; } toolTip.PlacementTarget = this; // required for property inheritance var pi = data?.GetType().GetProperty(name + "Tooltip"); if (pi == null) return; object tooltip = pi.GetValue(data); if (tooltip is string s) { if (string.IsNullOrWhiteSpace(s)) return; toolTip.Content = new TextBlock { Text = s, TextWrapping = TextWrapping.Wrap }; } else if (tooltip != null) { toolTip.Content = tooltip; } else { return; } e.Handled = true; toolTip.IsOpen = true; } private void ToolTipClosed(object sender, RoutedEventArgs e) { if (toolTip == sender) { toolTip = null; } } public ViewState GetState() { return new ViewState { DecompiledNodes = SelectedTreeNode == null ? null : new HashSet(new[] { SelectedTreeNode }) }; } } } ================================================ FILE: ILSpy/Metadata/MetadataHeapTreeNode.cs ================================================ // 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 // 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.Reflection.Metadata; using System.Windows.Controls; using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { internal abstract class MetadataHeapTreeNode : ILSpyTreeNode { protected MetadataFile metadataFile; protected int scrollTarget; public HandleKind Kind { get; } public override object NavigationText => $"{Text} ({metadataFile.Name})"; public override object Icon => Images.Heap; public MetadataHeapTreeNode(HandleKind kind, MetadataFile metadataFile) { this.Kind = kind; this.metadataFile = metadataFile; } protected void ScrollItemIntoView(DataGrid view, object item) { view.Loaded += View_Loaded; view.Dispatcher.BeginInvoke(() => view.SelectItem(item), DispatcherPriority.Background); } private void View_Loaded(object sender, System.Windows.RoutedEventArgs e) { DataGrid view = (DataGrid)sender; var sv = view.FindVisualChild(); sv.ScrollToVerticalOffset(scrollTarget - 1); view.Loaded -= View_Loaded; this.scrollTarget = default; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { } } } ================================================ FILE: ILSpy/Metadata/MetadataProtocolHandler.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { [Export(typeof(IProtocolHandler))] [Shared] class MetadataProtocolHandler(AssemblyTreeModel assemblyTreeModel) : IProtocolHandler { public ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage) { newTabPage = true; if (protocol != "metadata") return null; var assemblyTreeNode = assemblyTreeModel.FindTreeNode(module) as AssemblyTreeNode; if (assemblyTreeNode == null) return null; var mxNode = assemblyTreeNode.Children.OfType().FirstOrDefault(); if (mxNode != null) { mxNode.EnsureLazyChildren(); var node = mxNode.FindNodeByHandleKind(handle.Kind); node?.ScrollTo(handle); return node; } return null; } } } ================================================ FILE: ILSpy/Metadata/MetadataTableTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Windows.Controls; using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { internal abstract class MetadataTableTreeNode : ILSpyTreeNode { protected readonly MetadataFile metadataFile; protected int scrollTarget; public TableIndex Kind { get; } public override object Text => $"{(int)Kind:X2} {Kind} ({metadataFile.Metadata.GetTableRowCount(Kind)})"; public override object NavigationText => $"{(int)Kind:X2} {Kind} ({metadataFile.Name})"; public override object Icon => Images.MetadataTable; public MetadataTableTreeNode(TableIndex table, MetadataFile metadataFile) { this.Kind = table; this.metadataFile = metadataFile; } internal void ScrollTo(Handle handle) { this.scrollTarget = metadataFile.Metadata.GetRowNumber((EntityHandle)handle); } protected void ScrollRowIntoView(DataGrid view, int row) { if (!view.IsLoaded) { view.Loaded += View_Loaded; } else { View_Loaded(view, new System.Windows.RoutedEventArgs()); } if (view.Items.Count > row && row >= 0) view.Dispatcher.BeginInvoke(() => view.SelectItem(view.Items[row]), DispatcherPriority.Background); } private void View_Loaded(object sender, System.Windows.RoutedEventArgs e) { DataGrid view = (DataGrid)sender; var sv = view.FindVisualChild(); sv.ScrollToVerticalOffset(scrollTarget - 1); view.Loaded -= View_Loaded; this.scrollTarget = default; } protected static string GenerateTooltip(ref string tooltip, MetadataFile module, EntityHandle handle) { if (tooltip == null) { if (handle.IsNil) { return null; } ITextOutput output = new PlainTextOutput(); var context = new MetadataGenericContext(default(TypeDefinitionHandle), module.Metadata); var metadata = module.Metadata; switch (handle.Kind) { case HandleKind.ModuleDefinition: output.Write(metadata.GetString(metadata.GetModuleDefinition().Name)); output.Write(" (this module)"); break; case HandleKind.ModuleReference: ModuleReference moduleReference = metadata.GetModuleReference((ModuleReferenceHandle)handle); output.Write(metadata.GetString(moduleReference.Name)); break; case HandleKind.AssemblyReference: var asmRef = new Decompiler.Metadata.AssemblyReference(metadata, (AssemblyReferenceHandle)handle); output.Write(asmRef.ToString()); break; case HandleKind.Parameter: var param = metadata.GetParameter((ParameterHandle)handle); output.Write(param.SequenceNumber + " - " + metadata.GetString(param.Name)); break; case HandleKind.EventDefinition: var @event = metadata.GetEventDefinition((EventDefinitionHandle)handle); output.Write(metadata.GetString(@event.Name)); break; case HandleKind.PropertyDefinition: var prop = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); output.Write(metadata.GetString(prop.Name)); break; case HandleKind.AssemblyDefinition: var ad = metadata.GetAssemblyDefinition(); output.Write(metadata.GetString(ad.Name)); output.Write(" (this assembly)"); break; case HandleKind.AssemblyFile: var af = metadata.GetAssemblyFile((AssemblyFileHandle)handle); output.Write(metadata.GetString(af.Name)); break; case HandleKind.GenericParameter: var gp = metadata.GetGenericParameter((GenericParameterHandle)handle); output.Write(metadata.GetString(gp.Name)); break; case HandleKind.ManifestResource: var mfr = metadata.GetManifestResource((ManifestResourceHandle)handle); output.Write(metadata.GetString(mfr.Name)); break; case HandleKind.Document: var doc = metadata.GetDocument((DocumentHandle)handle); output.Write(metadata.GetString(doc.Name)); break; default: handle.WriteTo(module, output, context); break; } tooltip = "(" + handle.Kind + ") " + output.ToString(); } return tooltip; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { } } internal abstract class MetadataTableTreeNode : MetadataTableTreeNode where TEntry : struct { public MetadataTableTreeNode(TableIndex kind, MetadataFile metadataFile) : base(kind, metadataFile) { } protected abstract IReadOnlyList LoadTable(); protected virtual void ConfigureDataGrid(DataGrid view) { } public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); ConfigureDataGrid(view); view.ItemsSource = LoadTable(); tabPage.Content = view; ScrollRowIntoView(view, scrollTarget); return true; } } internal abstract class DebugMetadataTableTreeNode : MetadataTableTreeNode where TEntry : struct { public DebugMetadataTableTreeNode(TableIndex kind, MetadataFile metadataFile) : base(kind, metadataFile) { } } internal class UnsupportedMetadataTableTreeNode : MetadataTableTreeNode { public UnsupportedMetadataTableTreeNode(TableIndex kind, MetadataFile file) : base(kind, file) { } public override object Text => $"{(int)Kind:X2} {Kind.ToString()} [unsupported] ({metadataFile.Metadata.GetTableRowCount(Kind)})"; public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { output.WriteLine($"Unsupported table '{(int)Kind:X2} {Kind}' contains {metadataFile.Metadata.GetTableRowCount(Kind)} rows."); } } } ================================================ FILE: ILSpy/Metadata/MetadataTableViews.xaml ================================================ ================================================ FILE: ILSpy/Metadata/MetadataTableViews.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace ICSharpCode.ILSpy.Metadata { /// /// Interaction logic for MetadataTableViews.xaml /// public partial class MetadataTableViews : ResourceDictionary { public MetadataTableViews() { InitializeComponent(); } static MetadataTableViews instance; public static MetadataTableViews Instance { get { if (instance == null) { instance = new MetadataTableViews(); } return instance; } } private void DataGrid_AutoGeneratedColumns(object sender, EventArgs e) { Helpers.View_AutoGeneratedColumns(sender, e); } private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { Helpers.View_AutoGeneratingColumn(sender, e); } } } ================================================ FILE: ILSpy/Metadata/MetadataTablesTreeNode.cs ================================================ // Copyright (c) 2021 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 // 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.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { class MetadataTablesTreeNode : ILSpyTreeNode { readonly MetadataFile metadataFile; public MetadataTablesTreeNode(MetadataFile metadataFile) { this.metadataFile = metadataFile; this.LazyLoading = true; } public override object Text => "Tables"; public override object NavigationText => $"{Text} ({metadataFile.Name})"; public override object Icon => Images.MetadataTableGroup; protected override void LoadChildren() { foreach (var table in Enum.GetValues()) { if (ShowTable(table, metadataFile.Metadata)) this.Children.Add(CreateTableTreeNode(table, metadataFile)); } } internal static bool ShowTable(TableIndex table, MetadataReader metadata) => !SettingsService.DisplaySettings.HideEmptyMetadataTables || metadata.GetTableRowCount(table) > 0; internal static MetadataTableTreeNode CreateTableTreeNode(TableIndex table, MetadataFile metadataFile) { switch (table) { case TableIndex.Module: return new ModuleTableTreeNode(metadataFile); case TableIndex.TypeRef: return new TypeRefTableTreeNode(metadataFile); case TableIndex.TypeDef: return new TypeDefTableTreeNode(metadataFile); case TableIndex.Field: return new FieldTableTreeNode(metadataFile); case TableIndex.MethodDef: return new MethodTableTreeNode(metadataFile); case TableIndex.Param: return new ParamTableTreeNode(metadataFile); case TableIndex.InterfaceImpl: return new InterfaceImplTableTreeNode(metadataFile); case TableIndex.MemberRef: return new MemberRefTableTreeNode(metadataFile); case TableIndex.Constant: return new ConstantTableTreeNode(metadataFile); case TableIndex.CustomAttribute: return new CustomAttributeTableTreeNode(metadataFile); case TableIndex.FieldMarshal: return new FieldMarshalTableTreeNode(metadataFile); case TableIndex.DeclSecurity: return new DeclSecurityTableTreeNode(metadataFile); case TableIndex.ClassLayout: return new ClassLayoutTableTreeNode(metadataFile); case TableIndex.FieldLayout: return new FieldLayoutTableTreeNode(metadataFile); case TableIndex.StandAloneSig: return new StandAloneSigTableTreeNode(metadataFile); case TableIndex.EventMap: return new EventMapTableTreeNode(metadataFile); case TableIndex.Event: return new EventTableTreeNode(metadataFile); case TableIndex.PropertyMap: return new PropertyMapTableTreeNode(metadataFile); case TableIndex.Property: return new PropertyTableTreeNode(metadataFile); case TableIndex.MethodSemantics: return new MethodSemanticsTableTreeNode(metadataFile); case TableIndex.MethodImpl: return new MethodImplTableTreeNode(metadataFile); case TableIndex.ModuleRef: return new ModuleRefTableTreeNode(metadataFile); case TableIndex.TypeSpec: return new TypeSpecTableTreeNode(metadataFile); case TableIndex.ImplMap: return new ImplMapTableTreeNode(metadataFile); case TableIndex.FieldRva: return new FieldRVATableTreeNode(metadataFile); case TableIndex.Assembly: return new AssemblyTableTreeNode(metadataFile); case TableIndex.AssemblyRef: return new AssemblyRefTableTreeNode(metadataFile); case TableIndex.File: return new FileTableTreeNode(metadataFile); case TableIndex.ExportedType: return new ExportedTypeTableTreeNode(metadataFile); case TableIndex.ManifestResource: return new ManifestResourceTableTreeNode(metadataFile); case TableIndex.NestedClass: return new NestedClassTableTreeNode(metadataFile); case TableIndex.GenericParam: return new GenericParamTableTreeNode(metadataFile); case TableIndex.MethodSpec: return new MethodSpecTableTreeNode(metadataFile); case TableIndex.GenericParamConstraint: return new GenericParamConstraintTableTreeNode(metadataFile); case TableIndex.Document: return new DocumentTableTreeNode(metadataFile); case TableIndex.MethodDebugInformation: return new MethodDebugInformationTableTreeNode(metadataFile); case TableIndex.LocalScope: return new LocalScopeTableTreeNode(metadataFile); case TableIndex.LocalVariable: return new LocalVariableTableTreeNode(metadataFile); case TableIndex.LocalConstant: return new LocalConstantTableTreeNode(metadataFile); case TableIndex.ImportScope: return new ImportScopeTableTreeNode(metadataFile); case TableIndex.StateMachineMethod: return new StateMachineMethodTableTreeNode(metadataFile); case TableIndex.CustomDebugInformation: return new CustomDebugInformationTableTreeNode(metadataFile); case TableIndex.FieldPtr: case TableIndex.EventPtr: case TableIndex.MethodPtr: case TableIndex.ParamPtr: case TableIndex.PropertyPtr: return new PtrTableTreeNode(table, metadataFile); default: return new UnsupportedMetadataTableTreeNode(table, metadataFile); } } public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; return false; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "Metadata Tables"); } } } ================================================ FILE: ILSpy/Metadata/MetadataTreeNode.cs ================================================ // 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 // 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.Globalization; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Windows.Data; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata { class MetadataTreeNode : ILSpyTreeNode { private readonly MetadataFile metadataFile; private readonly string title; public MetadataTreeNode(MetadataFile module, string title) { this.metadataFile = module; this.title = title; this.LazyLoading = true; } public override object Text => title; public override object NavigationText => $"{Text} ({metadataFile.Name})"; public override object Icon => Images.Metadata; public override bool View(TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; return false; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, title); DumpMetadataInfo(language, output, this.metadataFile.Metadata); } internal static void DumpMetadataInfo(Language language, ITextOutput output, MetadataReader metadata) { language.WriteCommentLine(output, "MetadataKind: " + metadata.MetadataKind); language.WriteCommentLine(output, "MetadataVersion: " + metadata.MetadataVersion); if (metadata.DebugMetadataHeader is { } header) { output.WriteLine(); language.WriteCommentLine(output, "Header:"); language.WriteCommentLine(output, "Id: " + header.Id.ToHexString(header.Id.Length)); language.WriteCommentLine(output, "EntryPoint: " + MetadataTokens.GetToken(header.EntryPoint).ToString("X8")); } output.WriteLine(); language.WriteCommentLine(output, "Tables:"); foreach (var table in Enum.GetValues()) { int count = metadata.GetTableRowCount(table); if (count > 0) { language.WriteCommentLine(output, $"{(byte)table:X2} {table}: {count} rows"); } } } protected override void LoadChildren() { if (metadataFile is PEFile module) { this.Children.Add(new DosHeaderTreeNode(module)); this.Children.Add(new CoffHeaderTreeNode(module)); this.Children.Add(new OptionalHeaderTreeNode(module)); this.Children.Add(new DataDirectoriesTreeNode(module)); this.Children.Add(new DebugDirectoryTreeNode(module)); } this.Children.Add(new MetadataTablesTreeNode(metadataFile)); this.Children.Add(new StringHeapTreeNode(metadataFile)); this.Children.Add(new UserStringHeapTreeNode(metadataFile)); this.Children.Add(new GuidHeapTreeNode(metadataFile)); this.Children.Add(new BlobHeapTreeNode(metadataFile)); } public MetadataTableTreeNode FindNodeByHandleKind(HandleKind kind) { return this.Children.OfType().Single() .Children.OfType().SingleOrDefault(x => x.Kind == (TableIndex)kind); } } class Entry { public string Member { get; } public int Offset { get; } public int Size { get; } public object Value { get; } public string Meaning { get; } public IList RowDetails { get; } public Entry(int offset, object value, int size, string member, string meaning, IList rowDetails = null) { this.Member = member; this.Offset = offset; this.Size = size; this.Value = value; this.Meaning = meaning; this.RowDetails = rowDetails; } } class BitEntry { public bool Value { get; } public string Meaning { get; } public BitEntry(bool value, string meaning) { this.Value = value; this.Meaning = meaning; } } class ByteWidthConverter : IValueConverter { public static readonly ByteWidthConverter Instance = new ByteWidthConverter(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return string.Format("{0:X" + 2 * ((Entry)value).Size + "}", ((Entry)value).Value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } } ================================================ FILE: ILSpy/Metadata/OptionalHeaderTreeNode.cs ================================================ // 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 // 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.Collections.Generic; using System.Reflection.PortableExecutable; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX.Extensions; using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy.Metadata { class OptionalHeaderTreeNode : ILSpyTreeNode { private PEFile module; public OptionalHeaderTreeNode(PEFile module) { this.module = module; } public override object Text => "Optional Header"; public override object NavigationText => $"{Text} ({module.Name})"; public override object Icon => Images.Header; public override bool View(ViewModels.TabPageModel tabPage) { tabPage.Title = Text.ToString(); tabPage.SupportsLanguageSwitching = false; var dataGrid = Helpers.PrepareDataGrid(tabPage, this); dataGrid.RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector("DLL Characteristics"); dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed; dataGrid.Columns.Clear(); dataGrid.AutoGenerateColumns = false; dataGrid.Columns.AddRange( new[] { new DataGridTextColumn { IsReadOnly = true, Header = "Member", Binding = new Binding("Member") }, new DataGridTextColumn { IsReadOnly = true, Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8" } }, new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") }, new DataGridTextColumn { IsReadOnly = true, Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance } }, new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") } } ); var headers = module.Reader.PEHeaders; var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128); var header = headers.PEHeader; var isPE32Plus = (header.Magic == PEMagic.PE32Plus); var entries = new List(); ushort dllCharacteristics; Entry characteristics; entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Magic", header.Magic.ToString())); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadByte(), 1, "Major Linker Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadByte(), 1, "Minor Linker Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Code Size", "Size of the code (text) section, or the sum of all code sections if there are multiple sections.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Initialized Data Size", "Size of the initialized data section, or the sum of all initialized data sections if there are multiple data sections.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Uninitialized Data Size", "Size of the uninitialized data section, or the sum of all uninitialized data sections if there are multiple uninitialized data sections.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Entry Point RVA", "RVA of entry point, needs to point to bytes 0xFF 0x25 followed by the RVA in a section marked execute / read for EXEs or 0 for DLLs")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Base Of Code", "RVA of the code section.")); entries.Add(new Entry(isPE32Plus ? 0 : headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? 0UL : reader.ReadUInt32(), isPE32Plus ? 0 : 4, "Base Of Data", "PE32 only (not present in PE32Plus): RVA of the data section, relative to the Image Base.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Image Base", "Shall be a multiple of 0x10000.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Section Alignment", "Shall be greater than File Alignment.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "File Alignment", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Major OS Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Minor OS Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Major Image Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Minor Image Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Major Subsystem Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Minor Subsystem Version", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt32(), 4, "Win32VersionValue", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Image Size", "Size, in bytes, of image, including all headers and padding; shall be a multiple of Section Alignment.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Header Size", "Combined size of MS-DOS Header, PE Header, PE Optional Header and padding; shall be a multiple of the file alignment.")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "File Checksum", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Subsystem", header.Subsystem.ToString())); entries.Add(characteristics = new Entry(headers.PEHeaderStartOffset + reader.Offset, dllCharacteristics = reader.ReadUInt16(), 2, "DLL Characteristics", header.DllCharacteristics.ToString(), new[] { new BitEntry((dllCharacteristics & 0x0001) != 0, "<0001> Process Init (Reserved)"), new BitEntry((dllCharacteristics & 0x0002) != 0, "<0002> Process Term (Reserved)"), new BitEntry((dllCharacteristics & 0x0004) != 0, "<0004> Thread Init (Reserved)"), new BitEntry((dllCharacteristics & 0x0008) != 0, "<0008> Thread Term (Reserved)"), new BitEntry((dllCharacteristics & 0x0010) != 0, "<0010> Unused"), new BitEntry((dllCharacteristics & 0x0020) != 0, "<0020> Image can handle a high entropy 64-bit virtual address space (ASLR)"), new BitEntry((dllCharacteristics & 0x0040) != 0, "<0040> DLL can be relocated at load time"), new BitEntry((dllCharacteristics & 0x0080) != 0, "<0080> Code integrity checks are enforced"), new BitEntry((dllCharacteristics & 0x0100) != 0, "<0100> Image is NX compatible"), new BitEntry((dllCharacteristics & 0x0200) != 0, "<0200> Isolation aware, but do not isolate the image"), new BitEntry((dllCharacteristics & 0x0400) != 0, "<0400> Does not use structured exception handling (SEH)"), new BitEntry((dllCharacteristics & 0x0800) != 0, "<0800> Do not bind the image"), new BitEntry((dllCharacteristics & 0x1000) != 0, "<1000> Image must execute in an AppContainer"), new BitEntry((dllCharacteristics & 0x2000) != 0, "<2000> Driver is a WDM Driver"), new BitEntry((dllCharacteristics & 0x4000) != 0, "<4000> Image supports Control Flow Guard"), new BitEntry((dllCharacteristics & 0x8000) != 0, "<8000> Image is Terminal Server aware"), })); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Stack Reserve Size", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Stack Commit Size", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Heap Reserve Size", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Heap Commit Size", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt32(), 4, "Loader Flags", "")); entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Number of Data Directories", "")); dataGrid.ItemsSource = entries; dataGrid.SetDetailsVisibilityForItem(characteristics, Visibility.Visible); tabPage.Content = dataGrid; return true; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "Optional Header"); } } } ================================================ FILE: ILSpy/NativeMethods.cs ================================================ // 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 // 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.Runtime.InteropServices; namespace ICSharpCode.ILSpy { // Uses https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke-source-generation internal static partial class NativeMethods { const int S_OK = 0; [LibraryImport("user32.dll")] internal static partial int GetDoubleClickTime(); [LibraryImport("dwmapi.dll")] internal static partial int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttribute attr, ref int attrValue, int attrSize); public static bool UseImmersiveDarkMode(IntPtr hWnd, bool enable) { int darkMode = enable ? 1 : 0; int hResult = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, ref darkMode, sizeof(int)); return hResult > S_OK; } } public enum DwmWindowAttribute : uint { NCRenderingEnabled = 1, NCRenderingPolicy, TransitionsForceDisabled, AllowNCPaint, CaptionButtonBounds, NonClientRtlLayout, ForceIconicRepresentation, Flip3DPolicy, ExtendedFrameBounds, HasIconicBitmap, DisallowPeek, ExcludedFromPeek, Cloak, Cloaked, FreezeRepresentation, PassiveUpdateMode, UseHostBackdropBrush, UseImmersiveDarkMode = 20, WindowCornerPreference = 33, BorderColor, CaptionColor, TextColor, VisibleFrameBorderThickness, SystemBackdropType, Last } } ================================================ FILE: ILSpy/NavigationHistory.cs ================================================ // 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 // 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; namespace ICSharpCode.ILSpy { /// /// Stores the navigation history. /// internal sealed class NavigationHistory where T : class, IEquatable { private const double NavigationSecondsBeforeNewEntry = 0.5; private DateTime lastNavigationTime = DateTime.MinValue; T current; List back = new List(); List forward = new List(); public T[] BackList => back.ToArray(); public T[] ForwardList => forward.ToArray(); public bool CanNavigateBack { get { return back.Count > 0; } } public bool CanNavigateForward { get { return forward.Count > 0; } } public T GoBack() { forward.Add(current); current = back[back.Count - 1]; back.RemoveAt(back.Count - 1); return current; } public T GoForward() { back.Add(current); current = forward[forward.Count - 1]; forward.RemoveAt(forward.Count - 1); return current; } public void RemoveAll(Predicate predicate) { back.RemoveAll(predicate); forward.RemoveAll(predicate); } public void Clear() { back.Clear(); forward.Clear(); } public void UpdateCurrent(T node) { current = node; } public void Record(T node) { var navigationTime = DateTime.Now; var period = navigationTime - lastNavigationTime; if (period.TotalSeconds < NavigationSecondsBeforeNewEntry) { current = node; } else { if (current != null) back.Add(current); // We only store a record once, and ensure it is on the top of the stack, so we just remove the old record back.Remove(node); current = node; } forward.Clear(); lastNavigationTime = navigationTime; } } } ================================================ FILE: ILSpy/NavigationState.cs ================================================ // 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 // 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 ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy { [DebuggerDisplay("Nodes = {treeNodes.Count}, State = [{ViewState}]")] public class NavigationState : IEquatable { private readonly HashSet treeNodes; public IEnumerable TreeNodes => treeNodes; public ViewState ViewState { get; private set; } public TabPageModel TabPage { get; private set; } public NavigationState(TabPageModel tabPage, ViewState viewState) { this.TabPage = tabPage; this.treeNodes = new HashSet((IEnumerable)viewState.DecompiledNodes ?? Array.Empty()); ViewState = viewState; } public NavigationState(TabPageModel tabPage, IEnumerable treeNodes) { this.TabPage = tabPage; this.treeNodes = new HashSet(treeNodes); } public bool Equals(NavigationState other) { if (!this.treeNodes.SetEquals(other.treeNodes)) return false; if (object.ReferenceEquals(this.ViewState, other.ViewState)) return true; if (this.ViewState == null) return false; return this.ViewState.Equals(other.ViewState); } public object NavigationText { get { if (this.treeNodes.Count == 1) return this.treeNodes.First(); if (this.treeNodes.Count > 0) return string.Join(", ", treeNodes.Select(tn => tn.NavigationText)); if (this.ViewState?.ViewedUri is Uri uri) return uri; return ToString(); } } } } ================================================ FILE: ILSpy/Options/DecompilerSettingsPanel.xaml ================================================ ================================================ FILE: ILSpy/Options/DecompilerSettingsPanel.xaml.cs ================================================ // 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 // 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.Composition; using System.Xml.Linq; using ICSharpCode.ILSpyX.Settings; using TomsToolbox.Wpf.Composition.AttributedModel; namespace ICSharpCode.ILSpy.Options { /// /// Interaction logic for DecompilerSettingsPanel.xaml /// [DataTemplate(typeof(DecompilerSettingsViewModel))] [NonShared] internal partial class DecompilerSettingsPanel { public DecompilerSettingsPanel() { InitializeComponent(); } } } ================================================ FILE: ILSpy/Options/DecompilerSettingsViewModel.cs ================================================ // 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 // 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.Collections.Generic; using System.ComponentModel; using System.Composition; using System.Linq; using System.Reflection; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX.Settings; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Options { [ExportOptionPage(Order = 10)] [NonShared] public sealed class DecompilerSettingsViewModel : ObservableObjectBase, IOptionPage { private static readonly PropertyInfo[] propertyInfos = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false) .ToArray(); public string Title => Resources.Decompiler; private DecompilerSettingsGroupViewModel[] settings; public DecompilerSettingsGroupViewModel[] Settings { get => settings; set => SetProperty(ref settings, value); } private DecompilerSettings decompilerSettings; public void Load(SettingsSnapshot snapshot) { decompilerSettings = snapshot.GetSettings(); LoadSettings(); } private void LoadSettings() { this.Settings = propertyInfos .Select(p => new DecompilerSettingsItemViewModel(p, decompilerSettings)) .OrderBy(item => item.Category, NaturalStringComparer.Instance) .GroupBy(p => p.Category) .Select(g => new DecompilerSettingsGroupViewModel(g.Key, g.OrderBy(i => i.Description).ToArray())) .ToArray(); } public void LoadDefaults() { var defaults = new Decompiler.DecompilerSettings(); foreach (var propertyInfo in propertyInfos) { propertyInfo.SetValue(decompilerSettings, propertyInfo.GetValue(defaults)); } LoadSettings(); } } public sealed class DecompilerSettingsGroupViewModel : ObservableObjectBase { private bool? areAllItemsChecked; public DecompilerSettingsGroupViewModel(string category, DecompilerSettingsItemViewModel[] settings) { Settings = settings; Category = category; areAllItemsChecked = GetAreAllItemsChecked(Settings); foreach (DecompilerSettingsItemViewModel viewModel in settings) { viewModel.PropertyChanged += Item_PropertyChanged; } } public bool? AreAllItemsChecked { get => areAllItemsChecked; set { SetProperty(ref areAllItemsChecked, value); if (!value.HasValue) return; foreach (var setting in Settings) { setting.IsEnabled = value.Value; } } } public string Category { get; } public DecompilerSettingsItemViewModel[] Settings { get; } private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(DecompilerSettingsItemViewModel.IsEnabled)) { AreAllItemsChecked = GetAreAllItemsChecked(Settings); } } private static bool? GetAreAllItemsChecked(ICollection settings) { var numberOfEnabledItems = settings.Count(item => item.IsEnabled); if (numberOfEnabledItems == settings.Count) return true; if (numberOfEnabledItems == 0) return false; return null; } } public sealed class DecompilerSettingsItemViewModel(PropertyInfo property, DecompilerSettings decompilerSettings) : ObservableObjectBase { private bool isEnabled = property.GetValue(decompilerSettings) is true; public PropertyInfo Property => property; public bool IsEnabled { get => isEnabled; set { if (SetProperty(ref isEnabled, value)) { property.SetValue(decompilerSettings, value); } } } public string Description { get; set; } = GetResourceString(property.GetCustomAttribute()?.Description ?? property.Name); public string Category { get; set; } = GetResourceString(property.GetCustomAttribute()?.Category ?? Resources.Other); private static string GetResourceString(string key) { var str = !string.IsNullOrEmpty(key) ? Resources.ResourceManager.GetString(key) : null; return string.IsNullOrEmpty(key) || string.IsNullOrEmpty(str) ? key : str; } } } ================================================ FILE: ILSpy/Options/DisplaySettings.cs ================================================ // 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 // 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.Windows.Media; using System.Xml.Linq; using ICSharpCode.ILSpyX.Settings; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.Options { /// /// Description of DisplaySettings. /// public class DisplaySettings : ObservableObjectBase, ISettingsSection { FontFamily selectedFont; public FontFamily SelectedFont { get => selectedFont; set => SetProperty(ref selectedFont, value); } double selectedFontSize; public double SelectedFontSize { get => selectedFontSize; set => SetProperty(ref selectedFontSize, value); } bool showLineNumbers; public bool ShowLineNumbers { get => showLineNumbers; set => SetProperty(ref showLineNumbers, value); } bool showMetadataTokens; public bool ShowMetadataTokens { get => showMetadataTokens; set => SetProperty(ref showMetadataTokens, value); } bool showMetadataTokensInBase10; public bool ShowMetadataTokensInBase10 { get => showMetadataTokensInBase10; set => SetProperty(ref showMetadataTokensInBase10, value); } bool enableWordWrap; public bool EnableWordWrap { get => enableWordWrap; set => SetProperty(ref enableWordWrap, value); } bool sortResults; public bool SortResults { get => sortResults; set => SetProperty(ref sortResults, value); } bool foldBraces; public bool FoldBraces { get => foldBraces; set => SetProperty(ref foldBraces, value); } bool expandMemberDefinitions; public bool ExpandMemberDefinitions { get => expandMemberDefinitions; set => SetProperty(ref expandMemberDefinitions, value); } bool expandUsingDeclarations; public bool ExpandUsingDeclarations { get => expandUsingDeclarations; set => SetProperty(ref expandUsingDeclarations, value); } bool showDebugInfo; public bool ShowDebugInfo { get => showDebugInfo; set => SetProperty(ref showDebugInfo, value); } bool indentationUseTabs; public bool IndentationUseTabs { get => indentationUseTabs; set => SetProperty(ref indentationUseTabs, value); } int indentationTabSize; public int IndentationTabSize { get => indentationTabSize; set => SetProperty(ref indentationTabSize, value); } int indentationSize; public int IndentationSize { get => indentationSize; set => SetProperty(ref indentationSize, value); } bool highlightMatchingBraces; public bool HighlightMatchingBraces { get => highlightMatchingBraces; set => SetProperty(ref highlightMatchingBraces, value); } bool highlightCurrentLine; public bool HighlightCurrentLine { get => highlightCurrentLine; set => SetProperty(ref highlightCurrentLine, value); } bool hideEmptyMetadataTables; public bool HideEmptyMetadataTables { get => hideEmptyMetadataTables; set => SetProperty(ref hideEmptyMetadataTables, value); } bool useNestedNamespaceNodes; public bool UseNestedNamespaceNodes { get => useNestedNamespaceNodes; set => SetProperty(ref useNestedNamespaceNodes, value); } private bool styleWindowTitleBar; public bool StyleWindowTitleBar { get => styleWindowTitleBar; set => SetProperty(ref styleWindowTitleBar, value); } private bool showRawOffsetsAndBytesBeforeInstruction; public bool ShowRawOffsetsAndBytesBeforeInstruction { get => showRawOffsetsAndBytesBeforeInstruction; set => SetProperty(ref showRawOffsetsAndBytesBeforeInstruction, value); } private bool enableSmoothScrolling; public bool EnableSmoothScrolling { get => enableSmoothScrolling; set => SetProperty(ref enableSmoothScrolling, value); } private bool decodeCustomAttributeBlobs; public bool DecodeCustomAttributeBlobs { get => decodeCustomAttributeBlobs; set => SetProperty(ref decodeCustomAttributeBlobs, value); } public XName SectionName => "DisplaySettings"; public void LoadFromXml(XElement section) { SelectedFont = new FontFamily((string)section.Attribute("Font") ?? "Consolas"); SelectedFontSize = (double?)section.Attribute("FontSize") ?? 10.0 * 4 / 3; ShowLineNumbers = (bool?)section.Attribute("ShowLineNumbers") ?? false; ShowMetadataTokens = (bool?)section.Attribute("ShowMetadataTokens") ?? false; ShowMetadataTokensInBase10 = (bool?)section.Attribute("ShowMetadataTokensInBase10") ?? false; ShowDebugInfo = (bool?)section.Attribute("ShowDebugInfo") ?? false; EnableWordWrap = (bool?)section.Attribute("EnableWordWrap") ?? false; SortResults = (bool?)section.Attribute("SortResults") ?? true; FoldBraces = (bool?)section.Attribute("FoldBraces") ?? false; ExpandMemberDefinitions = (bool?)section.Attribute("ExpandMemberDefinitions") ?? false; ExpandUsingDeclarations = (bool?)section.Attribute("ExpandUsingDeclarations") ?? false; IndentationUseTabs = (bool?)section.Attribute("IndentationUseTabs") ?? true; IndentationSize = (int?)section.Attribute("IndentationSize") ?? 4; IndentationTabSize = (int?)section.Attribute("IndentationTabSize") ?? 4; HighlightMatchingBraces = (bool?)section.Attribute("HighlightMatchingBraces") ?? true; HighlightCurrentLine = (bool?)section.Attribute("HighlightCurrentLine") ?? false; HideEmptyMetadataTables = (bool?)section.Attribute("HideEmptyMetadataTables") ?? true; UseNestedNamespaceNodes = (bool?)section.Attribute("UseNestedNamespaceNodes") ?? false; ShowRawOffsetsAndBytesBeforeInstruction = (bool?)section.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false; StyleWindowTitleBar = (bool?)section.Attribute("StyleWindowTitleBar") ?? false; EnableSmoothScrolling = (bool?)section.Attribute("EnableSmoothScrolling") ?? true; DecodeCustomAttributeBlobs = (bool?)section.Attribute("DecodeCustomAttributeBlobs") ?? false; } public XElement SaveToXml() { var section = new XElement(SectionName); section.SetAttributeValue("Font", SelectedFont.Source); section.SetAttributeValue("FontSize", SelectedFontSize); section.SetAttributeValue("ShowLineNumbers", ShowLineNumbers); section.SetAttributeValue("ShowMetadataTokens", ShowMetadataTokens); section.SetAttributeValue("ShowMetadataTokensInBase10", ShowMetadataTokensInBase10); section.SetAttributeValue("ShowDebugInfo", ShowDebugInfo); section.SetAttributeValue("EnableWordWrap", EnableWordWrap); section.SetAttributeValue("SortResults", SortResults); section.SetAttributeValue("FoldBraces", FoldBraces); section.SetAttributeValue("ExpandMemberDefinitions", ExpandMemberDefinitions); section.SetAttributeValue("ExpandUsingDeclarations", ExpandUsingDeclarations); section.SetAttributeValue("IndentationUseTabs", IndentationUseTabs); section.SetAttributeValue("IndentationSize", IndentationSize); section.SetAttributeValue("IndentationTabSize", IndentationTabSize); section.SetAttributeValue("HighlightMatchingBraces", HighlightMatchingBraces); section.SetAttributeValue("HighlightCurrentLine", HighlightCurrentLine); section.SetAttributeValue("HideEmptyMetadataTables", HideEmptyMetadataTables); section.SetAttributeValue("UseNestedNamespaceNodes", UseNestedNamespaceNodes); section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", ShowRawOffsetsAndBytesBeforeInstruction); section.SetAttributeValue("StyleWindowTitleBar", StyleWindowTitleBar); section.SetAttributeValue("EnableSmoothScrolling", EnableSmoothScrolling); section.SetAttributeValue("DecodeCustomAttributeBlobs", DecodeCustomAttributeBlobs); return section; } } } ================================================ FILE: ILSpy/Options/DisplaySettingsPanel.xaml ================================================ ================================================ FILE: ILSpy/Options/DisplaySettingsPanel.xaml.cs ================================================ // 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 // 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.Composition; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Threading; using System.Xml.Linq; using ICSharpCode.ILSpyX.Settings; using TomsToolbox.Wpf.Composition.AttributedModel; using TomsToolbox.Wpf.Converters; namespace ICSharpCode.ILSpy.Options { /// /// Interaction logic for DisplaySettingsPanel.xaml /// [NonShared] [DataTemplate(typeof(DisplaySettingsViewModel))] public partial class DisplaySettingsPanel { public DisplaySettingsPanel() { InitializeComponent(); DataObject.AddPastingHandler(tabSizeTextBox, OnPaste); DataObject.AddPastingHandler(indentSizeTextBox, OnPaste); } private void TextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) { if (!e.Text.All(char.IsDigit)) e.Handled = true; } private static void OnPaste(object sender, DataObjectPastingEventArgs e) { if (!e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true)) return; var text = (string)e.SourceDataObject.GetData(DataFormats.UnicodeText, true) ?? string.Empty; if (!text.All(char.IsDigit)) e.CancelCommand(); } } public sealed class FontSizeConverter : ValueConverter { protected override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is double d) { return Math.Round(d / 4 * 3); } return DependencyProperty.UnsetValue; } protected override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is not string s) return DependencyProperty.UnsetValue; if (double.TryParse(s, out double d)) return d * 4 / 3; return 11.0 * 4 / 3; } } } ================================================ FILE: ILSpy/Options/DisplaySettingsViewModel.cs ================================================ using ICSharpCode.ILSpyX.Settings; using System.Windows.Media; using System.Xml.Linq; using System; using System.Composition; using System.Linq; using System.Threading.Tasks; using System.Windows; using TomsToolbox.Wpf; using ICSharpCode.ILSpy.Themes; namespace ICSharpCode.ILSpy.Options { [ExportOptionPage(Order = 20)] [NonShared] public class DisplaySettingsViewModel : ObservableObjectBase, IOptionPage { private DisplaySettings settings = new(); private FontFamily[] fontFamilies; private SessionSettings sessionSettings; public DisplaySettingsViewModel() { fontFamilies = [settings.SelectedFont]; Task.Run(FontLoader).ContinueWith(continuation => { FontFamilies = continuation.Result; if (continuation.Exception == null) return; foreach (var ex in continuation.Exception.InnerExceptions) { MessageBox.Show(ex.ToString()); } }); } public string Title => Properties.Resources.Display; public DisplaySettings Settings { get => settings; set => SetProperty(ref settings, value); } public SessionSettings SessionSettings { get => sessionSettings; set => SetProperty(ref sessionSettings, value); } public FontFamily[] FontFamilies { get => fontFamilies; set => SetProperty(ref fontFamilies, value); } public int[] FontSizes { get; } = Enumerable.Range(6, 24 - 6 + 1).ToArray(); public void Load(SettingsSnapshot snapshot) { Settings = snapshot.GetSettings(); SessionSettings = snapshot.GetSettings(); } static bool IsSymbolFont(FontFamily fontFamily) { foreach (var tf in fontFamily.GetTypefaces()) { try { if (tf.TryGetGlyphTypeface(out GlyphTypeface glyph)) return glyph.Symbol; } catch (Exception) { return true; } } return false; } static FontFamily[] FontLoader() { return Fonts.SystemFontFamilies .Where(ff => !IsSymbolFont(ff)) .OrderBy(ff => ff.Source) .ToArray(); } public void LoadDefaults() { Settings.LoadFromXml(new XElement("empty")); SessionSettings.Theme = ThemeManager.Current.DefaultTheme; } } } ================================================ FILE: ILSpy/Options/MiscSettings.cs ================================================ // Copyright (c) 2022 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 // 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.Xml.Linq; using TomsToolbox.Wpf; namespace ICSharpCode.ILSpyX.Settings { public class MiscSettings : ObservableObjectBase, ISettingsSection { private bool allowMultipleInstances; private bool loadPreviousAssemblies = true; public bool AllowMultipleInstances { get => allowMultipleInstances; set => SetProperty(ref allowMultipleInstances, value); } public bool LoadPreviousAssemblies { get => loadPreviousAssemblies; set => SetProperty(ref loadPreviousAssemblies, value); } public XName SectionName => "MiscSettings"; public void LoadFromXml(XElement e) { AllowMultipleInstances = (bool?)e.Attribute(nameof(AllowMultipleInstances)) ?? false; LoadPreviousAssemblies = (bool?)e.Attribute(nameof(LoadPreviousAssemblies)) ?? true; } public XElement SaveToXml() { var section = new XElement(SectionName); section.SetAttributeValue(nameof(AllowMultipleInstances), AllowMultipleInstances); section.SetAttributeValue(nameof(LoadPreviousAssemblies), LoadPreviousAssemblies); return section; } } } ================================================ FILE: ILSpy/Options/MiscSettingsPanel.xaml ================================================